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     /**
398      * Set the element that will be used to show or hide
399      */
400     setVisibilityEl : function(el)
401     {
402         this.visibilityEl = el;
403     },
404     
405      /**
406      * Get the element that will be used to show or hide
407      */
408     getVisibilityEl : function()
409     {
410         if (typeof(this.visibilityEl) == 'object') {
411             return this.visibilityEl;
412         }
413         
414         if (typeof(this.visibilityEl) == 'string') {
415             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
416         }
417         
418         return this.getEl();
419     },
420     
421     /**
422      * Show a component - removes 'hidden' class
423      */
424     show : function()
425     {
426         if(!this.getVisibilityEl()){
427             return;
428         }
429          
430         this.getVisibilityEl().removeClass(['hidden','d-none']);
431         
432         this.fireEvent('show', this);
433         
434         
435     },
436     /**
437      * Hide a component - adds 'hidden' class
438      */
439     hide: function()
440     {
441         if(!this.getVisibilityEl()){
442             return;
443         }
444         
445         this.getVisibilityEl().addClass(['hidden','d-none']);
446         
447         this.fireEvent('hide', this);
448         
449     }
450 });
451
452  /*
453  * - LGPL
454  *
455  * Body
456  *
457  */
458
459 /**
460  * @class Roo.bootstrap.Body
461  * @extends Roo.bootstrap.Component
462  * Bootstrap Body class
463  *
464  * @constructor
465  * Create a new body
466  * @param {Object} config The config object
467  */
468
469 Roo.bootstrap.Body = function(config){
470
471     config = config || {};
472
473     Roo.bootstrap.Body.superclass.constructor.call(this, config);
474     this.el = Roo.get(config.el ? config.el : document.body );
475     if (this.cls && this.cls.length) {
476         Roo.get(document.body).addClass(this.cls);
477     }
478 };
479
480 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
481
482     is_body : true,// just to make sure it's constructed?
483
484         autoCreate : {
485         cls: 'container'
486     },
487     onRender : function(ct, position)
488     {
489        /* Roo.log("Roo.bootstrap.Body - onRender");
490         if (this.cls && this.cls.length) {
491             Roo.get(document.body).addClass(this.cls);
492         }
493         // style??? xttr???
494         */
495     }
496
497
498
499
500 });
501 /*
502  * - LGPL
503  *
504  * button group
505  * 
506  */
507
508
509 /**
510  * @class Roo.bootstrap.ButtonGroup
511  * @extends Roo.bootstrap.Component
512  * Bootstrap ButtonGroup class
513  * @cfg {String} size lg | sm | xs (default empty normal)
514  * @cfg {String} align vertical | justified  (default none)
515  * @cfg {String} direction up | down (default down)
516  * @cfg {Boolean} toolbar false | true
517  * @cfg {Boolean} btn true | false
518  * 
519  * 
520  * @constructor
521  * Create a new Input
522  * @param {Object} config The config object
523  */
524
525 Roo.bootstrap.ButtonGroup = function(config){
526     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
527 };
528
529 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
530     
531     size: '',
532     align: '',
533     direction: '',
534     toolbar: false,
535     btn: true,
536
537     getAutoCreate : function(){
538         var cfg = {
539             cls: 'btn-group',
540             html : null
541         };
542         
543         cfg.html = this.html || cfg.html;
544         
545         if (this.toolbar) {
546             cfg = {
547                 cls: 'btn-toolbar',
548                 html: null
549             };
550             
551             return cfg;
552         }
553         
554         if (['vertical','justified'].indexOf(this.align)!==-1) {
555             cfg.cls = 'btn-group-' + this.align;
556             
557             if (this.align == 'justified') {
558                 console.log(this.items);
559             }
560         }
561         
562         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
563             cfg.cls += ' btn-group-' + this.size;
564         }
565         
566         if (this.direction == 'up') {
567             cfg.cls += ' dropup' ;
568         }
569         
570         return cfg;
571     },
572     /**
573      * Add a button to the group (similar to NavItem API.)
574      */
575     addItem : function(cfg)
576     {
577         var cn = new Roo.bootstrap.Button(cfg);
578         //this.register(cn);
579         cn.parentId = this.id;
580         cn.onRender(this.el, null);
581         return cn;
582     }
583    
584 });
585
586  /*
587  * - LGPL
588  *
589  * button
590  * 
591  */
592
593 /**
594  * @class Roo.bootstrap.Button
595  * @extends Roo.bootstrap.Component
596  * Bootstrap Button class
597  * @cfg {String} html The button content
598  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
599  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
600  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
601  * @cfg {String} size ( lg | sm | xs)
602  * @cfg {String} tag ( a | input | submit)
603  * @cfg {String} href empty or href
604  * @cfg {Boolean} disabled default false;
605  * @cfg {Boolean} isClose default false;
606  * @cfg {String} glyphicon depricated - use fa
607  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
608  * @cfg {String} badge text for badge
609  * @cfg {String} theme (default|glow)  
610  * @cfg {Boolean} inverse dark themed version
611  * @cfg {Boolean} toggle is it a slidy toggle button
612  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
613  * @cfg {String} ontext text for on slidy toggle state
614  * @cfg {String} offtext text for off slidy toggle state
615  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
616  * @cfg {Boolean} removeClass remove the standard class..
617  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
618  * 
619  * @constructor
620  * Create a new button
621  * @param {Object} config The config object
622  */
623
624
625 Roo.bootstrap.Button = function(config){
626     Roo.bootstrap.Button.superclass.constructor.call(this, config);
627     this.weightClass = ["btn-default btn-outline-secondary", 
628                        "btn-primary", 
629                        "btn-success", 
630                        "btn-info", 
631                        "btn-warning",
632                        "btn-danger",
633                        "btn-link"
634                       ],  
635     this.addEvents({
636         // raw events
637         /**
638          * @event click
639          * When a butotn is pressed
640          * @param {Roo.bootstrap.Button} btn
641          * @param {Roo.EventObject} e
642          */
643         "click" : true,
644          /**
645          * @event toggle
646          * After the button has been toggles
647          * @param {Roo.bootstrap.Button} btn
648          * @param {Roo.EventObject} e
649          * @param {boolean} pressed (also available as button.pressed)
650          */
651         "toggle" : true
652     });
653 };
654
655 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
656     html: false,
657     active: false,
658     weight: '',
659     badge_weight: '',
660     outline : false,
661     size: '',
662     tag: 'button',
663     href: '',
664     disabled: false,
665     isClose: false,
666     glyphicon: '',
667     fa: '',
668     badge: '',
669     theme: 'default',
670     inverse: false,
671     
672     toggle: false,
673     ontext: 'ON',
674     offtext: 'OFF',
675     defaulton: true,
676     preventDefault: true,
677     removeClass: false,
678     name: false,
679     target: false,
680      
681     pressed : null,
682      
683     
684     getAutoCreate : function(){
685         
686         var cfg = {
687             tag : 'button',
688             cls : 'roo-button',
689             html: ''
690         };
691         
692         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
693             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
694             this.tag = 'button';
695         } else {
696             cfg.tag = this.tag;
697         }
698         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
699         
700         if (this.toggle == true) {
701             cfg={
702                 tag: 'div',
703                 cls: 'slider-frame roo-button',
704                 cn: [
705                     {
706                         tag: 'span',
707                         'data-on-text':'ON',
708                         'data-off-text':'OFF',
709                         cls: 'slider-button',
710                         html: this.offtext
711                     }
712                 ]
713             };
714             
715             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
716                 cfg.cls += ' '+this.weight;
717             }
718             
719             return cfg;
720         }
721         
722         if (this.isClose) {
723             cfg.cls += ' close';
724             
725             cfg["aria-hidden"] = true;
726             
727             cfg.html = "&times;";
728             
729             return cfg;
730         }
731         
732          
733         if (this.theme==='default') {
734             cfg.cls = 'btn roo-button';
735             
736             //if (this.parentType != 'Navbar') {
737             this.weight = this.weight.length ?  this.weight : 'default';
738             //}
739             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
740                 
741                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
742                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
743                 cfg.cls += ' btn-' + outline + weight;
744                 if (this.weight == 'default') {
745                     // BC
746                     cfg.cls += ' btn-' + this.weight;
747                 }
748             }
749         } else if (this.theme==='glow') {
750             
751             cfg.tag = 'a';
752             cfg.cls = 'btn-glow roo-button';
753             
754             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
755                 
756                 cfg.cls += ' ' + this.weight;
757             }
758         }
759    
760         
761         if (this.inverse) {
762             this.cls += ' inverse';
763         }
764         
765         
766         if (this.active || this.pressed === true) {
767             cfg.cls += ' active';
768         }
769         
770         if (this.disabled) {
771             cfg.disabled = 'disabled';
772         }
773         
774         if (this.items) {
775             Roo.log('changing to ul' );
776             cfg.tag = 'ul';
777             this.glyphicon = 'caret';
778             if (Roo.bootstrap.version == 4) {
779                 this.fa = 'caret-down';
780             }
781             
782         }
783         
784         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
785          
786         //gsRoo.log(this.parentType);
787         if (this.parentType === 'Navbar' && !this.parent().bar) {
788             Roo.log('changing to li?');
789             
790             cfg.tag = 'li';
791             
792             cfg.cls = '';
793             cfg.cn =  [{
794                 tag : 'a',
795                 cls : 'roo-button',
796                 html : this.html,
797                 href : this.href || '#'
798             }];
799             if (this.menu) {
800                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
801                 cfg.cls += ' dropdown';
802             }   
803             
804             delete cfg.html;
805             
806         }
807         
808        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
809         
810         if (this.glyphicon) {
811             cfg.html = ' ' + cfg.html;
812             
813             cfg.cn = [
814                 {
815                     tag: 'span',
816                     cls: 'glyphicon glyphicon-' + this.glyphicon
817                 }
818             ];
819         }
820         if (this.fa) {
821             cfg.html = ' ' + cfg.html;
822             
823             cfg.cn = [
824                 {
825                     tag: 'i',
826                     cls: 'fa fas fa-' + this.fa
827                 }
828             ];
829         }
830         
831         if (this.badge) {
832             cfg.html += ' ';
833             
834             cfg.tag = 'a';
835             
836 //            cfg.cls='btn roo-button';
837             
838             cfg.href=this.href;
839             
840             var value = cfg.html;
841             
842             if(this.glyphicon){
843                 value = {
844                     tag: 'span',
845                     cls: 'glyphicon glyphicon-' + this.glyphicon,
846                     html: this.html
847                 };
848             }
849             if(this.fa){
850                 value = {
851                     tag: 'i',
852                     cls: 'fa fas fa-' + this.fa,
853                     html: this.html
854                 };
855             }
856             
857             var bw = this.badge_weight.length ? this.badge_weight :
858                 (this.weight.length ? this.weight : 'secondary');
859             bw = bw == 'default' ? 'secondary' : bw;
860             
861             cfg.cn = [
862                 value,
863                 {
864                     tag: 'span',
865                     cls: 'badge badge-' + bw,
866                     html: this.badge
867                 }
868             ];
869             
870             cfg.html='';
871         }
872         
873         if (this.menu) {
874             cfg.cls += ' dropdown';
875             cfg.html = typeof(cfg.html) != 'undefined' ?
876                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
877         }
878         
879         if (cfg.tag !== 'a' && this.href !== '') {
880             throw "Tag must be a to set href.";
881         } else if (this.href.length > 0) {
882             cfg.href = this.href;
883         }
884         
885         if(this.removeClass){
886             cfg.cls = '';
887         }
888         
889         if(this.target){
890             cfg.target = this.target;
891         }
892         
893         return cfg;
894     },
895     initEvents: function() {
896        // Roo.log('init events?');
897 //        Roo.log(this.el.dom);
898         // add the menu...
899         
900         if (typeof (this.menu) != 'undefined') {
901             this.menu.parentType = this.xtype;
902             this.menu.triggerEl = this.el;
903             this.addxtype(Roo.apply({}, this.menu));
904         }
905
906
907        if (this.el.hasClass('roo-button')) {
908             this.el.on('click', this.onClick, this);
909        } else {
910             this.el.select('.roo-button').on('click', this.onClick, this);
911        }
912        
913        if(this.removeClass){
914            this.el.on('click', this.onClick, this);
915        }
916        
917        this.el.enableDisplayMode();
918         
919     },
920     onClick : function(e)
921     {
922         if (this.disabled) {
923             return;
924         }
925         
926         Roo.log('button on click ');
927         if(this.preventDefault){
928             e.preventDefault();
929         }
930         
931         if (this.pressed === true || this.pressed === false) {
932             this.toggleActive(e);
933         }
934         
935         
936         this.fireEvent('click', this, e);
937     },
938     
939     /**
940      * Enables this button
941      */
942     enable : function()
943     {
944         this.disabled = false;
945         this.el.removeClass('disabled');
946     },
947     
948     /**
949      * Disable this button
950      */
951     disable : function()
952     {
953         this.disabled = true;
954         this.el.addClass('disabled');
955     },
956      /**
957      * sets the active state on/off, 
958      * @param {Boolean} state (optional) Force a particular state
959      */
960     setActive : function(v) {
961         
962         this.el[v ? 'addClass' : 'removeClass']('active');
963         this.pressed = v;
964     },
965      /**
966      * toggles the current active state 
967      */
968     toggleActive : function(e)
969     {
970         this.setActive(!this.pressed);
971         this.fireEvent('toggle', this, e, !this.pressed);
972     },
973      /**
974      * get the current active state
975      * @return {boolean} true if it's active
976      */
977     isActive : function()
978     {
979         return this.el.hasClass('active');
980     },
981     /**
982      * set the text of the first selected button
983      */
984     setText : function(str)
985     {
986         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
987     },
988     /**
989      * get the text of the first selected button
990      */
991     getText : function()
992     {
993         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
994     },
995     
996     setWeight : function(str)
997     {
998         this.el.removeClass(this.weightClass);
999         this.weight = str;
1000         var outline = this.outline ? 'outline-' : '';
1001         if (str == 'default') {
1002             this.el.addClass('btn-default btn-outline-secondary');        
1003             return;
1004         }
1005         this.el.addClass('btn-' + outline + str);        
1006     }
1007     
1008     
1009 });
1010
1011  /*
1012  * - LGPL
1013  *
1014  * column
1015  * 
1016  */
1017
1018 /**
1019  * @class Roo.bootstrap.Column
1020  * @extends Roo.bootstrap.Component
1021  * Bootstrap Column class
1022  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1023  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1024  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1025  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1026  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1027  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1028  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1029  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1030  *
1031  * 
1032  * @cfg {Boolean} hidden (true|false) hide the element
1033  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1034  * @cfg {String} fa (ban|check|...) font awesome icon
1035  * @cfg {Number} fasize (1|2|....) font awsome size
1036
1037  * @cfg {String} icon (info-sign|check|...) glyphicon name
1038
1039  * @cfg {String} html content of column.
1040  * 
1041  * @constructor
1042  * Create a new Column
1043  * @param {Object} config The config object
1044  */
1045
1046 Roo.bootstrap.Column = function(config){
1047     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1048 };
1049
1050 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1051     
1052     xs: false,
1053     sm: false,
1054     md: false,
1055     lg: false,
1056     xsoff: false,
1057     smoff: false,
1058     mdoff: false,
1059     lgoff: false,
1060     html: '',
1061     offset: 0,
1062     alert: false,
1063     fa: false,
1064     icon : false,
1065     hidden : false,
1066     fasize : 1,
1067     
1068     getAutoCreate : function(){
1069         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1070         
1071         cfg = {
1072             tag: 'div',
1073             cls: 'column'
1074         };
1075         
1076         var settings=this;
1077         ['xs','sm','md','lg'].map(function(size){
1078             //Roo.log( size + ':' + settings[size]);
1079             
1080             if (settings[size+'off'] !== false) {
1081                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1082             }
1083             
1084             if (settings[size] === false) {
1085                 return;
1086             }
1087             
1088             if (!settings[size]) { // 0 = hidden
1089                 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1090                 return;
1091             }
1092             cfg.cls += ' col-' + size + '-' + settings[size] + (
1093                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1094             );
1095             
1096         });
1097         
1098         if (this.hidden) {
1099             cfg.cls += ' hidden';
1100         }
1101         
1102         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1103             cfg.cls +=' alert alert-' + this.alert;
1104         }
1105         
1106         
1107         if (this.html.length) {
1108             cfg.html = this.html;
1109         }
1110         if (this.fa) {
1111             var fasize = '';
1112             if (this.fasize > 1) {
1113                 fasize = ' fa-' + this.fasize + 'x';
1114             }
1115             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1116             
1117             
1118         }
1119         if (this.icon) {
1120             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1121         }
1122         
1123         return cfg;
1124     }
1125    
1126 });
1127
1128  
1129
1130  /*
1131  * - LGPL
1132  *
1133  * page container.
1134  * 
1135  */
1136
1137
1138 /**
1139  * @class Roo.bootstrap.Container
1140  * @extends Roo.bootstrap.Component
1141  * Bootstrap Container class
1142  * @cfg {Boolean} jumbotron is it a jumbotron element
1143  * @cfg {String} html content of element
1144  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1145  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1146  * @cfg {String} header content of header (for panel)
1147  * @cfg {String} footer content of footer (for panel)
1148  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1149  * @cfg {String} tag (header|aside|section) type of HTML tag.
1150  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1151  * @cfg {String} fa font awesome icon
1152  * @cfg {String} icon (info-sign|check|...) glyphicon name
1153  * @cfg {Boolean} hidden (true|false) hide the element
1154  * @cfg {Boolean} expandable (true|false) default false
1155  * @cfg {Boolean} expanded (true|false) default true
1156  * @cfg {String} rheader contet on the right of header
1157  * @cfg {Boolean} clickable (true|false) default false
1158
1159  *     
1160  * @constructor
1161  * Create a new Container
1162  * @param {Object} config The config object
1163  */
1164
1165 Roo.bootstrap.Container = function(config){
1166     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1167     
1168     this.addEvents({
1169         // raw events
1170          /**
1171          * @event expand
1172          * After the panel has been expand
1173          * 
1174          * @param {Roo.bootstrap.Container} this
1175          */
1176         "expand" : true,
1177         /**
1178          * @event collapse
1179          * After the panel has been collapsed
1180          * 
1181          * @param {Roo.bootstrap.Container} this
1182          */
1183         "collapse" : true,
1184         /**
1185          * @event click
1186          * When a element is chick
1187          * @param {Roo.bootstrap.Container} this
1188          * @param {Roo.EventObject} e
1189          */
1190         "click" : true
1191     });
1192 };
1193
1194 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1195     
1196     jumbotron : false,
1197     well: '',
1198     panel : '',
1199     header: '',
1200     footer : '',
1201     sticky: '',
1202     tag : false,
1203     alert : false,
1204     fa: false,
1205     icon : false,
1206     expandable : false,
1207     rheader : '',
1208     expanded : true,
1209     clickable: false,
1210   
1211      
1212     getChildContainer : function() {
1213         
1214         if(!this.el){
1215             return false;
1216         }
1217         
1218         if (this.panel.length) {
1219             return this.el.select('.panel-body',true).first();
1220         }
1221         
1222         return this.el;
1223     },
1224     
1225     
1226     getAutoCreate : function(){
1227         
1228         var cfg = {
1229             tag : this.tag || 'div',
1230             html : '',
1231             cls : ''
1232         };
1233         if (this.jumbotron) {
1234             cfg.cls = 'jumbotron';
1235         }
1236         
1237         
1238         
1239         // - this is applied by the parent..
1240         //if (this.cls) {
1241         //    cfg.cls = this.cls + '';
1242         //}
1243         
1244         if (this.sticky.length) {
1245             
1246             var bd = Roo.get(document.body);
1247             if (!bd.hasClass('bootstrap-sticky')) {
1248                 bd.addClass('bootstrap-sticky');
1249                 Roo.select('html',true).setStyle('height', '100%');
1250             }
1251              
1252             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1253         }
1254         
1255         
1256         if (this.well.length) {
1257             switch (this.well) {
1258                 case 'lg':
1259                 case 'sm':
1260                     cfg.cls +=' well well-' +this.well;
1261                     break;
1262                 default:
1263                     cfg.cls +=' well';
1264                     break;
1265             }
1266         }
1267         
1268         if (this.hidden) {
1269             cfg.cls += ' hidden';
1270         }
1271         
1272         
1273         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1274             cfg.cls +=' alert alert-' + this.alert;
1275         }
1276         
1277         var body = cfg;
1278         
1279         if (this.panel.length) {
1280             cfg.cls += ' panel panel-' + this.panel;
1281             cfg.cn = [];
1282             if (this.header.length) {
1283                 
1284                 var h = [];
1285                 
1286                 if(this.expandable){
1287                     
1288                     cfg.cls = cfg.cls + ' expandable';
1289                     
1290                     h.push({
1291                         tag: 'i',
1292                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1293                     });
1294                     
1295                 }
1296                 
1297                 h.push(
1298                     {
1299                         tag: 'span',
1300                         cls : 'panel-title',
1301                         html : (this.expandable ? '&nbsp;' : '') + this.header
1302                     },
1303                     {
1304                         tag: 'span',
1305                         cls: 'panel-header-right',
1306                         html: this.rheader
1307                     }
1308                 );
1309                 
1310                 cfg.cn.push({
1311                     cls : 'panel-heading',
1312                     style : this.expandable ? 'cursor: pointer' : '',
1313                     cn : h
1314                 });
1315                 
1316             }
1317             
1318             body = false;
1319             cfg.cn.push({
1320                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1321                 html : this.html
1322             });
1323             
1324             
1325             if (this.footer.length) {
1326                 cfg.cn.push({
1327                     cls : 'panel-footer',
1328                     html : this.footer
1329                     
1330                 });
1331             }
1332             
1333         }
1334         
1335         if (body) {
1336             body.html = this.html || cfg.html;
1337             // prefix with the icons..
1338             if (this.fa) {
1339                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1340             }
1341             if (this.icon) {
1342                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1343             }
1344             
1345             
1346         }
1347         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1348             cfg.cls =  'container';
1349         }
1350         
1351         return cfg;
1352     },
1353     
1354     initEvents: function() 
1355     {
1356         if(this.expandable){
1357             var headerEl = this.headerEl();
1358         
1359             if(headerEl){
1360                 headerEl.on('click', this.onToggleClick, this);
1361             }
1362         }
1363         
1364         if(this.clickable){
1365             this.el.on('click', this.onClick, this);
1366         }
1367         
1368     },
1369     
1370     onToggleClick : function()
1371     {
1372         var headerEl = this.headerEl();
1373         
1374         if(!headerEl){
1375             return;
1376         }
1377         
1378         if(this.expanded){
1379             this.collapse();
1380             return;
1381         }
1382         
1383         this.expand();
1384     },
1385     
1386     expand : function()
1387     {
1388         if(this.fireEvent('expand', this)) {
1389             
1390             this.expanded = true;
1391             
1392             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1393             
1394             this.el.select('.panel-body',true).first().removeClass('hide');
1395             
1396             var toggleEl = this.toggleEl();
1397
1398             if(!toggleEl){
1399                 return;
1400             }
1401
1402             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1403         }
1404         
1405     },
1406     
1407     collapse : function()
1408     {
1409         if(this.fireEvent('collapse', this)) {
1410             
1411             this.expanded = false;
1412             
1413             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1414             this.el.select('.panel-body',true).first().addClass('hide');
1415         
1416             var toggleEl = this.toggleEl();
1417
1418             if(!toggleEl){
1419                 return;
1420             }
1421
1422             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1423         }
1424     },
1425     
1426     toggleEl : function()
1427     {
1428         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1429             return;
1430         }
1431         
1432         return this.el.select('.panel-heading .fa',true).first();
1433     },
1434     
1435     headerEl : function()
1436     {
1437         if(!this.el || !this.panel.length || !this.header.length){
1438             return;
1439         }
1440         
1441         return this.el.select('.panel-heading',true).first()
1442     },
1443     
1444     bodyEl : function()
1445     {
1446         if(!this.el || !this.panel.length){
1447             return;
1448         }
1449         
1450         return this.el.select('.panel-body',true).first()
1451     },
1452     
1453     titleEl : function()
1454     {
1455         if(!this.el || !this.panel.length || !this.header.length){
1456             return;
1457         }
1458         
1459         return this.el.select('.panel-title',true).first();
1460     },
1461     
1462     setTitle : function(v)
1463     {
1464         var titleEl = this.titleEl();
1465         
1466         if(!titleEl){
1467             return;
1468         }
1469         
1470         titleEl.dom.innerHTML = v;
1471     },
1472     
1473     getTitle : function()
1474     {
1475         
1476         var titleEl = this.titleEl();
1477         
1478         if(!titleEl){
1479             return '';
1480         }
1481         
1482         return titleEl.dom.innerHTML;
1483     },
1484     
1485     setRightTitle : function(v)
1486     {
1487         var t = this.el.select('.panel-header-right',true).first();
1488         
1489         if(!t){
1490             return;
1491         }
1492         
1493         t.dom.innerHTML = v;
1494     },
1495     
1496     onClick : function(e)
1497     {
1498         e.preventDefault();
1499         
1500         this.fireEvent('click', this, e);
1501     }
1502 });
1503
1504  /*
1505  * - LGPL
1506  *
1507  * image
1508  * 
1509  */
1510
1511
1512 /**
1513  * @class Roo.bootstrap.Img
1514  * @extends Roo.bootstrap.Component
1515  * Bootstrap Img class
1516  * @cfg {Boolean} imgResponsive false | true
1517  * @cfg {String} border rounded | circle | thumbnail
1518  * @cfg {String} src image source
1519  * @cfg {String} alt image alternative text
1520  * @cfg {String} href a tag href
1521  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1522  * @cfg {String} xsUrl xs image source
1523  * @cfg {String} smUrl sm image source
1524  * @cfg {String} mdUrl md image source
1525  * @cfg {String} lgUrl lg image source
1526  * 
1527  * @constructor
1528  * Create a new Input
1529  * @param {Object} config The config object
1530  */
1531
1532 Roo.bootstrap.Img = function(config){
1533     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1534     
1535     this.addEvents({
1536         // img events
1537         /**
1538          * @event click
1539          * The img click event for the img.
1540          * @param {Roo.EventObject} e
1541          */
1542         "click" : true
1543     });
1544 };
1545
1546 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1547     
1548     imgResponsive: true,
1549     border: '',
1550     src: 'about:blank',
1551     href: false,
1552     target: false,
1553     xsUrl: '',
1554     smUrl: '',
1555     mdUrl: '',
1556     lgUrl: '',
1557
1558     getAutoCreate : function()
1559     {   
1560         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1561             return this.createSingleImg();
1562         }
1563         
1564         var cfg = {
1565             tag: 'div',
1566             cls: 'roo-image-responsive-group',
1567             cn: []
1568         };
1569         var _this = this;
1570         
1571         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1572             
1573             if(!_this[size + 'Url']){
1574                 return;
1575             }
1576             
1577             var img = {
1578                 tag: 'img',
1579                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1580                 html: _this.html || cfg.html,
1581                 src: _this[size + 'Url']
1582             };
1583             
1584             img.cls += ' roo-image-responsive-' + size;
1585             
1586             var s = ['xs', 'sm', 'md', 'lg'];
1587             
1588             s.splice(s.indexOf(size), 1);
1589             
1590             Roo.each(s, function(ss){
1591                 img.cls += ' hidden-' + ss;
1592             });
1593             
1594             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1595                 cfg.cls += ' img-' + _this.border;
1596             }
1597             
1598             if(_this.alt){
1599                 cfg.alt = _this.alt;
1600             }
1601             
1602             if(_this.href){
1603                 var a = {
1604                     tag: 'a',
1605                     href: _this.href,
1606                     cn: [
1607                         img
1608                     ]
1609                 };
1610
1611                 if(this.target){
1612                     a.target = _this.target;
1613                 }
1614             }
1615             
1616             cfg.cn.push((_this.href) ? a : img);
1617             
1618         });
1619         
1620         return cfg;
1621     },
1622     
1623     createSingleImg : function()
1624     {
1625         var cfg = {
1626             tag: 'img',
1627             cls: (this.imgResponsive) ? 'img-responsive' : '',
1628             html : null,
1629             src : 'about:blank'  // just incase src get's set to undefined?!?
1630         };
1631         
1632         cfg.html = this.html || cfg.html;
1633         
1634         cfg.src = this.src || cfg.src;
1635         
1636         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1637             cfg.cls += ' img-' + this.border;
1638         }
1639         
1640         if(this.alt){
1641             cfg.alt = this.alt;
1642         }
1643         
1644         if(this.href){
1645             var a = {
1646                 tag: 'a',
1647                 href: this.href,
1648                 cn: [
1649                     cfg
1650                 ]
1651             };
1652             
1653             if(this.target){
1654                 a.target = this.target;
1655             }
1656             
1657         }
1658         
1659         return (this.href) ? a : cfg;
1660     },
1661     
1662     initEvents: function() 
1663     {
1664         if(!this.href){
1665             this.el.on('click', this.onClick, this);
1666         }
1667         
1668     },
1669     
1670     onClick : function(e)
1671     {
1672         Roo.log('img onclick');
1673         this.fireEvent('click', this, e);
1674     },
1675     /**
1676      * Sets the url of the image - used to update it
1677      * @param {String} url the url of the image
1678      */
1679     
1680     setSrc : function(url)
1681     {
1682         this.src =  url;
1683         
1684         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1685             this.el.dom.src =  url;
1686             return;
1687         }
1688         
1689         this.el.select('img', true).first().dom.src =  url;
1690     }
1691     
1692     
1693    
1694 });
1695
1696  /*
1697  * - LGPL
1698  *
1699  * image
1700  * 
1701  */
1702
1703
1704 /**
1705  * @class Roo.bootstrap.Link
1706  * @extends Roo.bootstrap.Component
1707  * Bootstrap Link Class
1708  * @cfg {String} alt image alternative text
1709  * @cfg {String} href a tag href
1710  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1711  * @cfg {String} html the content of the link.
1712  * @cfg {String} anchor name for the anchor link
1713  * @cfg {String} fa - favicon
1714
1715  * @cfg {Boolean} preventDefault (true | false) default false
1716
1717  * 
1718  * @constructor
1719  * Create a new Input
1720  * @param {Object} config The config object
1721  */
1722
1723 Roo.bootstrap.Link = function(config){
1724     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1725     
1726     this.addEvents({
1727         // img events
1728         /**
1729          * @event click
1730          * The img click event for the img.
1731          * @param {Roo.EventObject} e
1732          */
1733         "click" : true
1734     });
1735 };
1736
1737 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1738     
1739     href: false,
1740     target: false,
1741     preventDefault: false,
1742     anchor : false,
1743     alt : false,
1744     fa: false,
1745
1746
1747     getAutoCreate : function()
1748     {
1749         var html = this.html || '';
1750         
1751         if (this.fa !== false) {
1752             html = '<i class="fa fa-' + this.fa + '"></i>';
1753         }
1754         var cfg = {
1755             tag: 'a'
1756         };
1757         // anchor's do not require html/href...
1758         if (this.anchor === false) {
1759             cfg.html = html;
1760             cfg.href = this.href || '#';
1761         } else {
1762             cfg.name = this.anchor;
1763             if (this.html !== false || this.fa !== false) {
1764                 cfg.html = html;
1765             }
1766             if (this.href !== false) {
1767                 cfg.href = this.href;
1768             }
1769         }
1770         
1771         if(this.alt !== false){
1772             cfg.alt = this.alt;
1773         }
1774         
1775         
1776         if(this.target !== false) {
1777             cfg.target = this.target;
1778         }
1779         
1780         return cfg;
1781     },
1782     
1783     initEvents: function() {
1784         
1785         if(!this.href || this.preventDefault){
1786             this.el.on('click', this.onClick, this);
1787         }
1788     },
1789     
1790     onClick : function(e)
1791     {
1792         if(this.preventDefault){
1793             e.preventDefault();
1794         }
1795         //Roo.log('img onclick');
1796         this.fireEvent('click', this, e);
1797     }
1798    
1799 });
1800
1801  /*
1802  * - LGPL
1803  *
1804  * header
1805  * 
1806  */
1807
1808 /**
1809  * @class Roo.bootstrap.Header
1810  * @extends Roo.bootstrap.Component
1811  * Bootstrap Header class
1812  * @cfg {String} html content of header
1813  * @cfg {Number} level (1|2|3|4|5|6) default 1
1814  * 
1815  * @constructor
1816  * Create a new Header
1817  * @param {Object} config The config object
1818  */
1819
1820
1821 Roo.bootstrap.Header  = function(config){
1822     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1823 };
1824
1825 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1826     
1827     //href : false,
1828     html : false,
1829     level : 1,
1830     
1831     
1832     
1833     getAutoCreate : function(){
1834         
1835         
1836         
1837         var cfg = {
1838             tag: 'h' + (1 *this.level),
1839             html: this.html || ''
1840         } ;
1841         
1842         return cfg;
1843     }
1844    
1845 });
1846
1847  
1848
1849  /*
1850  * Based on:
1851  * Ext JS Library 1.1.1
1852  * Copyright(c) 2006-2007, Ext JS, LLC.
1853  *
1854  * Originally Released Under LGPL - original licence link has changed is not relivant.
1855  *
1856  * Fork - LGPL
1857  * <script type="text/javascript">
1858  */
1859  
1860 /**
1861  * @class Roo.bootstrap.MenuMgr
1862  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1863  * @singleton
1864  */
1865 Roo.bootstrap.MenuMgr = function(){
1866    var menus, active, groups = {}, attached = false, lastShow = new Date();
1867
1868    // private - called when first menu is created
1869    function init(){
1870        menus = {};
1871        active = new Roo.util.MixedCollection();
1872        Roo.get(document).addKeyListener(27, function(){
1873            if(active.length > 0){
1874                hideAll();
1875            }
1876        });
1877    }
1878
1879    // private
1880    function hideAll(){
1881        if(active && active.length > 0){
1882            var c = active.clone();
1883            c.each(function(m){
1884                m.hide();
1885            });
1886        }
1887    }
1888
1889    // private
1890    function onHide(m){
1891        active.remove(m);
1892        if(active.length < 1){
1893            Roo.get(document).un("mouseup", onMouseDown);
1894             
1895            attached = false;
1896        }
1897    }
1898
1899    // private
1900    function onShow(m){
1901        var last = active.last();
1902        lastShow = new Date();
1903        active.add(m);
1904        if(!attached){
1905           Roo.get(document).on("mouseup", onMouseDown);
1906            
1907            attached = true;
1908        }
1909        if(m.parentMenu){
1910           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1911           m.parentMenu.activeChild = m;
1912        }else if(last && last.isVisible()){
1913           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1914        }
1915    }
1916
1917    // private
1918    function onBeforeHide(m){
1919        if(m.activeChild){
1920            m.activeChild.hide();
1921        }
1922        if(m.autoHideTimer){
1923            clearTimeout(m.autoHideTimer);
1924            delete m.autoHideTimer;
1925        }
1926    }
1927
1928    // private
1929    function onBeforeShow(m){
1930        var pm = m.parentMenu;
1931        if(!pm && !m.allowOtherMenus){
1932            hideAll();
1933        }else if(pm && pm.activeChild && active != m){
1934            pm.activeChild.hide();
1935        }
1936    }
1937
1938    // private this should really trigger on mouseup..
1939    function onMouseDown(e){
1940         Roo.log("on Mouse Up");
1941         
1942         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1943             Roo.log("MenuManager hideAll");
1944             hideAll();
1945             e.stopEvent();
1946         }
1947         
1948         
1949    }
1950
1951    // private
1952    function onBeforeCheck(mi, state){
1953        if(state){
1954            var g = groups[mi.group];
1955            for(var i = 0, l = g.length; i < l; i++){
1956                if(g[i] != mi){
1957                    g[i].setChecked(false);
1958                }
1959            }
1960        }
1961    }
1962
1963    return {
1964
1965        /**
1966         * Hides all menus that are currently visible
1967         */
1968        hideAll : function(){
1969             hideAll();  
1970        },
1971
1972        // private
1973        register : function(menu){
1974            if(!menus){
1975                init();
1976            }
1977            menus[menu.id] = menu;
1978            menu.on("beforehide", onBeforeHide);
1979            menu.on("hide", onHide);
1980            menu.on("beforeshow", onBeforeShow);
1981            menu.on("show", onShow);
1982            var g = menu.group;
1983            if(g && menu.events["checkchange"]){
1984                if(!groups[g]){
1985                    groups[g] = [];
1986                }
1987                groups[g].push(menu);
1988                menu.on("checkchange", onCheck);
1989            }
1990        },
1991
1992         /**
1993          * Returns a {@link Roo.menu.Menu} object
1994          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1995          * be used to generate and return a new Menu instance.
1996          */
1997        get : function(menu){
1998            if(typeof menu == "string"){ // menu id
1999                return menus[menu];
2000            }else if(menu.events){  // menu instance
2001                return menu;
2002            }
2003            /*else if(typeof menu.length == 'number'){ // array of menu items?
2004                return new Roo.bootstrap.Menu({items:menu});
2005            }else{ // otherwise, must be a config
2006                return new Roo.bootstrap.Menu(menu);
2007            }
2008            */
2009            return false;
2010        },
2011
2012        // private
2013        unregister : function(menu){
2014            delete menus[menu.id];
2015            menu.un("beforehide", onBeforeHide);
2016            menu.un("hide", onHide);
2017            menu.un("beforeshow", onBeforeShow);
2018            menu.un("show", onShow);
2019            var g = menu.group;
2020            if(g && menu.events["checkchange"]){
2021                groups[g].remove(menu);
2022                menu.un("checkchange", onCheck);
2023            }
2024        },
2025
2026        // private
2027        registerCheckable : function(menuItem){
2028            var g = menuItem.group;
2029            if(g){
2030                if(!groups[g]){
2031                    groups[g] = [];
2032                }
2033                groups[g].push(menuItem);
2034                menuItem.on("beforecheckchange", onBeforeCheck);
2035            }
2036        },
2037
2038        // private
2039        unregisterCheckable : function(menuItem){
2040            var g = menuItem.group;
2041            if(g){
2042                groups[g].remove(menuItem);
2043                menuItem.un("beforecheckchange", onBeforeCheck);
2044            }
2045        }
2046    };
2047 }();/*
2048  * - LGPL
2049  *
2050  * menu
2051  * 
2052  */
2053
2054 /**
2055  * @class Roo.bootstrap.Menu
2056  * @extends Roo.bootstrap.Component
2057  * Bootstrap Menu class - container for MenuItems
2058  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2059  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2060  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2061  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2062  * 
2063  * @constructor
2064  * Create a new Menu
2065  * @param {Object} config The config object
2066  */
2067
2068
2069 Roo.bootstrap.Menu = function(config){
2070     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2071     if (this.registerMenu && this.type != 'treeview')  {
2072         Roo.bootstrap.MenuMgr.register(this);
2073     }
2074     
2075     
2076     this.addEvents({
2077         /**
2078          * @event beforeshow
2079          * Fires before this menu is displayed (return false to block)
2080          * @param {Roo.menu.Menu} this
2081          */
2082         beforeshow : true,
2083         /**
2084          * @event beforehide
2085          * Fires before this menu is hidden (return false to block)
2086          * @param {Roo.menu.Menu} this
2087          */
2088         beforehide : true,
2089         /**
2090          * @event show
2091          * Fires after this menu is displayed
2092          * @param {Roo.menu.Menu} this
2093          */
2094         show : true,
2095         /**
2096          * @event hide
2097          * Fires after this menu is hidden
2098          * @param {Roo.menu.Menu} this
2099          */
2100         hide : true,
2101         /**
2102          * @event click
2103          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2106          * @param {Roo.EventObject} e
2107          */
2108         click : true,
2109         /**
2110          * @event mouseover
2111          * Fires when the mouse is hovering over this menu
2112          * @param {Roo.menu.Menu} this
2113          * @param {Roo.EventObject} e
2114          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2115          */
2116         mouseover : true,
2117         /**
2118          * @event mouseout
2119          * Fires when the mouse exits this menu
2120          * @param {Roo.menu.Menu} this
2121          * @param {Roo.EventObject} e
2122          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2123          */
2124         mouseout : true,
2125         /**
2126          * @event itemclick
2127          * Fires when a menu item contained in this menu is clicked
2128          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2129          * @param {Roo.EventObject} e
2130          */
2131         itemclick: true
2132     });
2133     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2134 };
2135
2136 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2137     
2138    /// html : false,
2139     //align : '',
2140     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2141     type: false,
2142     /**
2143      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2144      */
2145     registerMenu : true,
2146     
2147     menuItems :false, // stores the menu items..
2148     
2149     hidden:true,
2150         
2151     parentMenu : false,
2152     
2153     stopEvent : true,
2154     
2155     isLink : false,
2156     
2157     getChildContainer : function() {
2158         return this.el;  
2159     },
2160     
2161     getAutoCreate : function(){
2162          
2163         //if (['right'].indexOf(this.align)!==-1) {
2164         //    cfg.cn[1].cls += ' pull-right'
2165         //}
2166         
2167         
2168         var cfg = {
2169             tag : 'ul',
2170             cls : 'dropdown-menu' ,
2171             style : 'z-index:1000'
2172             
2173         };
2174         
2175         if (this.type === 'submenu') {
2176             cfg.cls = 'submenu active';
2177         }
2178         if (this.type === 'treeview') {
2179             cfg.cls = 'treeview-menu';
2180         }
2181         
2182         return cfg;
2183     },
2184     initEvents : function() {
2185         
2186        // Roo.log("ADD event");
2187        // Roo.log(this.triggerEl.dom);
2188         
2189         this.triggerEl.on('click', this.onTriggerClick, this);
2190         
2191         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2192         
2193         
2194         if (this.triggerEl.hasClass('nav-item')) {
2195             // dropdown toggle on the 'a' in BS4?
2196             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2197         } else {
2198             this.triggerEl.addClass('dropdown-toggle');
2199         }
2200         if (Roo.isTouch) {
2201             this.el.on('touchstart'  , this.onTouch, this);
2202         }
2203         this.el.on('click' , this.onClick, this);
2204
2205         this.el.on("mouseover", this.onMouseOver, this);
2206         this.el.on("mouseout", this.onMouseOut, this);
2207         
2208     },
2209     
2210     findTargetItem : function(e)
2211     {
2212         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2213         if(!t){
2214             return false;
2215         }
2216         //Roo.log(t);         Roo.log(t.id);
2217         if(t && t.id){
2218             //Roo.log(this.menuitems);
2219             return this.menuitems.get(t.id);
2220             
2221             //return this.items.get(t.menuItemId);
2222         }
2223         
2224         return false;
2225     },
2226     
2227     onTouch : function(e) 
2228     {
2229         Roo.log("menu.onTouch");
2230         //e.stopEvent(); this make the user popdown broken
2231         this.onClick(e);
2232     },
2233     
2234     onClick : function(e)
2235     {
2236         Roo.log("menu.onClick");
2237         
2238         var t = this.findTargetItem(e);
2239         if(!t || t.isContainer){
2240             return;
2241         }
2242         Roo.log(e);
2243         /*
2244         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2245             if(t == this.activeItem && t.shouldDeactivate(e)){
2246                 this.activeItem.deactivate();
2247                 delete this.activeItem;
2248                 return;
2249             }
2250             if(t.canActivate){
2251                 this.setActiveItem(t, true);
2252             }
2253             return;
2254             
2255             
2256         }
2257         */
2258        
2259         Roo.log('pass click event');
2260         
2261         t.onClick(e);
2262         
2263         this.fireEvent("click", this, t, e);
2264         
2265         var _this = this;
2266         
2267         if(!t.href.length || t.href == '#'){
2268             (function() { _this.hide(); }).defer(100);
2269         }
2270         
2271     },
2272     
2273     onMouseOver : function(e){
2274         var t  = this.findTargetItem(e);
2275         //Roo.log(t);
2276         //if(t){
2277         //    if(t.canActivate && !t.disabled){
2278         //        this.setActiveItem(t, true);
2279         //    }
2280         //}
2281         
2282         this.fireEvent("mouseover", this, e, t);
2283     },
2284     isVisible : function(){
2285         return !this.hidden;
2286     },
2287     onMouseOut : function(e){
2288         var t  = this.findTargetItem(e);
2289         
2290         //if(t ){
2291         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2292         //        this.activeItem.deactivate();
2293         //        delete this.activeItem;
2294         //    }
2295         //}
2296         this.fireEvent("mouseout", this, e, t);
2297     },
2298     
2299     
2300     /**
2301      * Displays this menu relative to another element
2302      * @param {String/HTMLElement/Roo.Element} element The element to align to
2303      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2304      * the element (defaults to this.defaultAlign)
2305      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2306      */
2307     show : function(el, pos, parentMenu)
2308     {
2309         if (false === this.fireEvent("beforeshow", this)) {
2310             Roo.log("show canceled");
2311             return;
2312         }
2313         this.parentMenu = parentMenu;
2314         if(!this.el){
2315             this.render();
2316         }
2317         
2318         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2319     },
2320      /**
2321      * Displays this menu at a specific xy position
2322      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2323      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2324      */
2325     showAt : function(xy, parentMenu, /* private: */_e){
2326         this.parentMenu = parentMenu;
2327         if(!this.el){
2328             this.render();
2329         }
2330         if(_e !== false){
2331             this.fireEvent("beforeshow", this);
2332             //xy = this.el.adjustForConstraints(xy);
2333         }
2334         
2335         //this.el.show();
2336         this.hideMenuItems();
2337         this.hidden = false;
2338         this.triggerEl.addClass('open');
2339         this.el.addClass('show');
2340         
2341         // reassign x when hitting right
2342         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2343             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2344         }
2345         
2346         // reassign y when hitting bottom
2347         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2348             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2349         }
2350         
2351         // but the list may align on trigger left or trigger top... should it be a properity?
2352         
2353         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2354             this.el.setXY(xy);
2355         }
2356         
2357         this.focus();
2358         this.fireEvent("show", this);
2359     },
2360     
2361     focus : function(){
2362         return;
2363         if(!this.hidden){
2364             this.doFocus.defer(50, this);
2365         }
2366     },
2367
2368     doFocus : function(){
2369         if(!this.hidden){
2370             this.focusEl.focus();
2371         }
2372     },
2373
2374     /**
2375      * Hides this menu and optionally all parent menus
2376      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2377      */
2378     hide : function(deep)
2379     {
2380         if (false === this.fireEvent("beforehide", this)) {
2381             Roo.log("hide canceled");
2382             return;
2383         }
2384         this.hideMenuItems();
2385         if(this.el && this.isVisible()){
2386            
2387             if(this.activeItem){
2388                 this.activeItem.deactivate();
2389                 this.activeItem = null;
2390             }
2391             this.triggerEl.removeClass('open');;
2392             this.el.removeClass('show');
2393             this.hidden = true;
2394             this.fireEvent("hide", this);
2395         }
2396         if(deep === true && this.parentMenu){
2397             this.parentMenu.hide(true);
2398         }
2399     },
2400     
2401     onTriggerClick : function(e)
2402     {
2403         Roo.log('trigger click');
2404         
2405         var target = e.getTarget();
2406         
2407         Roo.log(target.nodeName.toLowerCase());
2408         
2409         if(target.nodeName.toLowerCase() === 'i'){
2410             e.preventDefault();
2411         }
2412         
2413     },
2414     
2415     onTriggerPress  : function(e)
2416     {
2417         Roo.log('trigger press');
2418         //Roo.log(e.getTarget());
2419        // Roo.log(this.triggerEl.dom);
2420        
2421         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2422         var pel = Roo.get(e.getTarget());
2423         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2424             Roo.log('is treeview or dropdown?');
2425             return;
2426         }
2427         
2428         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2429             return;
2430         }
2431         
2432         if (this.isVisible()) {
2433             Roo.log('hide');
2434             this.hide();
2435         } else {
2436             Roo.log('show');
2437             this.show(this.triggerEl, '?', false);
2438         }
2439         
2440         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2441             e.stopEvent();
2442         }
2443         
2444     },
2445        
2446     
2447     hideMenuItems : function()
2448     {
2449         Roo.log("hide Menu Items");
2450         if (!this.el) { 
2451             return;
2452         }
2453         
2454         this.el.select('.open',true).each(function(aa) {
2455             
2456             aa.removeClass('open');
2457          
2458         });
2459     },
2460     addxtypeChild : function (tree, cntr) {
2461         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2462           
2463         this.menuitems.add(comp);
2464         return comp;
2465
2466     },
2467     getEl : function()
2468     {
2469         Roo.log(this.el);
2470         return this.el;
2471     },
2472     
2473     clear : function()
2474     {
2475         this.getEl().dom.innerHTML = '';
2476         this.menuitems.clear();
2477     }
2478 });
2479
2480  
2481  /*
2482  * - LGPL
2483  *
2484  * menu item
2485  * 
2486  */
2487
2488
2489 /**
2490  * @class Roo.bootstrap.MenuItem
2491  * @extends Roo.bootstrap.Component
2492  * Bootstrap MenuItem class
2493  * @cfg {String} html the menu label
2494  * @cfg {String} href the link
2495  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2496  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2497  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2498  * @cfg {String} fa favicon to show on left of menu item.
2499  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2500  * 
2501  * 
2502  * @constructor
2503  * Create a new MenuItem
2504  * @param {Object} config The config object
2505  */
2506
2507
2508 Roo.bootstrap.MenuItem = function(config){
2509     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2510     this.addEvents({
2511         // raw events
2512         /**
2513          * @event click
2514          * The raw click event for the entire grid.
2515          * @param {Roo.bootstrap.MenuItem} this
2516          * @param {Roo.EventObject} e
2517          */
2518         "click" : true
2519     });
2520 };
2521
2522 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2523     
2524     href : false,
2525     html : false,
2526     preventDefault: false,
2527     isContainer : false,
2528     active : false,
2529     fa: false,
2530     
2531     getAutoCreate : function(){
2532         
2533         if(this.isContainer){
2534             return {
2535                 tag: 'li',
2536                 cls: 'dropdown-menu-item '
2537             };
2538         }
2539         var ctag = {
2540             tag: 'span',
2541             html: 'Link'
2542         };
2543         
2544         var anc = {
2545             tag : 'a',
2546             cls : 'dropdown-item',
2547             href : '#',
2548             cn : [  ]
2549         };
2550         
2551         if (this.fa !== false) {
2552             anc.cn.push({
2553                 tag : 'i',
2554                 cls : 'fa fa-' + this.fa
2555             });
2556         }
2557         
2558         anc.cn.push(ctag);
2559         
2560         
2561         var cfg= {
2562             tag: 'li',
2563             cls: 'dropdown-menu-item',
2564             cn: [ anc ]
2565         };
2566         if (this.parent().type == 'treeview') {
2567             cfg.cls = 'treeview-menu';
2568         }
2569         if (this.active) {
2570             cfg.cls += ' active';
2571         }
2572         
2573         
2574         
2575         anc.href = this.href || cfg.cn[0].href ;
2576         ctag.html = this.html || cfg.cn[0].html ;
2577         return cfg;
2578     },
2579     
2580     initEvents: function()
2581     {
2582         if (this.parent().type == 'treeview') {
2583             this.el.select('a').on('click', this.onClick, this);
2584         }
2585         
2586         if (this.menu) {
2587             this.menu.parentType = this.xtype;
2588             this.menu.triggerEl = this.el;
2589             this.menu = this.addxtype(Roo.apply({}, this.menu));
2590         }
2591         
2592     },
2593     onClick : function(e)
2594     {
2595         Roo.log('item on click ');
2596         
2597         if(this.preventDefault){
2598             e.preventDefault();
2599         }
2600         //this.parent().hideMenuItems();
2601         
2602         this.fireEvent('click', this, e);
2603     },
2604     getEl : function()
2605     {
2606         return this.el;
2607     } 
2608 });
2609
2610  
2611
2612  /*
2613  * - LGPL
2614  *
2615  * menu separator
2616  * 
2617  */
2618
2619
2620 /**
2621  * @class Roo.bootstrap.MenuSeparator
2622  * @extends Roo.bootstrap.Component
2623  * Bootstrap MenuSeparator class
2624  * 
2625  * @constructor
2626  * Create a new MenuItem
2627  * @param {Object} config The config object
2628  */
2629
2630
2631 Roo.bootstrap.MenuSeparator = function(config){
2632     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2633 };
2634
2635 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2636     
2637     getAutoCreate : function(){
2638         var cfg = {
2639             cls: 'divider',
2640             tag : 'li'
2641         };
2642         
2643         return cfg;
2644     }
2645    
2646 });
2647
2648  
2649
2650  
2651 /*
2652 * Licence: LGPL
2653 */
2654
2655 /**
2656  * @class Roo.bootstrap.Modal
2657  * @extends Roo.bootstrap.Component
2658  * Bootstrap Modal class
2659  * @cfg {String} title Title of dialog
2660  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2661  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2662  * @cfg {Boolean} specificTitle default false
2663  * @cfg {Array} buttons Array of buttons or standard button set..
2664  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2665  * @cfg {Boolean} animate default true
2666  * @cfg {Boolean} allow_close default true
2667  * @cfg {Boolean} fitwindow default false
2668  * @cfg {String} size (sm|lg) default empty
2669  * @cfg {Number} max_width set the max width of modal
2670  *
2671  *
2672  * @constructor
2673  * Create a new Modal Dialog
2674  * @param {Object} config The config object
2675  */
2676
2677 Roo.bootstrap.Modal = function(config){
2678     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2679     this.addEvents({
2680         // raw events
2681         /**
2682          * @event btnclick
2683          * The raw btnclick event for the button
2684          * @param {Roo.EventObject} e
2685          */
2686         "btnclick" : true,
2687         /**
2688          * @event resize
2689          * Fire when dialog resize
2690          * @param {Roo.bootstrap.Modal} this
2691          * @param {Roo.EventObject} e
2692          */
2693         "resize" : true
2694     });
2695     this.buttons = this.buttons || [];
2696
2697     if (this.tmpl) {
2698         this.tmpl = Roo.factory(this.tmpl);
2699     }
2700
2701 };
2702
2703 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2704
2705     title : 'test dialog',
2706
2707     buttons : false,
2708
2709     // set on load...
2710
2711     html: false,
2712
2713     tmp: false,
2714
2715     specificTitle: false,
2716
2717     buttonPosition: 'right',
2718
2719     allow_close : true,
2720
2721     animate : true,
2722
2723     fitwindow: false,
2724     
2725      // private
2726     dialogEl: false,
2727     bodyEl:  false,
2728     footerEl:  false,
2729     titleEl:  false,
2730     closeEl:  false,
2731
2732     size: '',
2733     
2734     max_width: 0,
2735     
2736     max_height: 0,
2737     
2738     fit_content: false,
2739
2740     onRender : function(ct, position)
2741     {
2742         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2743
2744         if(!this.el){
2745             var cfg = Roo.apply({},  this.getAutoCreate());
2746             cfg.id = Roo.id();
2747             //if(!cfg.name){
2748             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2749             //}
2750             //if (!cfg.name.length) {
2751             //    delete cfg.name;
2752            // }
2753             if (this.cls) {
2754                 cfg.cls += ' ' + this.cls;
2755             }
2756             if (this.style) {
2757                 cfg.style = this.style;
2758             }
2759             this.el = Roo.get(document.body).createChild(cfg, position);
2760         }
2761         //var type = this.el.dom.type;
2762
2763
2764         if(this.tabIndex !== undefined){
2765             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2766         }
2767
2768         this.dialogEl = this.el.select('.modal-dialog',true).first();
2769         this.bodyEl = this.el.select('.modal-body',true).first();
2770         this.closeEl = this.el.select('.modal-header .close', true).first();
2771         this.headerEl = this.el.select('.modal-header',true).first();
2772         this.titleEl = this.el.select('.modal-title',true).first();
2773         this.footerEl = this.el.select('.modal-footer',true).first();
2774
2775         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2776         
2777         //this.el.addClass("x-dlg-modal");
2778
2779         if (this.buttons.length) {
2780             Roo.each(this.buttons, function(bb) {
2781                 var b = Roo.apply({}, bb);
2782                 b.xns = b.xns || Roo.bootstrap;
2783                 b.xtype = b.xtype || 'Button';
2784                 if (typeof(b.listeners) == 'undefined') {
2785                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2786                 }
2787
2788                 var btn = Roo.factory(b);
2789
2790                 btn.render(this.getButtonContainer());
2791
2792             },this);
2793         }
2794         // render the children.
2795         var nitems = [];
2796
2797         if(typeof(this.items) != 'undefined'){
2798             var items = this.items;
2799             delete this.items;
2800
2801             for(var i =0;i < items.length;i++) {
2802                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2803             }
2804         }
2805
2806         this.items = nitems;
2807
2808         // where are these used - they used to be body/close/footer
2809
2810
2811         this.initEvents();
2812         //this.el.addClass([this.fieldClass, this.cls]);
2813
2814     },
2815
2816     getAutoCreate : function()
2817     {
2818         var bdy = {
2819                 cls : 'modal-body',
2820                 html : this.html || ''
2821         };
2822
2823         var title = {
2824             tag: 'h4',
2825             cls : 'modal-title',
2826             html : this.title
2827         };
2828
2829         if(this.specificTitle){
2830             title = this.title;
2831
2832         }
2833
2834         var header = [];
2835         if (this.allow_close && Roo.bootstrap.version == 3) {
2836             header.push({
2837                 tag: 'button',
2838                 cls : 'close',
2839                 html : '&times'
2840             });
2841         }
2842
2843         header.push(title);
2844
2845         if (this.allow_close && Roo.bootstrap.version == 4) {
2846             header.push({
2847                 tag: 'button',
2848                 cls : 'close',
2849                 html : '&times'
2850             });
2851         }
2852         
2853         var size = '';
2854
2855         if(this.size.length){
2856             size = 'modal-' + this.size;
2857         }
2858         
2859         var footer = Roo.bootstrap.version == 3 ?
2860             {
2861                 cls : 'modal-footer',
2862                 cn : [
2863                     {
2864                         tag: 'div',
2865                         cls: 'btn-' + this.buttonPosition
2866                     }
2867                 ]
2868
2869             } :
2870             {  // BS4 uses mr-auto on left buttons....
2871                 cls : 'modal-footer'
2872             };
2873
2874             
2875
2876         
2877         
2878         var modal = {
2879             cls: "modal",
2880              cn : [
2881                 {
2882                     cls: "modal-dialog " + size,
2883                     cn : [
2884                         {
2885                             cls : "modal-content",
2886                             cn : [
2887                                 {
2888                                     cls : 'modal-header',
2889                                     cn : header
2890                                 },
2891                                 bdy,
2892                                 footer
2893                             ]
2894
2895                         }
2896                     ]
2897
2898                 }
2899             ]
2900         };
2901
2902         if(this.animate){
2903             modal.cls += ' fade';
2904         }
2905
2906         return modal;
2907
2908     },
2909     getChildContainer : function() {
2910
2911          return this.bodyEl;
2912
2913     },
2914     getButtonContainer : function() {
2915         
2916          return Roo.bootstrap.version == 4 ?
2917             this.el.select('.modal-footer',true).first()
2918             : this.el.select('.modal-footer div',true).first();
2919
2920     },
2921     initEvents : function()
2922     {
2923         if (this.allow_close) {
2924             this.closeEl.on('click', this.hide, this);
2925         }
2926         Roo.EventManager.onWindowResize(this.resize, this, true);
2927
2928
2929     },
2930   
2931
2932     resize : function()
2933     {
2934         this.maskEl.setSize(
2935             Roo.lib.Dom.getViewWidth(true),
2936             Roo.lib.Dom.getViewHeight(true)
2937         );
2938         
2939         if (this.fitwindow) {
2940             
2941            
2942             this.setSize(
2943                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2944                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2945             );
2946             return;
2947         }
2948         
2949         if(this.max_width !== 0) {
2950             
2951             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2952             
2953             if(this.height) {
2954                 this.setSize(w, this.height);
2955                 return;
2956             }
2957             
2958             if(this.max_height) {
2959                 this.setSize(w,Math.min(
2960                     this.max_height,
2961                     Roo.lib.Dom.getViewportHeight(true) - 60
2962                 ));
2963                 
2964                 return;
2965             }
2966             
2967             if(!this.fit_content) {
2968                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2969                 return;
2970             }
2971             
2972             this.setSize(w, Math.min(
2973                 60 +
2974                 this.headerEl.getHeight() + 
2975                 this.footerEl.getHeight() + 
2976                 this.getChildHeight(this.bodyEl.dom.childNodes),
2977                 Roo.lib.Dom.getViewportHeight(true) - 60)
2978             );
2979         }
2980         
2981     },
2982
2983     setSize : function(w,h)
2984     {
2985         if (!w && !h) {
2986             return;
2987         }
2988         
2989         this.resizeTo(w,h);
2990     },
2991
2992     show : function() {
2993
2994         if (!this.rendered) {
2995             this.render();
2996         }
2997
2998         //this.el.setStyle('display', 'block');
2999         this.el.removeClass('hideing');
3000         this.el.dom.style.display='block';
3001         
3002         Roo.get(document.body).addClass('modal-open');
3003  
3004         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3005             
3006             (function(){
3007                 this.el.addClass('show');
3008                 this.el.addClass('in');
3009             }).defer(50, this);
3010         }else{
3011             this.el.addClass('show');
3012             this.el.addClass('in');
3013         }
3014
3015         // not sure how we can show data in here..
3016         //if (this.tmpl) {
3017         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3018         //}
3019
3020         Roo.get(document.body).addClass("x-body-masked");
3021         
3022         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3023         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3024         this.maskEl.dom.style.display = 'block';
3025         this.maskEl.addClass('show');
3026         
3027         
3028         this.resize();
3029         
3030         this.fireEvent('show', this);
3031
3032         // set zindex here - otherwise it appears to be ignored...
3033         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3034
3035         (function () {
3036             this.items.forEach( function(e) {
3037                 e.layout ? e.layout() : false;
3038
3039             });
3040         }).defer(100,this);
3041
3042     },
3043     hide : function()
3044     {
3045         if(this.fireEvent("beforehide", this) !== false){
3046             
3047             this.maskEl.removeClass('show');
3048             
3049             this.maskEl.dom.style.display = '';
3050             Roo.get(document.body).removeClass("x-body-masked");
3051             this.el.removeClass('in');
3052             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3053
3054             if(this.animate){ // why
3055                 this.el.addClass('hideing');
3056                 this.el.removeClass('show');
3057                 (function(){
3058                     if (!this.el.hasClass('hideing')) {
3059                         return; // it's been shown again...
3060                     }
3061                     
3062                     this.el.dom.style.display='';
3063
3064                     Roo.get(document.body).removeClass('modal-open');
3065                     this.el.removeClass('hideing');
3066                 }).defer(150,this);
3067                 
3068             }else{
3069                 this.el.removeClass('show');
3070                 this.el.dom.style.display='';
3071                 Roo.get(document.body).removeClass('modal-open');
3072
3073             }
3074             this.fireEvent('hide', this);
3075         }
3076     },
3077     isVisible : function()
3078     {
3079         
3080         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3081         
3082     },
3083
3084     addButton : function(str, cb)
3085     {
3086
3087
3088         var b = Roo.apply({}, { html : str } );
3089         b.xns = b.xns || Roo.bootstrap;
3090         b.xtype = b.xtype || 'Button';
3091         if (typeof(b.listeners) == 'undefined') {
3092             b.listeners = { click : cb.createDelegate(this)  };
3093         }
3094
3095         var btn = Roo.factory(b);
3096
3097         btn.render(this.getButtonContainer());
3098
3099         return btn;
3100
3101     },
3102
3103     setDefaultButton : function(btn)
3104     {
3105         //this.el.select('.modal-footer').()
3106     },
3107
3108     resizeTo: function(w,h)
3109     {
3110         this.dialogEl.setWidth(w);
3111         
3112         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3113
3114         this.bodyEl.setHeight(h - diff);
3115         
3116         this.fireEvent('resize', this);
3117     },
3118     
3119     setContentSize  : function(w, h)
3120     {
3121
3122     },
3123     onButtonClick: function(btn,e)
3124     {
3125         //Roo.log([a,b,c]);
3126         this.fireEvent('btnclick', btn.name, e);
3127     },
3128      /**
3129      * Set the title of the Dialog
3130      * @param {String} str new Title
3131      */
3132     setTitle: function(str) {
3133         this.titleEl.dom.innerHTML = str;
3134     },
3135     /**
3136      * Set the body of the Dialog
3137      * @param {String} str new Title
3138      */
3139     setBody: function(str) {
3140         this.bodyEl.dom.innerHTML = str;
3141     },
3142     /**
3143      * Set the body of the Dialog using the template
3144      * @param {Obj} data - apply this data to the template and replace the body contents.
3145      */
3146     applyBody: function(obj)
3147     {
3148         if (!this.tmpl) {
3149             Roo.log("Error - using apply Body without a template");
3150             //code
3151         }
3152         this.tmpl.overwrite(this.bodyEl, obj);
3153     },
3154     
3155     getChildHeight : function(child_nodes)
3156     {
3157         if(
3158             !child_nodes ||
3159             child_nodes.length == 0
3160         ) {
3161             return;
3162         }
3163         
3164         var child_height = 0;
3165         
3166         for(var i = 0; i < child_nodes.length; i++) {
3167             
3168             /*
3169             * for modal with tabs...
3170             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3171                 
3172                 var layout_childs = child_nodes[i].childNodes;
3173                 
3174                 for(var j = 0; j < layout_childs.length; j++) {
3175                     
3176                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3177                         
3178                         var layout_body_childs = layout_childs[j].childNodes;
3179                         
3180                         for(var k = 0; k < layout_body_childs.length; k++) {
3181                             
3182                             if(layout_body_childs[k].classList.contains('navbar')) {
3183                                 child_height += layout_body_childs[k].offsetHeight;
3184                                 continue;
3185                             }
3186                             
3187                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3188                                 
3189                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3190                                 
3191                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3192                                     
3193                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3194                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3195                                         continue;
3196                                     }
3197                                     
3198                                 }
3199                                 
3200                             }
3201                             
3202                         }
3203                     }
3204                 }
3205                 continue;
3206             }
3207             */
3208             
3209             child_height += child_nodes[i].offsetHeight;
3210             // Roo.log(child_nodes[i].offsetHeight);
3211         }
3212         
3213         return child_height;
3214     }
3215
3216 });
3217
3218
3219 Roo.apply(Roo.bootstrap.Modal,  {
3220     /**
3221          * Button config that displays a single OK button
3222          * @type Object
3223          */
3224         OK :  [{
3225             name : 'ok',
3226             weight : 'primary',
3227             html : 'OK'
3228         }],
3229         /**
3230          * Button config that displays Yes and No buttons
3231          * @type Object
3232          */
3233         YESNO : [
3234             {
3235                 name  : 'no',
3236                 html : 'No'
3237             },
3238             {
3239                 name  :'yes',
3240                 weight : 'primary',
3241                 html : 'Yes'
3242             }
3243         ],
3244
3245         /**
3246          * Button config that displays OK and Cancel buttons
3247          * @type Object
3248          */
3249         OKCANCEL : [
3250             {
3251                name : 'cancel',
3252                 html : 'Cancel'
3253             },
3254             {
3255                 name : 'ok',
3256                 weight : 'primary',
3257                 html : 'OK'
3258             }
3259         ],
3260         /**
3261          * Button config that displays Yes, No and Cancel buttons
3262          * @type Object
3263          */
3264         YESNOCANCEL : [
3265             {
3266                 name : 'yes',
3267                 weight : 'primary',
3268                 html : 'Yes'
3269             },
3270             {
3271                 name : 'no',
3272                 html : 'No'
3273             },
3274             {
3275                 name : 'cancel',
3276                 html : 'Cancel'
3277             }
3278         ],
3279         
3280         zIndex : 10001
3281 });
3282 /*
3283  * - LGPL
3284  *
3285  * messagebox - can be used as a replace
3286  * 
3287  */
3288 /**
3289  * @class Roo.MessageBox
3290  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3291  * Example usage:
3292  *<pre><code>
3293 // Basic alert:
3294 Roo.Msg.alert('Status', 'Changes saved successfully.');
3295
3296 // Prompt for user data:
3297 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3298     if (btn == 'ok'){
3299         // process text value...
3300     }
3301 });
3302
3303 // Show a dialog using config options:
3304 Roo.Msg.show({
3305    title:'Save Changes?',
3306    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3307    buttons: Roo.Msg.YESNOCANCEL,
3308    fn: processResult,
3309    animEl: 'elId'
3310 });
3311 </code></pre>
3312  * @singleton
3313  */
3314 Roo.bootstrap.MessageBox = function(){
3315     var dlg, opt, mask, waitTimer;
3316     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3317     var buttons, activeTextEl, bwidth;
3318
3319     
3320     // private
3321     var handleButton = function(button){
3322         dlg.hide();
3323         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3324     };
3325
3326     // private
3327     var handleHide = function(){
3328         if(opt && opt.cls){
3329             dlg.el.removeClass(opt.cls);
3330         }
3331         //if(waitTimer){
3332         //    Roo.TaskMgr.stop(waitTimer);
3333         //    waitTimer = null;
3334         //}
3335     };
3336
3337     // private
3338     var updateButtons = function(b){
3339         var width = 0;
3340         if(!b){
3341             buttons["ok"].hide();
3342             buttons["cancel"].hide();
3343             buttons["yes"].hide();
3344             buttons["no"].hide();
3345             dlg.footerEl.hide();
3346             
3347             return width;
3348         }
3349         dlg.footerEl.show();
3350         for(var k in buttons){
3351             if(typeof buttons[k] != "function"){
3352                 if(b[k]){
3353                     buttons[k].show();
3354                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3355                     width += buttons[k].el.getWidth()+15;
3356                 }else{
3357                     buttons[k].hide();
3358                 }
3359             }
3360         }
3361         return width;
3362     };
3363
3364     // private
3365     var handleEsc = function(d, k, e){
3366         if(opt && opt.closable !== false){
3367             dlg.hide();
3368         }
3369         if(e){
3370             e.stopEvent();
3371         }
3372     };
3373
3374     return {
3375         /**
3376          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3377          * @return {Roo.BasicDialog} The BasicDialog element
3378          */
3379         getDialog : function(){
3380            if(!dlg){
3381                 dlg = new Roo.bootstrap.Modal( {
3382                     //draggable: true,
3383                     //resizable:false,
3384                     //constraintoviewport:false,
3385                     //fixedcenter:true,
3386                     //collapsible : false,
3387                     //shim:true,
3388                     //modal: true,
3389                 //    width: 'auto',
3390                   //  height:100,
3391                     //buttonAlign:"center",
3392                     closeClick : function(){
3393                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3394                             handleButton("no");
3395                         }else{
3396                             handleButton("cancel");
3397                         }
3398                     }
3399                 });
3400                 dlg.render();
3401                 dlg.on("hide", handleHide);
3402                 mask = dlg.mask;
3403                 //dlg.addKeyListener(27, handleEsc);
3404                 buttons = {};
3405                 this.buttons = buttons;
3406                 var bt = this.buttonText;
3407                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3408                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3409                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3410                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3411                 //Roo.log(buttons);
3412                 bodyEl = dlg.bodyEl.createChild({
3413
3414                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3415                         '<textarea class="roo-mb-textarea"></textarea>' +
3416                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3417                 });
3418                 msgEl = bodyEl.dom.firstChild;
3419                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3420                 textboxEl.enableDisplayMode();
3421                 textboxEl.addKeyListener([10,13], function(){
3422                     if(dlg.isVisible() && opt && opt.buttons){
3423                         if(opt.buttons.ok){
3424                             handleButton("ok");
3425                         }else if(opt.buttons.yes){
3426                             handleButton("yes");
3427                         }
3428                     }
3429                 });
3430                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3431                 textareaEl.enableDisplayMode();
3432                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3433                 progressEl.enableDisplayMode();
3434                 
3435                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3436                 var pf = progressEl.dom.firstChild;
3437                 if (pf) {
3438                     pp = Roo.get(pf.firstChild);
3439                     pp.setHeight(pf.offsetHeight);
3440                 }
3441                 
3442             }
3443             return dlg;
3444         },
3445
3446         /**
3447          * Updates the message box body text
3448          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3449          * the XHTML-compliant non-breaking space character '&amp;#160;')
3450          * @return {Roo.MessageBox} This message box
3451          */
3452         updateText : function(text)
3453         {
3454             if(!dlg.isVisible() && !opt.width){
3455                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3456                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3457             }
3458             msgEl.innerHTML = text || '&#160;';
3459       
3460             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3461             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3462             var w = Math.max(
3463                     Math.min(opt.width || cw , this.maxWidth), 
3464                     Math.max(opt.minWidth || this.minWidth, bwidth)
3465             );
3466             if(opt.prompt){
3467                 activeTextEl.setWidth(w);
3468             }
3469             if(dlg.isVisible()){
3470                 dlg.fixedcenter = false;
3471             }
3472             // to big, make it scroll. = But as usual stupid IE does not support
3473             // !important..
3474             
3475             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3476                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3477                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3478             } else {
3479                 bodyEl.dom.style.height = '';
3480                 bodyEl.dom.style.overflowY = '';
3481             }
3482             if (cw > w) {
3483                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3484             } else {
3485                 bodyEl.dom.style.overflowX = '';
3486             }
3487             
3488             dlg.setContentSize(w, bodyEl.getHeight());
3489             if(dlg.isVisible()){
3490                 dlg.fixedcenter = true;
3491             }
3492             return this;
3493         },
3494
3495         /**
3496          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3497          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3498          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3499          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3500          * @return {Roo.MessageBox} This message box
3501          */
3502         updateProgress : function(value, text){
3503             if(text){
3504                 this.updateText(text);
3505             }
3506             
3507             if (pp) { // weird bug on my firefox - for some reason this is not defined
3508                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3509                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3510             }
3511             return this;
3512         },        
3513
3514         /**
3515          * Returns true if the message box is currently displayed
3516          * @return {Boolean} True if the message box is visible, else false
3517          */
3518         isVisible : function(){
3519             return dlg && dlg.isVisible();  
3520         },
3521
3522         /**
3523          * Hides the message box if it is displayed
3524          */
3525         hide : function(){
3526             if(this.isVisible()){
3527                 dlg.hide();
3528             }  
3529         },
3530
3531         /**
3532          * Displays a new message box, or reinitializes an existing message box, based on the config options
3533          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3534          * The following config object properties are supported:
3535          * <pre>
3536 Property    Type             Description
3537 ----------  ---------------  ------------------------------------------------------------------------------------
3538 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3539                                    closes (defaults to undefined)
3540 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3541                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3542 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3543                                    progress and wait dialogs will ignore this property and always hide the
3544                                    close button as they can only be closed programmatically.
3545 cls               String           A custom CSS class to apply to the message box element
3546 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3547                                    displayed (defaults to 75)
3548 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3549                                    function will be btn (the name of the button that was clicked, if applicable,
3550                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3551                                    Progress and wait dialogs will ignore this option since they do not respond to
3552                                    user actions and can only be closed programmatically, so any required function
3553                                    should be called by the same code after it closes the dialog.
3554 icon              String           A CSS class that provides a background image to be used as an icon for
3555                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3556 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3557 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3558 modal             Boolean          False to allow user interaction with the page while the message box is
3559                                    displayed (defaults to true)
3560 msg               String           A string that will replace the existing message box body text (defaults
3561                                    to the XHTML-compliant non-breaking space character '&#160;')
3562 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3563 progress          Boolean          True to display a progress bar (defaults to false)
3564 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3565 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3566 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3567 title             String           The title text
3568 value             String           The string value to set into the active textbox element if displayed
3569 wait              Boolean          True to display a progress bar (defaults to false)
3570 width             Number           The width of the dialog in pixels
3571 </pre>
3572          *
3573          * Example usage:
3574          * <pre><code>
3575 Roo.Msg.show({
3576    title: 'Address',
3577    msg: 'Please enter your address:',
3578    width: 300,
3579    buttons: Roo.MessageBox.OKCANCEL,
3580    multiline: true,
3581    fn: saveAddress,
3582    animEl: 'addAddressBtn'
3583 });
3584 </code></pre>
3585          * @param {Object} config Configuration options
3586          * @return {Roo.MessageBox} This message box
3587          */
3588         show : function(options)
3589         {
3590             
3591             // this causes nightmares if you show one dialog after another
3592             // especially on callbacks..
3593              
3594             if(this.isVisible()){
3595                 
3596                 this.hide();
3597                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3598                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3599                 Roo.log("New Dialog Message:" +  options.msg )
3600                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3601                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3602                 
3603             }
3604             var d = this.getDialog();
3605             opt = options;
3606             d.setTitle(opt.title || "&#160;");
3607             d.closeEl.setDisplayed(opt.closable !== false);
3608             activeTextEl = textboxEl;
3609             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3610             if(opt.prompt){
3611                 if(opt.multiline){
3612                     textboxEl.hide();
3613                     textareaEl.show();
3614                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3615                         opt.multiline : this.defaultTextHeight);
3616                     activeTextEl = textareaEl;
3617                 }else{
3618                     textboxEl.show();
3619                     textareaEl.hide();
3620                 }
3621             }else{
3622                 textboxEl.hide();
3623                 textareaEl.hide();
3624             }
3625             progressEl.setDisplayed(opt.progress === true);
3626             if (opt.progress) {
3627                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3628             }
3629             this.updateProgress(0);
3630             activeTextEl.dom.value = opt.value || "";
3631             if(opt.prompt){
3632                 dlg.setDefaultButton(activeTextEl);
3633             }else{
3634                 var bs = opt.buttons;
3635                 var db = null;
3636                 if(bs && bs.ok){
3637                     db = buttons["ok"];
3638                 }else if(bs && bs.yes){
3639                     db = buttons["yes"];
3640                 }
3641                 dlg.setDefaultButton(db);
3642             }
3643             bwidth = updateButtons(opt.buttons);
3644             this.updateText(opt.msg);
3645             if(opt.cls){
3646                 d.el.addClass(opt.cls);
3647             }
3648             d.proxyDrag = opt.proxyDrag === true;
3649             d.modal = opt.modal !== false;
3650             d.mask = opt.modal !== false ? mask : false;
3651             if(!d.isVisible()){
3652                 // force it to the end of the z-index stack so it gets a cursor in FF
3653                 document.body.appendChild(dlg.el.dom);
3654                 d.animateTarget = null;
3655                 d.show(options.animEl);
3656             }
3657             return this;
3658         },
3659
3660         /**
3661          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3662          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3663          * and closing the message box when the process is complete.
3664          * @param {String} title The title bar text
3665          * @param {String} msg The message box body text
3666          * @return {Roo.MessageBox} This message box
3667          */
3668         progress : function(title, msg){
3669             this.show({
3670                 title : title,
3671                 msg : msg,
3672                 buttons: false,
3673                 progress:true,
3674                 closable:false,
3675                 minWidth: this.minProgressWidth,
3676                 modal : true
3677             });
3678             return this;
3679         },
3680
3681         /**
3682          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3683          * If a callback function is passed it will be called after the user clicks the button, and the
3684          * id of the button that was clicked will be passed as the only parameter to the callback
3685          * (could also be the top-right close button).
3686          * @param {String} title The title bar text
3687          * @param {String} msg The message box body text
3688          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3689          * @param {Object} scope (optional) The scope of the callback function
3690          * @return {Roo.MessageBox} This message box
3691          */
3692         alert : function(title, msg, fn, scope)
3693         {
3694             this.show({
3695                 title : title,
3696                 msg : msg,
3697                 buttons: this.OK,
3698                 fn: fn,
3699                 closable : false,
3700                 scope : scope,
3701                 modal : true
3702             });
3703             return this;
3704         },
3705
3706         /**
3707          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3708          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3709          * You are responsible for closing the message box when the process is complete.
3710          * @param {String} msg The message box body text
3711          * @param {String} title (optional) The title bar text
3712          * @return {Roo.MessageBox} This message box
3713          */
3714         wait : function(msg, title){
3715             this.show({
3716                 title : title,
3717                 msg : msg,
3718                 buttons: false,
3719                 closable:false,
3720                 progress:true,
3721                 modal:true,
3722                 width:300,
3723                 wait:true
3724             });
3725             waitTimer = Roo.TaskMgr.start({
3726                 run: function(i){
3727                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3728                 },
3729                 interval: 1000
3730             });
3731             return this;
3732         },
3733
3734         /**
3735          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3736          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3737          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3738          * @param {String} title The title bar text
3739          * @param {String} msg The message box body text
3740          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3741          * @param {Object} scope (optional) The scope of the callback function
3742          * @return {Roo.MessageBox} This message box
3743          */
3744         confirm : function(title, msg, fn, scope){
3745             this.show({
3746                 title : title,
3747                 msg : msg,
3748                 buttons: this.YESNO,
3749                 fn: fn,
3750                 scope : scope,
3751                 modal : true
3752             });
3753             return this;
3754         },
3755
3756         /**
3757          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3758          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3759          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3760          * (could also be the top-right close button) and the text that was entered will be passed as the two
3761          * parameters to the callback.
3762          * @param {String} title The title bar text
3763          * @param {String} msg The message box body text
3764          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3765          * @param {Object} scope (optional) The scope of the callback function
3766          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3767          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3768          * @return {Roo.MessageBox} This message box
3769          */
3770         prompt : function(title, msg, fn, scope, multiline){
3771             this.show({
3772                 title : title,
3773                 msg : msg,
3774                 buttons: this.OKCANCEL,
3775                 fn: fn,
3776                 minWidth:250,
3777                 scope : scope,
3778                 prompt:true,
3779                 multiline: multiline,
3780                 modal : true
3781             });
3782             return this;
3783         },
3784
3785         /**
3786          * Button config that displays a single OK button
3787          * @type Object
3788          */
3789         OK : {ok:true},
3790         /**
3791          * Button config that displays Yes and No buttons
3792          * @type Object
3793          */
3794         YESNO : {yes:true, no:true},
3795         /**
3796          * Button config that displays OK and Cancel buttons
3797          * @type Object
3798          */
3799         OKCANCEL : {ok:true, cancel:true},
3800         /**
3801          * Button config that displays Yes, No and Cancel buttons
3802          * @type Object
3803          */
3804         YESNOCANCEL : {yes:true, no:true, cancel:true},
3805
3806         /**
3807          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3808          * @type Number
3809          */
3810         defaultTextHeight : 75,
3811         /**
3812          * The maximum width in pixels of the message box (defaults to 600)
3813          * @type Number
3814          */
3815         maxWidth : 600,
3816         /**
3817          * The minimum width in pixels of the message box (defaults to 100)
3818          * @type Number
3819          */
3820         minWidth : 100,
3821         /**
3822          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3823          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3824          * @type Number
3825          */
3826         minProgressWidth : 250,
3827         /**
3828          * An object containing the default button text strings that can be overriden for localized language support.
3829          * Supported properties are: ok, cancel, yes and no.
3830          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3831          * @type Object
3832          */
3833         buttonText : {
3834             ok : "OK",
3835             cancel : "Cancel",
3836             yes : "Yes",
3837             no : "No"
3838         }
3839     };
3840 }();
3841
3842 /**
3843  * Shorthand for {@link Roo.MessageBox}
3844  */
3845 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3846 Roo.Msg = Roo.Msg || Roo.MessageBox;
3847 /*
3848  * - LGPL
3849  *
3850  * navbar
3851  * 
3852  */
3853
3854 /**
3855  * @class Roo.bootstrap.Navbar
3856  * @extends Roo.bootstrap.Component
3857  * Bootstrap Navbar class
3858
3859  * @constructor
3860  * Create a new Navbar
3861  * @param {Object} config The config object
3862  */
3863
3864
3865 Roo.bootstrap.Navbar = function(config){
3866     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3867     this.addEvents({
3868         // raw events
3869         /**
3870          * @event beforetoggle
3871          * Fire before toggle the menu
3872          * @param {Roo.EventObject} e
3873          */
3874         "beforetoggle" : true
3875     });
3876 };
3877
3878 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3879     
3880     
3881    
3882     // private
3883     navItems : false,
3884     loadMask : false,
3885     
3886     
3887     getAutoCreate : function(){
3888         
3889         
3890         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3891         
3892     },
3893     
3894     initEvents :function ()
3895     {
3896         //Roo.log(this.el.select('.navbar-toggle',true));
3897         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3898         
3899         var mark = {
3900             tag: "div",
3901             cls:"x-dlg-mask"
3902         };
3903         
3904         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3905         
3906         var size = this.el.getSize();
3907         this.maskEl.setSize(size.width, size.height);
3908         this.maskEl.enableDisplayMode("block");
3909         this.maskEl.hide();
3910         
3911         if(this.loadMask){
3912             this.maskEl.show();
3913         }
3914     },
3915     
3916     
3917     getChildContainer : function()
3918     {
3919         if (this.el && this.el.select('.collapse').getCount()) {
3920             return this.el.select('.collapse',true).first();
3921         }
3922         
3923         return this.el;
3924     },
3925     
3926     mask : function()
3927     {
3928         this.maskEl.show();
3929     },
3930     
3931     unmask : function()
3932     {
3933         this.maskEl.hide();
3934     },
3935     onToggle : function()
3936     {
3937         
3938         if(this.fireEvent('beforetoggle', this) === false){
3939             return;
3940         }
3941         var ce = this.el.select('.navbar-collapse',true).first();
3942       
3943         if (!ce.hasClass('show')) {
3944            this.expand();
3945         } else {
3946             this.collapse();
3947         }
3948         
3949         
3950     
3951     },
3952     /**
3953      * Expand the navbar pulldown 
3954      */
3955     expand : function ()
3956     {
3957        
3958         var ce = this.el.select('.navbar-collapse',true).first();
3959         if (ce.hasClass('collapsing')) {
3960             return;
3961         }
3962         ce.dom.style.height = '';
3963                // show it...
3964         ce.addClass('in'); // old...
3965         ce.removeClass('collapse');
3966         ce.addClass('show');
3967         var h = ce.getHeight();
3968         Roo.log(h);
3969         ce.removeClass('show');
3970         // at this point we should be able to see it..
3971         ce.addClass('collapsing');
3972         
3973         ce.setHeight(0); // resize it ...
3974         ce.on('transitionend', function() {
3975             //Roo.log('done transition');
3976             ce.removeClass('collapsing');
3977             ce.addClass('show');
3978             ce.removeClass('collapse');
3979
3980             ce.dom.style.height = '';
3981         }, this, { single: true} );
3982         ce.setHeight(h);
3983         ce.dom.scrollTop = 0;
3984     },
3985     /**
3986      * Collapse the navbar pulldown 
3987      */
3988     collapse : function()
3989     {
3990          var ce = this.el.select('.navbar-collapse',true).first();
3991        
3992         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3993             // it's collapsed or collapsing..
3994             return;
3995         }
3996         ce.removeClass('in'); // old...
3997         ce.setHeight(ce.getHeight());
3998         ce.removeClass('show');
3999         ce.addClass('collapsing');
4000         
4001         ce.on('transitionend', function() {
4002             ce.dom.style.height = '';
4003             ce.removeClass('collapsing');
4004             ce.addClass('collapse');
4005         }, this, { single: true} );
4006         ce.setHeight(0);
4007     }
4008     
4009     
4010     
4011 });
4012
4013
4014
4015  
4016
4017  /*
4018  * - LGPL
4019  *
4020  * navbar
4021  * 
4022  */
4023
4024 /**
4025  * @class Roo.bootstrap.NavSimplebar
4026  * @extends Roo.bootstrap.Navbar
4027  * Bootstrap Sidebar class
4028  *
4029  * @cfg {Boolean} inverse is inverted color
4030  * 
4031  * @cfg {String} type (nav | pills | tabs)
4032  * @cfg {Boolean} arrangement stacked | justified
4033  * @cfg {String} align (left | right) alignment
4034  * 
4035  * @cfg {Boolean} main (true|false) main nav bar? default false
4036  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4037  * 
4038  * @cfg {String} tag (header|footer|nav|div) default is nav 
4039
4040  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4041  * 
4042  * 
4043  * @constructor
4044  * Create a new Sidebar
4045  * @param {Object} config The config object
4046  */
4047
4048
4049 Roo.bootstrap.NavSimplebar = function(config){
4050     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4051 };
4052
4053 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4054     
4055     inverse: false,
4056     
4057     type: false,
4058     arrangement: '',
4059     align : false,
4060     
4061     weight : 'light',
4062     
4063     main : false,
4064     
4065     
4066     tag : false,
4067     
4068     
4069     getAutoCreate : function(){
4070         
4071         
4072         var cfg = {
4073             tag : this.tag || 'div',
4074             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4075         };
4076         if (['light','white'].indexOf(this.weight) > -1) {
4077             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4078         }
4079         cfg.cls += ' bg-' + this.weight;
4080         
4081         if (this.inverse) {
4082             cfg.cls += ' navbar-inverse';
4083             
4084         }
4085         
4086         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4087         
4088         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4089             return cfg;
4090         }
4091         
4092         
4093     
4094         
4095         cfg.cn = [
4096             {
4097                 cls: 'nav nav-' + this.xtype,
4098                 tag : 'ul'
4099             }
4100         ];
4101         
4102          
4103         this.type = this.type || 'nav';
4104         if (['tabs','pills'].indexOf(this.type) != -1) {
4105             cfg.cn[0].cls += ' nav-' + this.type
4106         
4107         
4108         } else {
4109             if (this.type!=='nav') {
4110                 Roo.log('nav type must be nav/tabs/pills')
4111             }
4112             cfg.cn[0].cls += ' navbar-nav'
4113         }
4114         
4115         
4116         
4117         
4118         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4119             cfg.cn[0].cls += ' nav-' + this.arrangement;
4120         }
4121         
4122         
4123         if (this.align === 'right') {
4124             cfg.cn[0].cls += ' navbar-right';
4125         }
4126         
4127         
4128         
4129         
4130         return cfg;
4131     
4132         
4133     }
4134     
4135     
4136     
4137 });
4138
4139
4140
4141  
4142
4143  
4144        /*
4145  * - LGPL
4146  *
4147  * navbar
4148  * navbar-fixed-top
4149  * navbar-expand-md  fixed-top 
4150  */
4151
4152 /**
4153  * @class Roo.bootstrap.NavHeaderbar
4154  * @extends Roo.bootstrap.NavSimplebar
4155  * Bootstrap Sidebar class
4156  *
4157  * @cfg {String} brand what is brand
4158  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4159  * @cfg {String} brand_href href of the brand
4160  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4161  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4162  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4163  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4164  * 
4165  * @constructor
4166  * Create a new Sidebar
4167  * @param {Object} config The config object
4168  */
4169
4170
4171 Roo.bootstrap.NavHeaderbar = function(config){
4172     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4173       
4174 };
4175
4176 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4177     
4178     position: '',
4179     brand: '',
4180     brand_href: false,
4181     srButton : true,
4182     autohide : false,
4183     desktopCenter : false,
4184    
4185     
4186     getAutoCreate : function(){
4187         
4188         var   cfg = {
4189             tag: this.nav || 'nav',
4190             cls: 'navbar navbar-expand-md',
4191             role: 'navigation',
4192             cn: []
4193         };
4194         
4195         var cn = cfg.cn;
4196         if (this.desktopCenter) {
4197             cn.push({cls : 'container', cn : []});
4198             cn = cn[0].cn;
4199         }
4200         
4201         if(this.srButton){
4202             var btn = {
4203                 tag: 'button',
4204                 type: 'button',
4205                 cls: 'navbar-toggle navbar-toggler',
4206                 'data-toggle': 'collapse',
4207                 cn: [
4208                     {
4209                         tag: 'span',
4210                         cls: 'sr-only',
4211                         html: 'Toggle navigation'
4212                     },
4213                     {
4214                         tag: 'span',
4215                         cls: 'icon-bar navbar-toggler-icon'
4216                     },
4217                     {
4218                         tag: 'span',
4219                         cls: 'icon-bar'
4220                     },
4221                     {
4222                         tag: 'span',
4223                         cls: 'icon-bar'
4224                     }
4225                 ]
4226             };
4227             
4228             cn.push( Roo.bootstrap.version == 4 ? btn : {
4229                 tag: 'div',
4230                 cls: 'navbar-header',
4231                 cn: [
4232                     btn
4233                 ]
4234             });
4235         }
4236         
4237         cn.push({
4238             tag: 'div',
4239             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4240             cn : []
4241         });
4242         
4243         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4244         
4245         if (['light','white'].indexOf(this.weight) > -1) {
4246             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4247         }
4248         cfg.cls += ' bg-' + this.weight;
4249         
4250         
4251         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4252             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4253             
4254             // tag can override this..
4255             
4256             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4257         }
4258         
4259         if (this.brand !== '') {
4260             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4261             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4262                 tag: 'a',
4263                 href: this.brand_href ? this.brand_href : '#',
4264                 cls: 'navbar-brand',
4265                 cn: [
4266                 this.brand
4267                 ]
4268             });
4269         }
4270         
4271         if(this.main){
4272             cfg.cls += ' main-nav';
4273         }
4274         
4275         
4276         return cfg;
4277
4278         
4279     },
4280     getHeaderChildContainer : function()
4281     {
4282         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4283             return this.el.select('.navbar-header',true).first();
4284         }
4285         
4286         return this.getChildContainer();
4287     },
4288     
4289     
4290     initEvents : function()
4291     {
4292         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4293         
4294         if (this.autohide) {
4295             
4296             var prevScroll = 0;
4297             var ft = this.el;
4298             
4299             Roo.get(document).on('scroll',function(e) {
4300                 var ns = Roo.get(document).getScroll().top;
4301                 var os = prevScroll;
4302                 prevScroll = ns;
4303                 
4304                 if(ns > os){
4305                     ft.removeClass('slideDown');
4306                     ft.addClass('slideUp');
4307                     return;
4308                 }
4309                 ft.removeClass('slideUp');
4310                 ft.addClass('slideDown');
4311                  
4312               
4313           },this);
4314         }
4315     }    
4316     
4317 });
4318
4319
4320
4321  
4322
4323  /*
4324  * - LGPL
4325  *
4326  * navbar
4327  * 
4328  */
4329
4330 /**
4331  * @class Roo.bootstrap.NavSidebar
4332  * @extends Roo.bootstrap.Navbar
4333  * Bootstrap Sidebar class
4334  * 
4335  * @constructor
4336  * Create a new Sidebar
4337  * @param {Object} config The config object
4338  */
4339
4340
4341 Roo.bootstrap.NavSidebar = function(config){
4342     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4343 };
4344
4345 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4346     
4347     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4348     
4349     getAutoCreate : function(){
4350         
4351         
4352         return  {
4353             tag: 'div',
4354             cls: 'sidebar sidebar-nav'
4355         };
4356     
4357         
4358     }
4359     
4360     
4361     
4362 });
4363
4364
4365
4366  
4367
4368  /*
4369  * - LGPL
4370  *
4371  * nav group
4372  * 
4373  */
4374
4375 /**
4376  * @class Roo.bootstrap.NavGroup
4377  * @extends Roo.bootstrap.Component
4378  * Bootstrap NavGroup class
4379  * @cfg {String} align (left|right)
4380  * @cfg {Boolean} inverse
4381  * @cfg {String} type (nav|pills|tab) default nav
4382  * @cfg {String} navId - reference Id for navbar.
4383
4384  * 
4385  * @constructor
4386  * Create a new nav group
4387  * @param {Object} config The config object
4388  */
4389
4390 Roo.bootstrap.NavGroup = function(config){
4391     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4392     this.navItems = [];
4393    
4394     Roo.bootstrap.NavGroup.register(this);
4395      this.addEvents({
4396         /**
4397              * @event changed
4398              * Fires when the active item changes
4399              * @param {Roo.bootstrap.NavGroup} this
4400              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4401              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4402          */
4403         'changed': true
4404      });
4405     
4406 };
4407
4408 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4409     
4410     align: '',
4411     inverse: false,
4412     form: false,
4413     type: 'nav',
4414     navId : '',
4415     // private
4416     
4417     navItems : false, 
4418     
4419     getAutoCreate : function()
4420     {
4421         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4422         
4423         cfg = {
4424             tag : 'ul',
4425             cls: 'nav' 
4426         };
4427         if (Roo.bootstrap.version == 4) {
4428             if (['tabs','pills'].indexOf(this.type) != -1) {
4429                 cfg.cls += ' nav-' + this.type; 
4430             } else {
4431                 // trying to remove so header bar can right align top?
4432                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4433                     // do not use on header bar... 
4434                     cfg.cls += ' navbar-nav';
4435                 }
4436             }
4437             
4438         } else {
4439             if (['tabs','pills'].indexOf(this.type) != -1) {
4440                 cfg.cls += ' nav-' + this.type
4441             } else {
4442                 if (this.type !== 'nav') {
4443                     Roo.log('nav type must be nav/tabs/pills')
4444                 }
4445                 cfg.cls += ' navbar-nav'
4446             }
4447         }
4448         
4449         if (this.parent() && this.parent().sidebar) {
4450             cfg = {
4451                 tag: 'ul',
4452                 cls: 'dashboard-menu sidebar-menu'
4453             };
4454             
4455             return cfg;
4456         }
4457         
4458         if (this.form === true) {
4459             cfg = {
4460                 tag: 'form',
4461                 cls: 'navbar-form form-inline'
4462             };
4463             //nav navbar-right ml-md-auto
4464             if (this.align === 'right') {
4465                 cfg.cls += ' navbar-right ml-md-auto';
4466             } else {
4467                 cfg.cls += ' navbar-left';
4468             }
4469         }
4470         
4471         if (this.align === 'right') {
4472             cfg.cls += ' navbar-right ml-md-auto';
4473         } else {
4474             cfg.cls += ' mr-auto';
4475         }
4476         
4477         if (this.inverse) {
4478             cfg.cls += ' navbar-inverse';
4479             
4480         }
4481         
4482         
4483         return cfg;
4484     },
4485     /**
4486     * sets the active Navigation item
4487     * @param {Roo.bootstrap.NavItem} the new current navitem
4488     */
4489     setActiveItem : function(item)
4490     {
4491         var prev = false;
4492         Roo.each(this.navItems, function(v){
4493             if (v == item) {
4494                 return ;
4495             }
4496             if (v.isActive()) {
4497                 v.setActive(false, true);
4498                 prev = v;
4499                 
4500             }
4501             
4502         });
4503
4504         item.setActive(true, true);
4505         this.fireEvent('changed', this, item, prev);
4506         
4507         
4508     },
4509     /**
4510     * gets the active Navigation item
4511     * @return {Roo.bootstrap.NavItem} the current navitem
4512     */
4513     getActive : function()
4514     {
4515         
4516         var prev = false;
4517         Roo.each(this.navItems, function(v){
4518             
4519             if (v.isActive()) {
4520                 prev = v;
4521                 
4522             }
4523             
4524         });
4525         return prev;
4526     },
4527     
4528     indexOfNav : function()
4529     {
4530         
4531         var prev = false;
4532         Roo.each(this.navItems, function(v,i){
4533             
4534             if (v.isActive()) {
4535                 prev = i;
4536                 
4537             }
4538             
4539         });
4540         return prev;
4541     },
4542     /**
4543     * adds a Navigation item
4544     * @param {Roo.bootstrap.NavItem} the navitem to add
4545     */
4546     addItem : function(cfg)
4547     {
4548         if (this.form && Roo.bootstrap.version == 4) {
4549             cfg.tag = 'div';
4550         }
4551         var cn = new Roo.bootstrap.NavItem(cfg);
4552         this.register(cn);
4553         cn.parentId = this.id;
4554         cn.onRender(this.el, null);
4555         return cn;
4556     },
4557     /**
4558     * register a Navigation item
4559     * @param {Roo.bootstrap.NavItem} the navitem to add
4560     */
4561     register : function(item)
4562     {
4563         this.navItems.push( item);
4564         item.navId = this.navId;
4565     
4566     },
4567     
4568     /**
4569     * clear all the Navigation item
4570     */
4571    
4572     clearAll : function()
4573     {
4574         this.navItems = [];
4575         this.el.dom.innerHTML = '';
4576     },
4577     
4578     getNavItem: function(tabId)
4579     {
4580         var ret = false;
4581         Roo.each(this.navItems, function(e) {
4582             if (e.tabId == tabId) {
4583                ret =  e;
4584                return false;
4585             }
4586             return true;
4587             
4588         });
4589         return ret;
4590     },
4591     
4592     setActiveNext : function()
4593     {
4594         var i = this.indexOfNav(this.getActive());
4595         if (i > this.navItems.length) {
4596             return;
4597         }
4598         this.setActiveItem(this.navItems[i+1]);
4599     },
4600     setActivePrev : function()
4601     {
4602         var i = this.indexOfNav(this.getActive());
4603         if (i  < 1) {
4604             return;
4605         }
4606         this.setActiveItem(this.navItems[i-1]);
4607     },
4608     clearWasActive : function(except) {
4609         Roo.each(this.navItems, function(e) {
4610             if (e.tabId != except.tabId && e.was_active) {
4611                e.was_active = false;
4612                return false;
4613             }
4614             return true;
4615             
4616         });
4617     },
4618     getWasActive : function ()
4619     {
4620         var r = false;
4621         Roo.each(this.navItems, function(e) {
4622             if (e.was_active) {
4623                r = e;
4624                return false;
4625             }
4626             return true;
4627             
4628         });
4629         return r;
4630     }
4631     
4632     
4633 });
4634
4635  
4636 Roo.apply(Roo.bootstrap.NavGroup, {
4637     
4638     groups: {},
4639      /**
4640     * register a Navigation Group
4641     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4642     */
4643     register : function(navgrp)
4644     {
4645         this.groups[navgrp.navId] = navgrp;
4646         
4647     },
4648     /**
4649     * fetch a Navigation Group based on the navigation ID
4650     * @param {string} the navgroup to add
4651     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4652     */
4653     get: function(navId) {
4654         if (typeof(this.groups[navId]) == 'undefined') {
4655             return false;
4656             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4657         }
4658         return this.groups[navId] ;
4659     }
4660     
4661     
4662     
4663 });
4664
4665  /*
4666  * - LGPL
4667  *
4668  * row
4669  * 
4670  */
4671
4672 /**
4673  * @class Roo.bootstrap.NavItem
4674  * @extends Roo.bootstrap.Component
4675  * Bootstrap Navbar.NavItem class
4676  * @cfg {String} href  link to
4677  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4678
4679  * @cfg {String} html content of button
4680  * @cfg {String} badge text inside badge
4681  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4682  * @cfg {String} glyphicon DEPRICATED - use fa
4683  * @cfg {String} icon DEPRICATED - use fa
4684  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4685  * @cfg {Boolean} active Is item active
4686  * @cfg {Boolean} disabled Is item disabled
4687  
4688  * @cfg {Boolean} preventDefault (true | false) default false
4689  * @cfg {String} tabId the tab that this item activates.
4690  * @cfg {String} tagtype (a|span) render as a href or span?
4691  * @cfg {Boolean} animateRef (true|false) link to element default false  
4692   
4693  * @constructor
4694  * Create a new Navbar Item
4695  * @param {Object} config The config object
4696  */
4697 Roo.bootstrap.NavItem = function(config){
4698     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4699     this.addEvents({
4700         // raw events
4701         /**
4702          * @event click
4703          * The raw click event for the entire grid.
4704          * @param {Roo.EventObject} e
4705          */
4706         "click" : true,
4707          /**
4708             * @event changed
4709             * Fires when the active item active state changes
4710             * @param {Roo.bootstrap.NavItem} this
4711             * @param {boolean} state the new state
4712              
4713          */
4714         'changed': true,
4715         /**
4716             * @event scrollto
4717             * Fires when scroll to element
4718             * @param {Roo.bootstrap.NavItem} this
4719             * @param {Object} options
4720             * @param {Roo.EventObject} e
4721              
4722          */
4723         'scrollto': true
4724     });
4725    
4726 };
4727
4728 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4729     
4730     href: false,
4731     html: '',
4732     badge: '',
4733     icon: false,
4734     fa : false,
4735     glyphicon: false,
4736     active: false,
4737     preventDefault : false,
4738     tabId : false,
4739     tagtype : 'a',
4740     tag: 'li',
4741     disabled : false,
4742     animateRef : false,
4743     was_active : false,
4744     button_weight : '',
4745     button_outline : false,
4746     
4747     navLink: false,
4748     
4749     getAutoCreate : function(){
4750          
4751         var cfg = {
4752             tag: this.tag,
4753             cls: 'nav-item'
4754         };
4755         
4756         if (this.active) {
4757             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4758         }
4759         if (this.disabled) {
4760             cfg.cls += ' disabled';
4761         }
4762         
4763         // BS4 only?
4764         if (this.button_weight.length) {
4765             cfg.tag = this.href ? 'a' : 'button';
4766             cfg.html = this.html || '';
4767             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4768             if (this.href) {
4769                 cfg.href = this.href;
4770             }
4771             if (this.fa) {
4772                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4773             }
4774             
4775             // menu .. should add dropdown-menu class - so no need for carat..
4776             
4777             if (this.badge !== '') {
4778                  
4779                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4780             }
4781             return cfg;
4782         }
4783         
4784         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4785             cfg.cn = [
4786                 {
4787                     tag: this.tagtype,
4788                     href : this.href || "#",
4789                     html: this.html || ''
4790                 }
4791             ];
4792             if (this.tagtype == 'a') {
4793                 cfg.cn[0].cls = 'nav-link';
4794             }
4795             if (this.icon) {
4796                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4797             }
4798             if (this.fa) {
4799                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4800             }
4801             if(this.glyphicon) {
4802                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4803             }
4804             
4805             if (this.menu) {
4806                 
4807                 cfg.cn[0].html += " <span class='caret'></span>";
4808              
4809             }
4810             
4811             if (this.badge !== '') {
4812                  
4813                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4814             }
4815         }
4816         
4817         
4818         
4819         return cfg;
4820     },
4821     onRender : function(ct, position)
4822     {
4823        // Roo.log("Call onRender: " + this.xtype);
4824         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4825             this.tag = 'div';
4826         }
4827         
4828         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4829         this.navLink = this.el.select('.nav-link',true).first();
4830         return ret;
4831     },
4832       
4833     
4834     initEvents: function() 
4835     {
4836         if (typeof (this.menu) != 'undefined') {
4837             this.menu.parentType = this.xtype;
4838             this.menu.triggerEl = this.el;
4839             this.menu = this.addxtype(Roo.apply({}, this.menu));
4840         }
4841         
4842         this.el.select('a',true).on('click', this.onClick, this);
4843         
4844         if(this.tagtype == 'span'){
4845             this.el.select('span',true).on('click', this.onClick, this);
4846         }
4847        
4848         // at this point parent should be available..
4849         this.parent().register(this);
4850     },
4851     
4852     onClick : function(e)
4853     {
4854         if (e.getTarget('.dropdown-menu-item')) {
4855             // did you click on a menu itemm.... - then don't trigger onclick..
4856             return;
4857         }
4858         
4859         if(
4860                 this.preventDefault || 
4861                 this.href == '#' 
4862         ){
4863             Roo.log("NavItem - prevent Default?");
4864             e.preventDefault();
4865         }
4866         
4867         if (this.disabled) {
4868             return;
4869         }
4870         
4871         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4872         if (tg && tg.transition) {
4873             Roo.log("waiting for the transitionend");
4874             return;
4875         }
4876         
4877         
4878         
4879         //Roo.log("fire event clicked");
4880         if(this.fireEvent('click', this, e) === false){
4881             return;
4882         };
4883         
4884         if(this.tagtype == 'span'){
4885             return;
4886         }
4887         
4888         //Roo.log(this.href);
4889         var ael = this.el.select('a',true).first();
4890         //Roo.log(ael);
4891         
4892         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4893             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4894             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4895                 return; // ignore... - it's a 'hash' to another page.
4896             }
4897             Roo.log("NavItem - prevent Default?");
4898             e.preventDefault();
4899             this.scrollToElement(e);
4900         }
4901         
4902         
4903         var p =  this.parent();
4904    
4905         if (['tabs','pills'].indexOf(p.type)!==-1) {
4906             if (typeof(p.setActiveItem) !== 'undefined') {
4907                 p.setActiveItem(this);
4908             }
4909         }
4910         
4911         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4912         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4913             // remove the collapsed menu expand...
4914             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
4915         }
4916     },
4917     
4918     isActive: function () {
4919         return this.active
4920     },
4921     setActive : function(state, fire, is_was_active)
4922     {
4923         if (this.active && !state && this.navId) {
4924             this.was_active = true;
4925             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4926             if (nv) {
4927                 nv.clearWasActive(this);
4928             }
4929             
4930         }
4931         this.active = state;
4932         
4933         if (!state ) {
4934             this.el.removeClass('active');
4935             this.navLink ? this.navLink.removeClass('active') : false;
4936         } else if (!this.el.hasClass('active')) {
4937             
4938             this.el.addClass('active');
4939             if (Roo.bootstrap.version == 4 && this.navLink ) {
4940                 this.navLink.addClass('active');
4941             }
4942             
4943         }
4944         if (fire) {
4945             this.fireEvent('changed', this, state);
4946         }
4947         
4948         // show a panel if it's registered and related..
4949         
4950         if (!this.navId || !this.tabId || !state || is_was_active) {
4951             return;
4952         }
4953         
4954         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4955         if (!tg) {
4956             return;
4957         }
4958         var pan = tg.getPanelByName(this.tabId);
4959         if (!pan) {
4960             return;
4961         }
4962         // if we can not flip to new panel - go back to old nav highlight..
4963         if (false == tg.showPanel(pan)) {
4964             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4965             if (nv) {
4966                 var onav = nv.getWasActive();
4967                 if (onav) {
4968                     onav.setActive(true, false, true);
4969                 }
4970             }
4971             
4972         }
4973         
4974         
4975         
4976     },
4977      // this should not be here...
4978     setDisabled : function(state)
4979     {
4980         this.disabled = state;
4981         if (!state ) {
4982             this.el.removeClass('disabled');
4983         } else if (!this.el.hasClass('disabled')) {
4984             this.el.addClass('disabled');
4985         }
4986         
4987     },
4988     
4989     /**
4990      * Fetch the element to display the tooltip on.
4991      * @return {Roo.Element} defaults to this.el
4992      */
4993     tooltipEl : function()
4994     {
4995         return this.el.select('' + this.tagtype + '', true).first();
4996     },
4997     
4998     scrollToElement : function(e)
4999     {
5000         var c = document.body;
5001         
5002         /*
5003          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5004          */
5005         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5006             c = document.documentElement;
5007         }
5008         
5009         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5010         
5011         if(!target){
5012             return;
5013         }
5014
5015         var o = target.calcOffsetsTo(c);
5016         
5017         var options = {
5018             target : target,
5019             value : o[1]
5020         };
5021         
5022         this.fireEvent('scrollto', this, options, e);
5023         
5024         Roo.get(c).scrollTo('top', options.value, true);
5025         
5026         return;
5027     }
5028 });
5029  
5030
5031  /*
5032  * - LGPL
5033  *
5034  * sidebar item
5035  *
5036  *  li
5037  *    <span> icon </span>
5038  *    <span> text </span>
5039  *    <span>badge </span>
5040  */
5041
5042 /**
5043  * @class Roo.bootstrap.NavSidebarItem
5044  * @extends Roo.bootstrap.NavItem
5045  * Bootstrap Navbar.NavSidebarItem class
5046  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5047  * {Boolean} open is the menu open
5048  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5049  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5050  * {String} buttonSize (sm|md|lg)the extra classes for the button
5051  * {Boolean} showArrow show arrow next to the text (default true)
5052  * @constructor
5053  * Create a new Navbar Button
5054  * @param {Object} config The config object
5055  */
5056 Roo.bootstrap.NavSidebarItem = function(config){
5057     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5058     this.addEvents({
5059         // raw events
5060         /**
5061          * @event click
5062          * The raw click event for the entire grid.
5063          * @param {Roo.EventObject} e
5064          */
5065         "click" : true,
5066          /**
5067             * @event changed
5068             * Fires when the active item active state changes
5069             * @param {Roo.bootstrap.NavSidebarItem} this
5070             * @param {boolean} state the new state
5071              
5072          */
5073         'changed': true
5074     });
5075    
5076 };
5077
5078 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5079     
5080     badgeWeight : 'default',
5081     
5082     open: false,
5083     
5084     buttonView : false,
5085     
5086     buttonWeight : 'default',
5087     
5088     buttonSize : 'md',
5089     
5090     showArrow : true,
5091     
5092     getAutoCreate : function(){
5093         
5094         
5095         var a = {
5096                 tag: 'a',
5097                 href : this.href || '#',
5098                 cls: '',
5099                 html : '',
5100                 cn : []
5101         };
5102         
5103         if(this.buttonView){
5104             a = {
5105                 tag: 'button',
5106                 href : this.href || '#',
5107                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5108                 html : this.html,
5109                 cn : []
5110             };
5111         }
5112         
5113         var cfg = {
5114             tag: 'li',
5115             cls: '',
5116             cn: [ a ]
5117         };
5118         
5119         if (this.active) {
5120             cfg.cls += ' active';
5121         }
5122         
5123         if (this.disabled) {
5124             cfg.cls += ' disabled';
5125         }
5126         if (this.open) {
5127             cfg.cls += ' open x-open';
5128         }
5129         // left icon..
5130         if (this.glyphicon || this.icon) {
5131             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5132             a.cn.push({ tag : 'i', cls : c }) ;
5133         }
5134         
5135         if(!this.buttonView){
5136             var span = {
5137                 tag: 'span',
5138                 html : this.html || ''
5139             };
5140
5141             a.cn.push(span);
5142             
5143         }
5144         
5145         if (this.badge !== '') {
5146             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5147         }
5148         
5149         if (this.menu) {
5150             
5151             if(this.showArrow){
5152                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5153             }
5154             
5155             a.cls += ' dropdown-toggle treeview' ;
5156         }
5157         
5158         return cfg;
5159     },
5160     
5161     initEvents : function()
5162     { 
5163         if (typeof (this.menu) != 'undefined') {
5164             this.menu.parentType = this.xtype;
5165             this.menu.triggerEl = this.el;
5166             this.menu = this.addxtype(Roo.apply({}, this.menu));
5167         }
5168         
5169         this.el.on('click', this.onClick, this);
5170         
5171         if(this.badge !== ''){
5172             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5173         }
5174         
5175     },
5176     
5177     onClick : function(e)
5178     {
5179         if(this.disabled){
5180             e.preventDefault();
5181             return;
5182         }
5183         
5184         if(this.preventDefault){
5185             e.preventDefault();
5186         }
5187         
5188         this.fireEvent('click', this, e);
5189     },
5190     
5191     disable : function()
5192     {
5193         this.setDisabled(true);
5194     },
5195     
5196     enable : function()
5197     {
5198         this.setDisabled(false);
5199     },
5200     
5201     setDisabled : function(state)
5202     {
5203         if(this.disabled == state){
5204             return;
5205         }
5206         
5207         this.disabled = state;
5208         
5209         if (state) {
5210             this.el.addClass('disabled');
5211             return;
5212         }
5213         
5214         this.el.removeClass('disabled');
5215         
5216         return;
5217     },
5218     
5219     setActive : function(state)
5220     {
5221         if(this.active == state){
5222             return;
5223         }
5224         
5225         this.active = state;
5226         
5227         if (state) {
5228             this.el.addClass('active');
5229             return;
5230         }
5231         
5232         this.el.removeClass('active');
5233         
5234         return;
5235     },
5236     
5237     isActive: function () 
5238     {
5239         return this.active;
5240     },
5241     
5242     setBadge : function(str)
5243     {
5244         if(!this.badgeEl){
5245             return;
5246         }
5247         
5248         this.badgeEl.dom.innerHTML = str;
5249     }
5250     
5251    
5252      
5253  
5254 });
5255  
5256
5257  /*
5258  * - LGPL
5259  *
5260  * row
5261  * 
5262  */
5263
5264 /**
5265  * @class Roo.bootstrap.Row
5266  * @extends Roo.bootstrap.Component
5267  * Bootstrap Row class (contains columns...)
5268  * 
5269  * @constructor
5270  * Create a new Row
5271  * @param {Object} config The config object
5272  */
5273
5274 Roo.bootstrap.Row = function(config){
5275     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5276 };
5277
5278 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5279     
5280     getAutoCreate : function(){
5281        return {
5282             cls: 'row clearfix'
5283        };
5284     }
5285     
5286     
5287 });
5288
5289  
5290
5291  /*
5292  * - LGPL
5293  *
5294  * element
5295  * 
5296  */
5297
5298 /**
5299  * @class Roo.bootstrap.Element
5300  * @extends Roo.bootstrap.Component
5301  * Bootstrap Element class
5302  * @cfg {String} html contents of the element
5303  * @cfg {String} tag tag of the element
5304  * @cfg {String} cls class of the element
5305  * @cfg {Boolean} preventDefault (true|false) default false
5306  * @cfg {Boolean} clickable (true|false) default false
5307  * 
5308  * @constructor
5309  * Create a new Element
5310  * @param {Object} config The config object
5311  */
5312
5313 Roo.bootstrap.Element = function(config){
5314     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5315     
5316     this.addEvents({
5317         // raw events
5318         /**
5319          * @event click
5320          * When a element is chick
5321          * @param {Roo.bootstrap.Element} this
5322          * @param {Roo.EventObject} e
5323          */
5324         "click" : true
5325     });
5326 };
5327
5328 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5329     
5330     tag: 'div',
5331     cls: '',
5332     html: '',
5333     preventDefault: false, 
5334     clickable: false,
5335     
5336     getAutoCreate : function(){
5337         
5338         var cfg = {
5339             tag: this.tag,
5340             // cls: this.cls, double assign in parent class Component.js :: onRender
5341             html: this.html
5342         };
5343         
5344         return cfg;
5345     },
5346     
5347     initEvents: function() 
5348     {
5349         Roo.bootstrap.Element.superclass.initEvents.call(this);
5350         
5351         if(this.clickable){
5352             this.el.on('click', this.onClick, this);
5353         }
5354         
5355     },
5356     
5357     onClick : function(e)
5358     {
5359         if(this.preventDefault){
5360             e.preventDefault();
5361         }
5362         
5363         this.fireEvent('click', this, e);
5364     },
5365     
5366     getValue : function()
5367     {
5368         return this.el.dom.innerHTML;
5369     },
5370     
5371     setValue : function(value)
5372     {
5373         this.el.dom.innerHTML = value;
5374     }
5375    
5376 });
5377
5378  
5379
5380  /*
5381  * - LGPL
5382  *
5383  * pagination
5384  * 
5385  */
5386
5387 /**
5388  * @class Roo.bootstrap.Pagination
5389  * @extends Roo.bootstrap.Component
5390  * Bootstrap Pagination class
5391  * @cfg {String} size xs | sm | md | lg
5392  * @cfg {Boolean} inverse false | true
5393  * 
5394  * @constructor
5395  * Create a new Pagination
5396  * @param {Object} config The config object
5397  */
5398
5399 Roo.bootstrap.Pagination = function(config){
5400     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5401 };
5402
5403 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5404     
5405     cls: false,
5406     size: false,
5407     inverse: false,
5408     
5409     getAutoCreate : function(){
5410         var cfg = {
5411             tag: 'ul',
5412                 cls: 'pagination'
5413         };
5414         if (this.inverse) {
5415             cfg.cls += ' inverse';
5416         }
5417         if (this.html) {
5418             cfg.html=this.html;
5419         }
5420         if (this.cls) {
5421             cfg.cls += " " + this.cls;
5422         }
5423         return cfg;
5424     }
5425    
5426 });
5427
5428  
5429
5430  /*
5431  * - LGPL
5432  *
5433  * Pagination item
5434  * 
5435  */
5436
5437
5438 /**
5439  * @class Roo.bootstrap.PaginationItem
5440  * @extends Roo.bootstrap.Component
5441  * Bootstrap PaginationItem class
5442  * @cfg {String} html text
5443  * @cfg {String} href the link
5444  * @cfg {Boolean} preventDefault (true | false) default true
5445  * @cfg {Boolean} active (true | false) default false
5446  * @cfg {Boolean} disabled default false
5447  * 
5448  * 
5449  * @constructor
5450  * Create a new PaginationItem
5451  * @param {Object} config The config object
5452  */
5453
5454
5455 Roo.bootstrap.PaginationItem = function(config){
5456     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5457     this.addEvents({
5458         // raw events
5459         /**
5460          * @event click
5461          * The raw click event for the entire grid.
5462          * @param {Roo.EventObject} e
5463          */
5464         "click" : true
5465     });
5466 };
5467
5468 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5469     
5470     href : false,
5471     html : false,
5472     preventDefault: true,
5473     active : false,
5474     cls : false,
5475     disabled: false,
5476     
5477     getAutoCreate : function(){
5478         var cfg= {
5479             tag: 'li',
5480             cn: [
5481                 {
5482                     tag : 'a',
5483                     href : this.href ? this.href : '#',
5484                     html : this.html ? this.html : ''
5485                 }
5486             ]
5487         };
5488         
5489         if(this.cls){
5490             cfg.cls = this.cls;
5491         }
5492         
5493         if(this.disabled){
5494             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5495         }
5496         
5497         if(this.active){
5498             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5499         }
5500         
5501         return cfg;
5502     },
5503     
5504     initEvents: function() {
5505         
5506         this.el.on('click', this.onClick, this);
5507         
5508     },
5509     onClick : function(e)
5510     {
5511         Roo.log('PaginationItem on click ');
5512         if(this.preventDefault){
5513             e.preventDefault();
5514         }
5515         
5516         if(this.disabled){
5517             return;
5518         }
5519         
5520         this.fireEvent('click', this, e);
5521     }
5522    
5523 });
5524
5525  
5526
5527  /*
5528  * - LGPL
5529  *
5530  * slider
5531  * 
5532  */
5533
5534
5535 /**
5536  * @class Roo.bootstrap.Slider
5537  * @extends Roo.bootstrap.Component
5538  * Bootstrap Slider class
5539  *    
5540  * @constructor
5541  * Create a new Slider
5542  * @param {Object} config The config object
5543  */
5544
5545 Roo.bootstrap.Slider = function(config){
5546     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5547 };
5548
5549 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5550     
5551     getAutoCreate : function(){
5552         
5553         var cfg = {
5554             tag: 'div',
5555             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5556             cn: [
5557                 {
5558                     tag: 'a',
5559                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5560                 }
5561             ]
5562         };
5563         
5564         return cfg;
5565     }
5566    
5567 });
5568
5569  /*
5570  * Based on:
5571  * Ext JS Library 1.1.1
5572  * Copyright(c) 2006-2007, Ext JS, LLC.
5573  *
5574  * Originally Released Under LGPL - original licence link has changed is not relivant.
5575  *
5576  * Fork - LGPL
5577  * <script type="text/javascript">
5578  */
5579  
5580
5581 /**
5582  * @class Roo.grid.ColumnModel
5583  * @extends Roo.util.Observable
5584  * This is the default implementation of a ColumnModel used by the Grid. It defines
5585  * the columns in the grid.
5586  * <br>Usage:<br>
5587  <pre><code>
5588  var colModel = new Roo.grid.ColumnModel([
5589         {header: "Ticker", width: 60, sortable: true, locked: true},
5590         {header: "Company Name", width: 150, sortable: true},
5591         {header: "Market Cap.", width: 100, sortable: true},
5592         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5593         {header: "Employees", width: 100, sortable: true, resizable: false}
5594  ]);
5595  </code></pre>
5596  * <p>
5597  
5598  * The config options listed for this class are options which may appear in each
5599  * individual column definition.
5600  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5601  * @constructor
5602  * @param {Object} config An Array of column config objects. See this class's
5603  * config objects for details.
5604 */
5605 Roo.grid.ColumnModel = function(config){
5606         /**
5607      * The config passed into the constructor
5608      */
5609     this.config = config;
5610     this.lookup = {};
5611
5612     // if no id, create one
5613     // if the column does not have a dataIndex mapping,
5614     // map it to the order it is in the config
5615     for(var i = 0, len = config.length; i < len; i++){
5616         var c = config[i];
5617         if(typeof c.dataIndex == "undefined"){
5618             c.dataIndex = i;
5619         }
5620         if(typeof c.renderer == "string"){
5621             c.renderer = Roo.util.Format[c.renderer];
5622         }
5623         if(typeof c.id == "undefined"){
5624             c.id = Roo.id();
5625         }
5626         if(c.editor && c.editor.xtype){
5627             c.editor  = Roo.factory(c.editor, Roo.grid);
5628         }
5629         if(c.editor && c.editor.isFormField){
5630             c.editor = new Roo.grid.GridEditor(c.editor);
5631         }
5632         this.lookup[c.id] = c;
5633     }
5634
5635     /**
5636      * The width of columns which have no width specified (defaults to 100)
5637      * @type Number
5638      */
5639     this.defaultWidth = 100;
5640
5641     /**
5642      * Default sortable of columns which have no sortable specified (defaults to false)
5643      * @type Boolean
5644      */
5645     this.defaultSortable = false;
5646
5647     this.addEvents({
5648         /**
5649              * @event widthchange
5650              * Fires when the width of a column changes.
5651              * @param {ColumnModel} this
5652              * @param {Number} columnIndex The column index
5653              * @param {Number} newWidth The new width
5654              */
5655             "widthchange": true,
5656         /**
5657              * @event headerchange
5658              * Fires when the text of a header changes.
5659              * @param {ColumnModel} this
5660              * @param {Number} columnIndex The column index
5661              * @param {Number} newText The new header text
5662              */
5663             "headerchange": true,
5664         /**
5665              * @event hiddenchange
5666              * Fires when a column is hidden or "unhidden".
5667              * @param {ColumnModel} this
5668              * @param {Number} columnIndex The column index
5669              * @param {Boolean} hidden true if hidden, false otherwise
5670              */
5671             "hiddenchange": true,
5672             /**
5673          * @event columnmoved
5674          * Fires when a column is moved.
5675          * @param {ColumnModel} this
5676          * @param {Number} oldIndex
5677          * @param {Number} newIndex
5678          */
5679         "columnmoved" : true,
5680         /**
5681          * @event columlockchange
5682          * Fires when a column's locked state is changed
5683          * @param {ColumnModel} this
5684          * @param {Number} colIndex
5685          * @param {Boolean} locked true if locked
5686          */
5687         "columnlockchange" : true
5688     });
5689     Roo.grid.ColumnModel.superclass.constructor.call(this);
5690 };
5691 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5692     /**
5693      * @cfg {String} header The header text to display in the Grid view.
5694      */
5695     /**
5696      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5697      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5698      * specified, the column's index is used as an index into the Record's data Array.
5699      */
5700     /**
5701      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5702      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5703      */
5704     /**
5705      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5706      * Defaults to the value of the {@link #defaultSortable} property.
5707      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5708      */
5709     /**
5710      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5711      */
5712     /**
5713      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5714      */
5715     /**
5716      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5717      */
5718     /**
5719      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5720      */
5721     /**
5722      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5723      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5724      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5725      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5726      */
5727        /**
5728      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5729      */
5730     /**
5731      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5732      */
5733     /**
5734      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5735      */
5736     /**
5737      * @cfg {String} cursor (Optional)
5738      */
5739     /**
5740      * @cfg {String} tooltip (Optional)
5741      */
5742     /**
5743      * @cfg {Number} xs (Optional)
5744      */
5745     /**
5746      * @cfg {Number} sm (Optional)
5747      */
5748     /**
5749      * @cfg {Number} md (Optional)
5750      */
5751     /**
5752      * @cfg {Number} lg (Optional)
5753      */
5754     /**
5755      * Returns the id of the column at the specified index.
5756      * @param {Number} index The column index
5757      * @return {String} the id
5758      */
5759     getColumnId : function(index){
5760         return this.config[index].id;
5761     },
5762
5763     /**
5764      * Returns the column for a specified id.
5765      * @param {String} id The column id
5766      * @return {Object} the column
5767      */
5768     getColumnById : function(id){
5769         return this.lookup[id];
5770     },
5771
5772     
5773     /**
5774      * Returns the column for a specified dataIndex.
5775      * @param {String} dataIndex The column dataIndex
5776      * @return {Object|Boolean} the column or false if not found
5777      */
5778     getColumnByDataIndex: function(dataIndex){
5779         var index = this.findColumnIndex(dataIndex);
5780         return index > -1 ? this.config[index] : false;
5781     },
5782     
5783     /**
5784      * Returns the index for a specified column id.
5785      * @param {String} id The column id
5786      * @return {Number} the index, or -1 if not found
5787      */
5788     getIndexById : function(id){
5789         for(var i = 0, len = this.config.length; i < len; i++){
5790             if(this.config[i].id == id){
5791                 return i;
5792             }
5793         }
5794         return -1;
5795     },
5796     
5797     /**
5798      * Returns the index for a specified column dataIndex.
5799      * @param {String} dataIndex The column dataIndex
5800      * @return {Number} the index, or -1 if not found
5801      */
5802     
5803     findColumnIndex : function(dataIndex){
5804         for(var i = 0, len = this.config.length; i < len; i++){
5805             if(this.config[i].dataIndex == dataIndex){
5806                 return i;
5807             }
5808         }
5809         return -1;
5810     },
5811     
5812     
5813     moveColumn : function(oldIndex, newIndex){
5814         var c = this.config[oldIndex];
5815         this.config.splice(oldIndex, 1);
5816         this.config.splice(newIndex, 0, c);
5817         this.dataMap = null;
5818         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5819     },
5820
5821     isLocked : function(colIndex){
5822         return this.config[colIndex].locked === true;
5823     },
5824
5825     setLocked : function(colIndex, value, suppressEvent){
5826         if(this.isLocked(colIndex) == value){
5827             return;
5828         }
5829         this.config[colIndex].locked = value;
5830         if(!suppressEvent){
5831             this.fireEvent("columnlockchange", this, colIndex, value);
5832         }
5833     },
5834
5835     getTotalLockedWidth : function(){
5836         var totalWidth = 0;
5837         for(var i = 0; i < this.config.length; i++){
5838             if(this.isLocked(i) && !this.isHidden(i)){
5839                 this.totalWidth += this.getColumnWidth(i);
5840             }
5841         }
5842         return totalWidth;
5843     },
5844
5845     getLockedCount : function(){
5846         for(var i = 0, len = this.config.length; i < len; i++){
5847             if(!this.isLocked(i)){
5848                 return i;
5849             }
5850         }
5851         
5852         return this.config.length;
5853     },
5854
5855     /**
5856      * Returns the number of columns.
5857      * @return {Number}
5858      */
5859     getColumnCount : function(visibleOnly){
5860         if(visibleOnly === true){
5861             var c = 0;
5862             for(var i = 0, len = this.config.length; i < len; i++){
5863                 if(!this.isHidden(i)){
5864                     c++;
5865                 }
5866             }
5867             return c;
5868         }
5869         return this.config.length;
5870     },
5871
5872     /**
5873      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5874      * @param {Function} fn
5875      * @param {Object} scope (optional)
5876      * @return {Array} result
5877      */
5878     getColumnsBy : function(fn, scope){
5879         var r = [];
5880         for(var i = 0, len = this.config.length; i < len; i++){
5881             var c = this.config[i];
5882             if(fn.call(scope||this, c, i) === true){
5883                 r[r.length] = c;
5884             }
5885         }
5886         return r;
5887     },
5888
5889     /**
5890      * Returns true if the specified column is sortable.
5891      * @param {Number} col The column index
5892      * @return {Boolean}
5893      */
5894     isSortable : function(col){
5895         if(typeof this.config[col].sortable == "undefined"){
5896             return this.defaultSortable;
5897         }
5898         return this.config[col].sortable;
5899     },
5900
5901     /**
5902      * Returns the rendering (formatting) function defined for the column.
5903      * @param {Number} col The column index.
5904      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5905      */
5906     getRenderer : function(col){
5907         if(!this.config[col].renderer){
5908             return Roo.grid.ColumnModel.defaultRenderer;
5909         }
5910         return this.config[col].renderer;
5911     },
5912
5913     /**
5914      * Sets the rendering (formatting) function for a column.
5915      * @param {Number} col The column index
5916      * @param {Function} fn The function to use to process the cell's raw data
5917      * to return HTML markup for the grid view. The render function is called with
5918      * the following parameters:<ul>
5919      * <li>Data value.</li>
5920      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5921      * <li>css A CSS style string to apply to the table cell.</li>
5922      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5923      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5924      * <li>Row index</li>
5925      * <li>Column index</li>
5926      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5927      */
5928     setRenderer : function(col, fn){
5929         this.config[col].renderer = fn;
5930     },
5931
5932     /**
5933      * Returns the width for the specified column.
5934      * @param {Number} col The column index
5935      * @return {Number}
5936      */
5937     getColumnWidth : function(col){
5938         return this.config[col].width * 1 || this.defaultWidth;
5939     },
5940
5941     /**
5942      * Sets the width for a column.
5943      * @param {Number} col The column index
5944      * @param {Number} width The new width
5945      */
5946     setColumnWidth : function(col, width, suppressEvent){
5947         this.config[col].width = width;
5948         this.totalWidth = null;
5949         if(!suppressEvent){
5950              this.fireEvent("widthchange", this, col, width);
5951         }
5952     },
5953
5954     /**
5955      * Returns the total width of all columns.
5956      * @param {Boolean} includeHidden True to include hidden column widths
5957      * @return {Number}
5958      */
5959     getTotalWidth : function(includeHidden){
5960         if(!this.totalWidth){
5961             this.totalWidth = 0;
5962             for(var i = 0, len = this.config.length; i < len; i++){
5963                 if(includeHidden || !this.isHidden(i)){
5964                     this.totalWidth += this.getColumnWidth(i);
5965                 }
5966             }
5967         }
5968         return this.totalWidth;
5969     },
5970
5971     /**
5972      * Returns the header for the specified column.
5973      * @param {Number} col The column index
5974      * @return {String}
5975      */
5976     getColumnHeader : function(col){
5977         return this.config[col].header;
5978     },
5979
5980     /**
5981      * Sets the header for a column.
5982      * @param {Number} col The column index
5983      * @param {String} header The new header
5984      */
5985     setColumnHeader : function(col, header){
5986         this.config[col].header = header;
5987         this.fireEvent("headerchange", this, col, header);
5988     },
5989
5990     /**
5991      * Returns the tooltip for the specified column.
5992      * @param {Number} col The column index
5993      * @return {String}
5994      */
5995     getColumnTooltip : function(col){
5996             return this.config[col].tooltip;
5997     },
5998     /**
5999      * Sets the tooltip for a column.
6000      * @param {Number} col The column index
6001      * @param {String} tooltip The new tooltip
6002      */
6003     setColumnTooltip : function(col, tooltip){
6004             this.config[col].tooltip = tooltip;
6005     },
6006
6007     /**
6008      * Returns the dataIndex for the specified column.
6009      * @param {Number} col The column index
6010      * @return {Number}
6011      */
6012     getDataIndex : function(col){
6013         return this.config[col].dataIndex;
6014     },
6015
6016     /**
6017      * Sets the dataIndex for a column.
6018      * @param {Number} col The column index
6019      * @param {Number} dataIndex The new dataIndex
6020      */
6021     setDataIndex : function(col, dataIndex){
6022         this.config[col].dataIndex = dataIndex;
6023     },
6024
6025     
6026     
6027     /**
6028      * Returns true if the cell is editable.
6029      * @param {Number} colIndex The column index
6030      * @param {Number} rowIndex The row index - this is nto actually used..?
6031      * @return {Boolean}
6032      */
6033     isCellEditable : function(colIndex, rowIndex){
6034         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6035     },
6036
6037     /**
6038      * Returns the editor defined for the cell/column.
6039      * return false or null to disable editing.
6040      * @param {Number} colIndex The column index
6041      * @param {Number} rowIndex The row index
6042      * @return {Object}
6043      */
6044     getCellEditor : function(colIndex, rowIndex){
6045         return this.config[colIndex].editor;
6046     },
6047
6048     /**
6049      * Sets if a column is editable.
6050      * @param {Number} col The column index
6051      * @param {Boolean} editable True if the column is editable
6052      */
6053     setEditable : function(col, editable){
6054         this.config[col].editable = editable;
6055     },
6056
6057
6058     /**
6059      * Returns true if the column is hidden.
6060      * @param {Number} colIndex The column index
6061      * @return {Boolean}
6062      */
6063     isHidden : function(colIndex){
6064         return this.config[colIndex].hidden;
6065     },
6066
6067
6068     /**
6069      * Returns true if the column width cannot be changed
6070      */
6071     isFixed : function(colIndex){
6072         return this.config[colIndex].fixed;
6073     },
6074
6075     /**
6076      * Returns true if the column can be resized
6077      * @return {Boolean}
6078      */
6079     isResizable : function(colIndex){
6080         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6081     },
6082     /**
6083      * Sets if a column is hidden.
6084      * @param {Number} colIndex The column index
6085      * @param {Boolean} hidden True if the column is hidden
6086      */
6087     setHidden : function(colIndex, hidden){
6088         this.config[colIndex].hidden = hidden;
6089         this.totalWidth = null;
6090         this.fireEvent("hiddenchange", this, colIndex, hidden);
6091     },
6092
6093     /**
6094      * Sets the editor for a column.
6095      * @param {Number} col The column index
6096      * @param {Object} editor The editor object
6097      */
6098     setEditor : function(col, editor){
6099         this.config[col].editor = editor;
6100     }
6101 });
6102
6103 Roo.grid.ColumnModel.defaultRenderer = function(value)
6104 {
6105     if(typeof value == "object") {
6106         return value;
6107     }
6108         if(typeof value == "string" && value.length < 1){
6109             return "&#160;";
6110         }
6111     
6112         return String.format("{0}", value);
6113 };
6114
6115 // Alias for backwards compatibility
6116 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6117 /*
6118  * Based on:
6119  * Ext JS Library 1.1.1
6120  * Copyright(c) 2006-2007, Ext JS, LLC.
6121  *
6122  * Originally Released Under LGPL - original licence link has changed is not relivant.
6123  *
6124  * Fork - LGPL
6125  * <script type="text/javascript">
6126  */
6127  
6128 /**
6129  * @class Roo.LoadMask
6130  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6131  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6132  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6133  * element's UpdateManager load indicator and will be destroyed after the initial load.
6134  * @constructor
6135  * Create a new LoadMask
6136  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6137  * @param {Object} config The config object
6138  */
6139 Roo.LoadMask = function(el, config){
6140     this.el = Roo.get(el);
6141     Roo.apply(this, config);
6142     if(this.store){
6143         this.store.on('beforeload', this.onBeforeLoad, this);
6144         this.store.on('load', this.onLoad, this);
6145         this.store.on('loadexception', this.onLoadException, this);
6146         this.removeMask = false;
6147     }else{
6148         var um = this.el.getUpdateManager();
6149         um.showLoadIndicator = false; // disable the default indicator
6150         um.on('beforeupdate', this.onBeforeLoad, this);
6151         um.on('update', this.onLoad, this);
6152         um.on('failure', this.onLoad, this);
6153         this.removeMask = true;
6154     }
6155 };
6156
6157 Roo.LoadMask.prototype = {
6158     /**
6159      * @cfg {Boolean} removeMask
6160      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6161      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6162      */
6163     /**
6164      * @cfg {String} msg
6165      * The text to display in a centered loading message box (defaults to 'Loading...')
6166      */
6167     msg : 'Loading...',
6168     /**
6169      * @cfg {String} msgCls
6170      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6171      */
6172     msgCls : 'x-mask-loading',
6173
6174     /**
6175      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6176      * @type Boolean
6177      */
6178     disabled: false,
6179
6180     /**
6181      * Disables the mask to prevent it from being displayed
6182      */
6183     disable : function(){
6184        this.disabled = true;
6185     },
6186
6187     /**
6188      * Enables the mask so that it can be displayed
6189      */
6190     enable : function(){
6191         this.disabled = false;
6192     },
6193     
6194     onLoadException : function()
6195     {
6196         Roo.log(arguments);
6197         
6198         if (typeof(arguments[3]) != 'undefined') {
6199             Roo.MessageBox.alert("Error loading",arguments[3]);
6200         } 
6201         /*
6202         try {
6203             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6204                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6205             }   
6206         } catch(e) {
6207             
6208         }
6209         */
6210     
6211         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6212     },
6213     // private
6214     onLoad : function()
6215     {
6216         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6217     },
6218
6219     // private
6220     onBeforeLoad : function(){
6221         if(!this.disabled){
6222             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6223         }
6224     },
6225
6226     // private
6227     destroy : function(){
6228         if(this.store){
6229             this.store.un('beforeload', this.onBeforeLoad, this);
6230             this.store.un('load', this.onLoad, this);
6231             this.store.un('loadexception', this.onLoadException, this);
6232         }else{
6233             var um = this.el.getUpdateManager();
6234             um.un('beforeupdate', this.onBeforeLoad, this);
6235             um.un('update', this.onLoad, this);
6236             um.un('failure', this.onLoad, this);
6237         }
6238     }
6239 };/*
6240  * - LGPL
6241  *
6242  * table
6243  * 
6244  */
6245
6246 /**
6247  * @class Roo.bootstrap.Table
6248  * @extends Roo.bootstrap.Component
6249  * Bootstrap Table class
6250  * @cfg {String} cls table class
6251  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6252  * @cfg {String} bgcolor Specifies the background color for a table
6253  * @cfg {Number} border Specifies whether the table cells should have borders or not
6254  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6255  * @cfg {Number} cellspacing Specifies the space between cells
6256  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6257  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6258  * @cfg {String} sortable Specifies that the table should be sortable
6259  * @cfg {String} summary Specifies a summary of the content of a table
6260  * @cfg {Number} width Specifies the width of a table
6261  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6262  * 
6263  * @cfg {boolean} striped Should the rows be alternative striped
6264  * @cfg {boolean} bordered Add borders to the table
6265  * @cfg {boolean} hover Add hover highlighting
6266  * @cfg {boolean} condensed Format condensed
6267  * @cfg {boolean} responsive Format condensed
6268  * @cfg {Boolean} loadMask (true|false) default false
6269  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6270  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6271  * @cfg {Boolean} rowSelection (true|false) default false
6272  * @cfg {Boolean} cellSelection (true|false) default false
6273  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6274  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6275  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6276  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6277  
6278  * 
6279  * @constructor
6280  * Create a new Table
6281  * @param {Object} config The config object
6282  */
6283
6284 Roo.bootstrap.Table = function(config){
6285     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6286     
6287   
6288     
6289     // BC...
6290     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6291     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6292     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6293     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6294     
6295     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6296     if (this.sm) {
6297         this.sm.grid = this;
6298         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6299         this.sm = this.selModel;
6300         this.sm.xmodule = this.xmodule || false;
6301     }
6302     
6303     if (this.cm && typeof(this.cm.config) == 'undefined') {
6304         this.colModel = new Roo.grid.ColumnModel(this.cm);
6305         this.cm = this.colModel;
6306         this.cm.xmodule = this.xmodule || false;
6307     }
6308     if (this.store) {
6309         this.store= Roo.factory(this.store, Roo.data);
6310         this.ds = this.store;
6311         this.ds.xmodule = this.xmodule || false;
6312          
6313     }
6314     if (this.footer && this.store) {
6315         this.footer.dataSource = this.ds;
6316         this.footer = Roo.factory(this.footer);
6317     }
6318     
6319     /** @private */
6320     this.addEvents({
6321         /**
6322          * @event cellclick
6323          * Fires when a cell is clicked
6324          * @param {Roo.bootstrap.Table} this
6325          * @param {Roo.Element} el
6326          * @param {Number} rowIndex
6327          * @param {Number} columnIndex
6328          * @param {Roo.EventObject} e
6329          */
6330         "cellclick" : true,
6331         /**
6332          * @event celldblclick
6333          * Fires when a cell is double clicked
6334          * @param {Roo.bootstrap.Table} this
6335          * @param {Roo.Element} el
6336          * @param {Number} rowIndex
6337          * @param {Number} columnIndex
6338          * @param {Roo.EventObject} e
6339          */
6340         "celldblclick" : true,
6341         /**
6342          * @event rowclick
6343          * Fires when a row is clicked
6344          * @param {Roo.bootstrap.Table} this
6345          * @param {Roo.Element} el
6346          * @param {Number} rowIndex
6347          * @param {Roo.EventObject} e
6348          */
6349         "rowclick" : true,
6350         /**
6351          * @event rowdblclick
6352          * Fires when a row is double clicked
6353          * @param {Roo.bootstrap.Table} this
6354          * @param {Roo.Element} el
6355          * @param {Number} rowIndex
6356          * @param {Roo.EventObject} e
6357          */
6358         "rowdblclick" : true,
6359         /**
6360          * @event mouseover
6361          * Fires when a mouseover occur
6362          * @param {Roo.bootstrap.Table} this
6363          * @param {Roo.Element} el
6364          * @param {Number} rowIndex
6365          * @param {Number} columnIndex
6366          * @param {Roo.EventObject} e
6367          */
6368         "mouseover" : true,
6369         /**
6370          * @event mouseout
6371          * Fires when a mouseout occur
6372          * @param {Roo.bootstrap.Table} this
6373          * @param {Roo.Element} el
6374          * @param {Number} rowIndex
6375          * @param {Number} columnIndex
6376          * @param {Roo.EventObject} e
6377          */
6378         "mouseout" : true,
6379         /**
6380          * @event rowclass
6381          * Fires when a row is rendered, so you can change add a style to it.
6382          * @param {Roo.bootstrap.Table} this
6383          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6384          */
6385         'rowclass' : true,
6386           /**
6387          * @event rowsrendered
6388          * Fires when all the  rows have been rendered
6389          * @param {Roo.bootstrap.Table} this
6390          */
6391         'rowsrendered' : true,
6392         /**
6393          * @event contextmenu
6394          * The raw contextmenu event for the entire grid.
6395          * @param {Roo.EventObject} e
6396          */
6397         "contextmenu" : true,
6398         /**
6399          * @event rowcontextmenu
6400          * Fires when a row is right clicked
6401          * @param {Roo.bootstrap.Table} this
6402          * @param {Number} rowIndex
6403          * @param {Roo.EventObject} e
6404          */
6405         "rowcontextmenu" : true,
6406         /**
6407          * @event cellcontextmenu
6408          * Fires when a cell is right clicked
6409          * @param {Roo.bootstrap.Table} this
6410          * @param {Number} rowIndex
6411          * @param {Number} cellIndex
6412          * @param {Roo.EventObject} e
6413          */
6414          "cellcontextmenu" : true,
6415          /**
6416          * @event headercontextmenu
6417          * Fires when a header is right clicked
6418          * @param {Roo.bootstrap.Table} this
6419          * @param {Number} columnIndex
6420          * @param {Roo.EventObject} e
6421          */
6422         "headercontextmenu" : true
6423     });
6424 };
6425
6426 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6427     
6428     cls: false,
6429     align: false,
6430     bgcolor: false,
6431     border: false,
6432     cellpadding: false,
6433     cellspacing: false,
6434     frame: false,
6435     rules: false,
6436     sortable: false,
6437     summary: false,
6438     width: false,
6439     striped : false,
6440     scrollBody : false,
6441     bordered: false,
6442     hover:  false,
6443     condensed : false,
6444     responsive : false,
6445     sm : false,
6446     cm : false,
6447     store : false,
6448     loadMask : false,
6449     footerShow : true,
6450     headerShow : true,
6451   
6452     rowSelection : false,
6453     cellSelection : false,
6454     layout : false,
6455     
6456     // Roo.Element - the tbody
6457     mainBody: false,
6458     // Roo.Element - thead element
6459     mainHead: false,
6460     
6461     container: false, // used by gridpanel...
6462     
6463     lazyLoad : false,
6464     
6465     CSS : Roo.util.CSS,
6466     
6467     auto_hide_footer : false,
6468     
6469     getAutoCreate : function()
6470     {
6471         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6472         
6473         cfg = {
6474             tag: 'table',
6475             cls : 'table',
6476             cn : []
6477         };
6478         if (this.scrollBody) {
6479             cfg.cls += ' table-body-fixed';
6480         }    
6481         if (this.striped) {
6482             cfg.cls += ' table-striped';
6483         }
6484         
6485         if (this.hover) {
6486             cfg.cls += ' table-hover';
6487         }
6488         if (this.bordered) {
6489             cfg.cls += ' table-bordered';
6490         }
6491         if (this.condensed) {
6492             cfg.cls += ' table-condensed';
6493         }
6494         if (this.responsive) {
6495             cfg.cls += ' table-responsive';
6496         }
6497         
6498         if (this.cls) {
6499             cfg.cls+=  ' ' +this.cls;
6500         }
6501         
6502         // this lot should be simplifed...
6503         var _t = this;
6504         var cp = [
6505             'align',
6506             'bgcolor',
6507             'border',
6508             'cellpadding',
6509             'cellspacing',
6510             'frame',
6511             'rules',
6512             'sortable',
6513             'summary',
6514             'width'
6515         ].forEach(function(k) {
6516             if (_t[k]) {
6517                 cfg[k] = _t[k];
6518             }
6519         });
6520         
6521         
6522         if (this.layout) {
6523             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6524         }
6525         
6526         if(this.store || this.cm){
6527             if(this.headerShow){
6528                 cfg.cn.push(this.renderHeader());
6529             }
6530             
6531             cfg.cn.push(this.renderBody());
6532             
6533             if(this.footerShow){
6534                 cfg.cn.push(this.renderFooter());
6535             }
6536             // where does this come from?
6537             //cfg.cls+=  ' TableGrid';
6538         }
6539         
6540         return { cn : [ cfg ] };
6541     },
6542     
6543     initEvents : function()
6544     {   
6545         if(!this.store || !this.cm){
6546             return;
6547         }
6548         if (this.selModel) {
6549             this.selModel.initEvents();
6550         }
6551         
6552         
6553         //Roo.log('initEvents with ds!!!!');
6554         
6555         this.mainBody = this.el.select('tbody', true).first();
6556         this.mainHead = this.el.select('thead', true).first();
6557         this.mainFoot = this.el.select('tfoot', true).first();
6558         
6559         
6560         
6561         var _this = this;
6562         
6563         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6564             e.on('click', _this.sort, _this);
6565         });
6566         
6567         this.mainBody.on("click", this.onClick, this);
6568         this.mainBody.on("dblclick", this.onDblClick, this);
6569         
6570         // why is this done????? = it breaks dialogs??
6571         //this.parent().el.setStyle('position', 'relative');
6572         
6573         
6574         if (this.footer) {
6575             this.footer.parentId = this.id;
6576             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6577             
6578             if(this.lazyLoad){
6579                 this.el.select('tfoot tr td').first().addClass('hide');
6580             }
6581         } 
6582         
6583         if(this.loadMask) {
6584             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6585         }
6586         
6587         this.store.on('load', this.onLoad, this);
6588         this.store.on('beforeload', this.onBeforeLoad, this);
6589         this.store.on('update', this.onUpdate, this);
6590         this.store.on('add', this.onAdd, this);
6591         this.store.on("clear", this.clear, this);
6592         
6593         this.el.on("contextmenu", this.onContextMenu, this);
6594         
6595         this.mainBody.on('scroll', this.onBodyScroll, this);
6596         
6597         this.cm.on("headerchange", this.onHeaderChange, this);
6598         
6599         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6600         
6601     },
6602     
6603     onContextMenu : function(e, t)
6604     {
6605         this.processEvent("contextmenu", e);
6606     },
6607     
6608     processEvent : function(name, e)
6609     {
6610         if (name != 'touchstart' ) {
6611             this.fireEvent(name, e);    
6612         }
6613         
6614         var t = e.getTarget();
6615         
6616         var cell = Roo.get(t);
6617         
6618         if(!cell){
6619             return;
6620         }
6621         
6622         if(cell.findParent('tfoot', false, true)){
6623             return;
6624         }
6625         
6626         if(cell.findParent('thead', false, true)){
6627             
6628             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6629                 cell = Roo.get(t).findParent('th', false, true);
6630                 if (!cell) {
6631                     Roo.log("failed to find th in thead?");
6632                     Roo.log(e.getTarget());
6633                     return;
6634                 }
6635             }
6636             
6637             var cellIndex = cell.dom.cellIndex;
6638             
6639             var ename = name == 'touchstart' ? 'click' : name;
6640             this.fireEvent("header" + ename, this, cellIndex, e);
6641             
6642             return;
6643         }
6644         
6645         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6646             cell = Roo.get(t).findParent('td', false, true);
6647             if (!cell) {
6648                 Roo.log("failed to find th in tbody?");
6649                 Roo.log(e.getTarget());
6650                 return;
6651             }
6652         }
6653         
6654         var row = cell.findParent('tr', false, true);
6655         var cellIndex = cell.dom.cellIndex;
6656         var rowIndex = row.dom.rowIndex - 1;
6657         
6658         if(row !== false){
6659             
6660             this.fireEvent("row" + name, this, rowIndex, e);
6661             
6662             if(cell !== false){
6663             
6664                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6665             }
6666         }
6667         
6668     },
6669     
6670     onMouseover : function(e, el)
6671     {
6672         var cell = Roo.get(el);
6673         
6674         if(!cell){
6675             return;
6676         }
6677         
6678         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6679             cell = cell.findParent('td', false, true);
6680         }
6681         
6682         var row = cell.findParent('tr', false, true);
6683         var cellIndex = cell.dom.cellIndex;
6684         var rowIndex = row.dom.rowIndex - 1; // start from 0
6685         
6686         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6687         
6688     },
6689     
6690     onMouseout : function(e, el)
6691     {
6692         var cell = Roo.get(el);
6693         
6694         if(!cell){
6695             return;
6696         }
6697         
6698         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6699             cell = cell.findParent('td', false, true);
6700         }
6701         
6702         var row = cell.findParent('tr', false, true);
6703         var cellIndex = cell.dom.cellIndex;
6704         var rowIndex = row.dom.rowIndex - 1; // start from 0
6705         
6706         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6707         
6708     },
6709     
6710     onClick : function(e, el)
6711     {
6712         var cell = Roo.get(el);
6713         
6714         if(!cell || (!this.cellSelection && !this.rowSelection)){
6715             return;
6716         }
6717         
6718         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6719             cell = cell.findParent('td', false, true);
6720         }
6721         
6722         if(!cell || typeof(cell) == 'undefined'){
6723             return;
6724         }
6725         
6726         var row = cell.findParent('tr', false, true);
6727         
6728         if(!row || typeof(row) == 'undefined'){
6729             return;
6730         }
6731         
6732         var cellIndex = cell.dom.cellIndex;
6733         var rowIndex = this.getRowIndex(row);
6734         
6735         // why??? - should these not be based on SelectionModel?
6736         if(this.cellSelection){
6737             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6738         }
6739         
6740         if(this.rowSelection){
6741             this.fireEvent('rowclick', this, row, rowIndex, e);
6742         }
6743         
6744         
6745     },
6746         
6747     onDblClick : function(e,el)
6748     {
6749         var cell = Roo.get(el);
6750         
6751         if(!cell || (!this.cellSelection && !this.rowSelection)){
6752             return;
6753         }
6754         
6755         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6756             cell = cell.findParent('td', false, true);
6757         }
6758         
6759         if(!cell || typeof(cell) == 'undefined'){
6760             return;
6761         }
6762         
6763         var row = cell.findParent('tr', false, true);
6764         
6765         if(!row || typeof(row) == 'undefined'){
6766             return;
6767         }
6768         
6769         var cellIndex = cell.dom.cellIndex;
6770         var rowIndex = this.getRowIndex(row);
6771         
6772         if(this.cellSelection){
6773             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6774         }
6775         
6776         if(this.rowSelection){
6777             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6778         }
6779     },
6780     
6781     sort : function(e,el)
6782     {
6783         var col = Roo.get(el);
6784         
6785         if(!col.hasClass('sortable')){
6786             return;
6787         }
6788         
6789         var sort = col.attr('sort');
6790         var dir = 'ASC';
6791         
6792         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6793             dir = 'DESC';
6794         }
6795         
6796         this.store.sortInfo = {field : sort, direction : dir};
6797         
6798         if (this.footer) {
6799             Roo.log("calling footer first");
6800             this.footer.onClick('first');
6801         } else {
6802         
6803             this.store.load({ params : { start : 0 } });
6804         }
6805     },
6806     
6807     renderHeader : function()
6808     {
6809         var header = {
6810             tag: 'thead',
6811             cn : []
6812         };
6813         
6814         var cm = this.cm;
6815         this.totalWidth = 0;
6816         
6817         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6818             
6819             var config = cm.config[i];
6820             
6821             var c = {
6822                 tag: 'th',
6823                 cls : 'x-hcol-' + i,
6824                 style : '',
6825                 html: cm.getColumnHeader(i)
6826             };
6827             
6828             var hh = '';
6829             
6830             if(typeof(config.sortable) != 'undefined' && config.sortable){
6831                 c.cls = 'sortable';
6832                 c.html = '<i class="glyphicon"></i>' + c.html;
6833             }
6834             
6835             // could use BS4 hidden-..-down 
6836             
6837             if(typeof(config.lgHeader) != 'undefined'){
6838                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6839             }
6840             
6841             if(typeof(config.mdHeader) != 'undefined'){
6842                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6843             }
6844             
6845             if(typeof(config.smHeader) != 'undefined'){
6846                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6847             }
6848             
6849             if(typeof(config.xsHeader) != 'undefined'){
6850                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6851             }
6852             
6853             if(hh.length){
6854                 c.html = hh;
6855             }
6856             
6857             if(typeof(config.tooltip) != 'undefined'){
6858                 c.tooltip = config.tooltip;
6859             }
6860             
6861             if(typeof(config.colspan) != 'undefined'){
6862                 c.colspan = config.colspan;
6863             }
6864             
6865             if(typeof(config.hidden) != 'undefined' && config.hidden){
6866                 c.style += ' display:none;';
6867             }
6868             
6869             if(typeof(config.dataIndex) != 'undefined'){
6870                 c.sort = config.dataIndex;
6871             }
6872             
6873            
6874             
6875             if(typeof(config.align) != 'undefined' && config.align.length){
6876                 c.style += ' text-align:' + config.align + ';';
6877             }
6878             
6879             if(typeof(config.width) != 'undefined'){
6880                 c.style += ' width:' + config.width + 'px;';
6881                 this.totalWidth += config.width;
6882             } else {
6883                 this.totalWidth += 100; // assume minimum of 100 per column?
6884             }
6885             
6886             if(typeof(config.cls) != 'undefined'){
6887                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6888             }
6889             
6890             ['xs','sm','md','lg'].map(function(size){
6891                 
6892                 if(typeof(config[size]) == 'undefined'){
6893                     return;
6894                 }
6895                  
6896                 if (!config[size]) { // 0 = hidden
6897                     // BS 4 '0' is treated as hide that column and below.
6898                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6899                     return;
6900                 }
6901                 
6902                 c.cls += ' col-' + size + '-' + config[size] + (
6903                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6904                 );
6905                 
6906                 
6907             });
6908             
6909             header.cn.push(c)
6910         }
6911         
6912         return header;
6913     },
6914     
6915     renderBody : function()
6916     {
6917         var body = {
6918             tag: 'tbody',
6919             cn : [
6920                 {
6921                     tag: 'tr',
6922                     cn : [
6923                         {
6924                             tag : 'td',
6925                             colspan :  this.cm.getColumnCount()
6926                         }
6927                     ]
6928                 }
6929             ]
6930         };
6931         
6932         return body;
6933     },
6934     
6935     renderFooter : function()
6936     {
6937         var footer = {
6938             tag: 'tfoot',
6939             cn : [
6940                 {
6941                     tag: 'tr',
6942                     cn : [
6943                         {
6944                             tag : 'td',
6945                             colspan :  this.cm.getColumnCount()
6946                         }
6947                     ]
6948                 }
6949             ]
6950         };
6951         
6952         return footer;
6953     },
6954     
6955     
6956     
6957     onLoad : function()
6958     {
6959 //        Roo.log('ds onload');
6960         this.clear();
6961         
6962         var _this = this;
6963         var cm = this.cm;
6964         var ds = this.store;
6965         
6966         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6967             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6968             if (_this.store.sortInfo) {
6969                     
6970                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6971                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6972                 }
6973                 
6974                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6975                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6976                 }
6977             }
6978         });
6979         
6980         var tbody =  this.mainBody;
6981               
6982         if(ds.getCount() > 0){
6983             ds.data.each(function(d,rowIndex){
6984                 var row =  this.renderRow(cm, ds, rowIndex);
6985                 
6986                 tbody.createChild(row);
6987                 
6988                 var _this = this;
6989                 
6990                 if(row.cellObjects.length){
6991                     Roo.each(row.cellObjects, function(r){
6992                         _this.renderCellObject(r);
6993                     })
6994                 }
6995                 
6996             }, this);
6997         }
6998         
6999         var tfoot = this.el.select('tfoot', true).first();
7000         
7001         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7002             
7003             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7004             
7005             var total = this.ds.getTotalCount();
7006             
7007             if(this.footer.pageSize < total){
7008                 this.mainFoot.show();
7009             }
7010         }
7011         
7012         Roo.each(this.el.select('tbody td', true).elements, function(e){
7013             e.on('mouseover', _this.onMouseover, _this);
7014         });
7015         
7016         Roo.each(this.el.select('tbody td', true).elements, function(e){
7017             e.on('mouseout', _this.onMouseout, _this);
7018         });
7019         this.fireEvent('rowsrendered', this);
7020         
7021         this.autoSize();
7022     },
7023     
7024     
7025     onUpdate : function(ds,record)
7026     {
7027         this.refreshRow(record);
7028         this.autoSize();
7029     },
7030     
7031     onRemove : function(ds, record, index, isUpdate){
7032         if(isUpdate !== true){
7033             this.fireEvent("beforerowremoved", this, index, record);
7034         }
7035         var bt = this.mainBody.dom;
7036         
7037         var rows = this.el.select('tbody > tr', true).elements;
7038         
7039         if(typeof(rows[index]) != 'undefined'){
7040             bt.removeChild(rows[index].dom);
7041         }
7042         
7043 //        if(bt.rows[index]){
7044 //            bt.removeChild(bt.rows[index]);
7045 //        }
7046         
7047         if(isUpdate !== true){
7048             //this.stripeRows(index);
7049             //this.syncRowHeights(index, index);
7050             //this.layout();
7051             this.fireEvent("rowremoved", this, index, record);
7052         }
7053     },
7054     
7055     onAdd : function(ds, records, rowIndex)
7056     {
7057         //Roo.log('on Add called');
7058         // - note this does not handle multiple adding very well..
7059         var bt = this.mainBody.dom;
7060         for (var i =0 ; i < records.length;i++) {
7061             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7062             //Roo.log(records[i]);
7063             //Roo.log(this.store.getAt(rowIndex+i));
7064             this.insertRow(this.store, rowIndex + i, false);
7065             return;
7066         }
7067         
7068     },
7069     
7070     
7071     refreshRow : function(record){
7072         var ds = this.store, index;
7073         if(typeof record == 'number'){
7074             index = record;
7075             record = ds.getAt(index);
7076         }else{
7077             index = ds.indexOf(record);
7078         }
7079         this.insertRow(ds, index, true);
7080         this.autoSize();
7081         this.onRemove(ds, record, index+1, true);
7082         this.autoSize();
7083         //this.syncRowHeights(index, index);
7084         //this.layout();
7085         this.fireEvent("rowupdated", this, index, record);
7086     },
7087     
7088     insertRow : function(dm, rowIndex, isUpdate){
7089         
7090         if(!isUpdate){
7091             this.fireEvent("beforerowsinserted", this, rowIndex);
7092         }
7093             //var s = this.getScrollState();
7094         var row = this.renderRow(this.cm, this.store, rowIndex);
7095         // insert before rowIndex..
7096         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7097         
7098         var _this = this;
7099                 
7100         if(row.cellObjects.length){
7101             Roo.each(row.cellObjects, function(r){
7102                 _this.renderCellObject(r);
7103             })
7104         }
7105             
7106         if(!isUpdate){
7107             this.fireEvent("rowsinserted", this, rowIndex);
7108             //this.syncRowHeights(firstRow, lastRow);
7109             //this.stripeRows(firstRow);
7110             //this.layout();
7111         }
7112         
7113     },
7114     
7115     
7116     getRowDom : function(rowIndex)
7117     {
7118         var rows = this.el.select('tbody > tr', true).elements;
7119         
7120         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7121         
7122     },
7123     // returns the object tree for a tr..
7124   
7125     
7126     renderRow : function(cm, ds, rowIndex) 
7127     {
7128         var d = ds.getAt(rowIndex);
7129         
7130         var row = {
7131             tag : 'tr',
7132             cls : 'x-row-' + rowIndex,
7133             cn : []
7134         };
7135             
7136         var cellObjects = [];
7137         
7138         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7139             var config = cm.config[i];
7140             
7141             var renderer = cm.getRenderer(i);
7142             var value = '';
7143             var id = false;
7144             
7145             if(typeof(renderer) !== 'undefined'){
7146                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7147             }
7148             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7149             // and are rendered into the cells after the row is rendered - using the id for the element.
7150             
7151             if(typeof(value) === 'object'){
7152                 id = Roo.id();
7153                 cellObjects.push({
7154                     container : id,
7155                     cfg : value 
7156                 })
7157             }
7158             
7159             var rowcfg = {
7160                 record: d,
7161                 rowIndex : rowIndex,
7162                 colIndex : i,
7163                 rowClass : ''
7164             };
7165
7166             this.fireEvent('rowclass', this, rowcfg);
7167             
7168             var td = {
7169                 tag: 'td',
7170                 cls : rowcfg.rowClass + ' x-col-' + i,
7171                 style: '',
7172                 html: (typeof(value) === 'object') ? '' : value
7173             };
7174             
7175             if (id) {
7176                 td.id = id;
7177             }
7178             
7179             if(typeof(config.colspan) != 'undefined'){
7180                 td.colspan = config.colspan;
7181             }
7182             
7183             if(typeof(config.hidden) != 'undefined' && config.hidden){
7184                 td.style += ' display:none;';
7185             }
7186             
7187             if(typeof(config.align) != 'undefined' && config.align.length){
7188                 td.style += ' text-align:' + config.align + ';';
7189             }
7190             if(typeof(config.valign) != 'undefined' && config.valign.length){
7191                 td.style += ' vertical-align:' + config.valign + ';';
7192             }
7193             
7194             if(typeof(config.width) != 'undefined'){
7195                 td.style += ' width:' +  config.width + 'px;';
7196             }
7197             
7198             if(typeof(config.cursor) != 'undefined'){
7199                 td.style += ' cursor:' +  config.cursor + ';';
7200             }
7201             
7202             if(typeof(config.cls) != 'undefined'){
7203                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7204             }
7205             
7206             ['xs','sm','md','lg'].map(function(size){
7207                 
7208                 if(typeof(config[size]) == 'undefined'){
7209                     return;
7210                 }
7211                 
7212                 
7213                   
7214                 if (!config[size]) { // 0 = hidden
7215                     // BS 4 '0' is treated as hide that column and below.
7216                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7217                     return;
7218                 }
7219                 
7220                 td.cls += ' col-' + size + '-' + config[size] + (
7221                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7222                 );
7223                  
7224
7225             });
7226             
7227             row.cn.push(td);
7228            
7229         }
7230         
7231         row.cellObjects = cellObjects;
7232         
7233         return row;
7234           
7235     },
7236     
7237     
7238     
7239     onBeforeLoad : function()
7240     {
7241         
7242     },
7243      /**
7244      * Remove all rows
7245      */
7246     clear : function()
7247     {
7248         this.el.select('tbody', true).first().dom.innerHTML = '';
7249     },
7250     /**
7251      * Show or hide a row.
7252      * @param {Number} rowIndex to show or hide
7253      * @param {Boolean} state hide
7254      */
7255     setRowVisibility : function(rowIndex, state)
7256     {
7257         var bt = this.mainBody.dom;
7258         
7259         var rows = this.el.select('tbody > tr', true).elements;
7260         
7261         if(typeof(rows[rowIndex]) == 'undefined'){
7262             return;
7263         }
7264         rows[rowIndex].dom.style.display = state ? '' : 'none';
7265     },
7266     
7267     
7268     getSelectionModel : function(){
7269         if(!this.selModel){
7270             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7271         }
7272         return this.selModel;
7273     },
7274     /*
7275      * Render the Roo.bootstrap object from renderder
7276      */
7277     renderCellObject : function(r)
7278     {
7279         var _this = this;
7280         
7281         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7282         
7283         var t = r.cfg.render(r.container);
7284         
7285         if(r.cfg.cn){
7286             Roo.each(r.cfg.cn, function(c){
7287                 var child = {
7288                     container: t.getChildContainer(),
7289                     cfg: c
7290                 };
7291                 _this.renderCellObject(child);
7292             })
7293         }
7294     },
7295     
7296     getRowIndex : function(row)
7297     {
7298         var rowIndex = -1;
7299         
7300         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7301             if(el != row){
7302                 return;
7303             }
7304             
7305             rowIndex = index;
7306         });
7307         
7308         return rowIndex;
7309     },
7310      /**
7311      * Returns the grid's underlying element = used by panel.Grid
7312      * @return {Element} The element
7313      */
7314     getGridEl : function(){
7315         return this.el;
7316     },
7317      /**
7318      * Forces a resize - used by panel.Grid
7319      * @return {Element} The element
7320      */
7321     autoSize : function()
7322     {
7323         //var ctr = Roo.get(this.container.dom.parentElement);
7324         var ctr = Roo.get(this.el.dom);
7325         
7326         var thd = this.getGridEl().select('thead',true).first();
7327         var tbd = this.getGridEl().select('tbody', true).first();
7328         var tfd = this.getGridEl().select('tfoot', true).first();
7329         
7330         var cw = ctr.getWidth();
7331         
7332         if (tbd) {
7333             
7334             tbd.setSize(ctr.getWidth(),
7335                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7336             );
7337             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7338             cw -= barsize;
7339         }
7340         cw = Math.max(cw, this.totalWidth);
7341         this.getGridEl().select('tr',true).setWidth(cw);
7342         // resize 'expandable coloumn?
7343         
7344         return; // we doe not have a view in this design..
7345         
7346     },
7347     onBodyScroll: function()
7348     {
7349         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7350         if(this.mainHead){
7351             this.mainHead.setStyle({
7352                 'position' : 'relative',
7353                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7354             });
7355         }
7356         
7357         if(this.lazyLoad){
7358             
7359             var scrollHeight = this.mainBody.dom.scrollHeight;
7360             
7361             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7362             
7363             var height = this.mainBody.getHeight();
7364             
7365             if(scrollHeight - height == scrollTop) {
7366                 
7367                 var total = this.ds.getTotalCount();
7368                 
7369                 if(this.footer.cursor + this.footer.pageSize < total){
7370                     
7371                     this.footer.ds.load({
7372                         params : {
7373                             start : this.footer.cursor + this.footer.pageSize,
7374                             limit : this.footer.pageSize
7375                         },
7376                         add : true
7377                     });
7378                 }
7379             }
7380             
7381         }
7382     },
7383     
7384     onHeaderChange : function()
7385     {
7386         var header = this.renderHeader();
7387         var table = this.el.select('table', true).first();
7388         
7389         this.mainHead.remove();
7390         this.mainHead = table.createChild(header, this.mainBody, false);
7391     },
7392     
7393     onHiddenChange : function(colModel, colIndex, hidden)
7394     {
7395         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7396         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7397         
7398         this.CSS.updateRule(thSelector, "display", "");
7399         this.CSS.updateRule(tdSelector, "display", "");
7400         
7401         if(hidden){
7402             this.CSS.updateRule(thSelector, "display", "none");
7403             this.CSS.updateRule(tdSelector, "display", "none");
7404         }
7405         
7406         this.onHeaderChange();
7407         this.onLoad();
7408     },
7409     
7410     setColumnWidth: function(col_index, width)
7411     {
7412         // width = "md-2 xs-2..."
7413         if(!this.colModel.config[col_index]) {
7414             return;
7415         }
7416         
7417         var w = width.split(" ");
7418         
7419         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7420         
7421         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7422         
7423         
7424         for(var j = 0; j < w.length; j++) {
7425             
7426             if(!w[j]) {
7427                 continue;
7428             }
7429             
7430             var size_cls = w[j].split("-");
7431             
7432             if(!Number.isInteger(size_cls[1] * 1)) {
7433                 continue;
7434             }
7435             
7436             if(!this.colModel.config[col_index][size_cls[0]]) {
7437                 continue;
7438             }
7439             
7440             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7441                 continue;
7442             }
7443             
7444             h_row[0].classList.replace(
7445                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7446                 "col-"+size_cls[0]+"-"+size_cls[1]
7447             );
7448             
7449             for(var i = 0; i < rows.length; i++) {
7450                 
7451                 var size_cls = w[j].split("-");
7452                 
7453                 if(!Number.isInteger(size_cls[1] * 1)) {
7454                     continue;
7455                 }
7456                 
7457                 if(!this.colModel.config[col_index][size_cls[0]]) {
7458                     continue;
7459                 }
7460                 
7461                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7462                     continue;
7463                 }
7464                 
7465                 rows[i].classList.replace(
7466                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7467                     "col-"+size_cls[0]+"-"+size_cls[1]
7468                 );
7469             }
7470             
7471             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7472         }
7473     }
7474 });
7475
7476  
7477
7478  /*
7479  * - LGPL
7480  *
7481  * table cell
7482  * 
7483  */
7484
7485 /**
7486  * @class Roo.bootstrap.TableCell
7487  * @extends Roo.bootstrap.Component
7488  * Bootstrap TableCell class
7489  * @cfg {String} html cell contain text
7490  * @cfg {String} cls cell class
7491  * @cfg {String} tag cell tag (td|th) default td
7492  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7493  * @cfg {String} align Aligns the content in a cell
7494  * @cfg {String} axis Categorizes cells
7495  * @cfg {String} bgcolor Specifies the background color of a cell
7496  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7497  * @cfg {Number} colspan Specifies the number of columns a cell should span
7498  * @cfg {String} headers Specifies one or more header cells a cell is related to
7499  * @cfg {Number} height Sets the height of a cell
7500  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7501  * @cfg {Number} rowspan Sets the number of rows a cell should span
7502  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7503  * @cfg {String} valign Vertical aligns the content in a cell
7504  * @cfg {Number} width Specifies the width of a cell
7505  * 
7506  * @constructor
7507  * Create a new TableCell
7508  * @param {Object} config The config object
7509  */
7510
7511 Roo.bootstrap.TableCell = function(config){
7512     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7513 };
7514
7515 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7516     
7517     html: false,
7518     cls: false,
7519     tag: false,
7520     abbr: false,
7521     align: false,
7522     axis: false,
7523     bgcolor: false,
7524     charoff: false,
7525     colspan: false,
7526     headers: false,
7527     height: false,
7528     nowrap: false,
7529     rowspan: false,
7530     scope: false,
7531     valign: false,
7532     width: false,
7533     
7534     
7535     getAutoCreate : function(){
7536         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7537         
7538         cfg = {
7539             tag: 'td'
7540         };
7541         
7542         if(this.tag){
7543             cfg.tag = this.tag;
7544         }
7545         
7546         if (this.html) {
7547             cfg.html=this.html
7548         }
7549         if (this.cls) {
7550             cfg.cls=this.cls
7551         }
7552         if (this.abbr) {
7553             cfg.abbr=this.abbr
7554         }
7555         if (this.align) {
7556             cfg.align=this.align
7557         }
7558         if (this.axis) {
7559             cfg.axis=this.axis
7560         }
7561         if (this.bgcolor) {
7562             cfg.bgcolor=this.bgcolor
7563         }
7564         if (this.charoff) {
7565             cfg.charoff=this.charoff
7566         }
7567         if (this.colspan) {
7568             cfg.colspan=this.colspan
7569         }
7570         if (this.headers) {
7571             cfg.headers=this.headers
7572         }
7573         if (this.height) {
7574             cfg.height=this.height
7575         }
7576         if (this.nowrap) {
7577             cfg.nowrap=this.nowrap
7578         }
7579         if (this.rowspan) {
7580             cfg.rowspan=this.rowspan
7581         }
7582         if (this.scope) {
7583             cfg.scope=this.scope
7584         }
7585         if (this.valign) {
7586             cfg.valign=this.valign
7587         }
7588         if (this.width) {
7589             cfg.width=this.width
7590         }
7591         
7592         
7593         return cfg;
7594     }
7595    
7596 });
7597
7598  
7599
7600  /*
7601  * - LGPL
7602  *
7603  * table row
7604  * 
7605  */
7606
7607 /**
7608  * @class Roo.bootstrap.TableRow
7609  * @extends Roo.bootstrap.Component
7610  * Bootstrap TableRow class
7611  * @cfg {String} cls row class
7612  * @cfg {String} align Aligns the content in a table row
7613  * @cfg {String} bgcolor Specifies a background color for a table row
7614  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7615  * @cfg {String} valign Vertical aligns the content in a table row
7616  * 
7617  * @constructor
7618  * Create a new TableRow
7619  * @param {Object} config The config object
7620  */
7621
7622 Roo.bootstrap.TableRow = function(config){
7623     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7624 };
7625
7626 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7627     
7628     cls: false,
7629     align: false,
7630     bgcolor: false,
7631     charoff: false,
7632     valign: false,
7633     
7634     getAutoCreate : function(){
7635         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7636         
7637         cfg = {
7638             tag: 'tr'
7639         };
7640             
7641         if(this.cls){
7642             cfg.cls = this.cls;
7643         }
7644         if(this.align){
7645             cfg.align = this.align;
7646         }
7647         if(this.bgcolor){
7648             cfg.bgcolor = this.bgcolor;
7649         }
7650         if(this.charoff){
7651             cfg.charoff = this.charoff;
7652         }
7653         if(this.valign){
7654             cfg.valign = this.valign;
7655         }
7656         
7657         return cfg;
7658     }
7659    
7660 });
7661
7662  
7663
7664  /*
7665  * - LGPL
7666  *
7667  * table body
7668  * 
7669  */
7670
7671 /**
7672  * @class Roo.bootstrap.TableBody
7673  * @extends Roo.bootstrap.Component
7674  * Bootstrap TableBody class
7675  * @cfg {String} cls element class
7676  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7677  * @cfg {String} align Aligns the content inside the element
7678  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7679  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7680  * 
7681  * @constructor
7682  * Create a new TableBody
7683  * @param {Object} config The config object
7684  */
7685
7686 Roo.bootstrap.TableBody = function(config){
7687     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7688 };
7689
7690 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7691     
7692     cls: false,
7693     tag: false,
7694     align: false,
7695     charoff: false,
7696     valign: false,
7697     
7698     getAutoCreate : function(){
7699         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7700         
7701         cfg = {
7702             tag: 'tbody'
7703         };
7704             
7705         if (this.cls) {
7706             cfg.cls=this.cls
7707         }
7708         if(this.tag){
7709             cfg.tag = this.tag;
7710         }
7711         
7712         if(this.align){
7713             cfg.align = this.align;
7714         }
7715         if(this.charoff){
7716             cfg.charoff = this.charoff;
7717         }
7718         if(this.valign){
7719             cfg.valign = this.valign;
7720         }
7721         
7722         return cfg;
7723     }
7724     
7725     
7726 //    initEvents : function()
7727 //    {
7728 //        
7729 //        if(!this.store){
7730 //            return;
7731 //        }
7732 //        
7733 //        this.store = Roo.factory(this.store, Roo.data);
7734 //        this.store.on('load', this.onLoad, this);
7735 //        
7736 //        this.store.load();
7737 //        
7738 //    },
7739 //    
7740 //    onLoad: function () 
7741 //    {   
7742 //        this.fireEvent('load', this);
7743 //    }
7744 //    
7745 //   
7746 });
7747
7748  
7749
7750  /*
7751  * Based on:
7752  * Ext JS Library 1.1.1
7753  * Copyright(c) 2006-2007, Ext JS, LLC.
7754  *
7755  * Originally Released Under LGPL - original licence link has changed is not relivant.
7756  *
7757  * Fork - LGPL
7758  * <script type="text/javascript">
7759  */
7760
7761 // as we use this in bootstrap.
7762 Roo.namespace('Roo.form');
7763  /**
7764  * @class Roo.form.Action
7765  * Internal Class used to handle form actions
7766  * @constructor
7767  * @param {Roo.form.BasicForm} el The form element or its id
7768  * @param {Object} config Configuration options
7769  */
7770
7771  
7772  
7773 // define the action interface
7774 Roo.form.Action = function(form, options){
7775     this.form = form;
7776     this.options = options || {};
7777 };
7778 /**
7779  * Client Validation Failed
7780  * @const 
7781  */
7782 Roo.form.Action.CLIENT_INVALID = 'client';
7783 /**
7784  * Server Validation Failed
7785  * @const 
7786  */
7787 Roo.form.Action.SERVER_INVALID = 'server';
7788  /**
7789  * Connect to Server Failed
7790  * @const 
7791  */
7792 Roo.form.Action.CONNECT_FAILURE = 'connect';
7793 /**
7794  * Reading Data from Server Failed
7795  * @const 
7796  */
7797 Roo.form.Action.LOAD_FAILURE = 'load';
7798
7799 Roo.form.Action.prototype = {
7800     type : 'default',
7801     failureType : undefined,
7802     response : undefined,
7803     result : undefined,
7804
7805     // interface method
7806     run : function(options){
7807
7808     },
7809
7810     // interface method
7811     success : function(response){
7812
7813     },
7814
7815     // interface method
7816     handleResponse : function(response){
7817
7818     },
7819
7820     // default connection failure
7821     failure : function(response){
7822         
7823         this.response = response;
7824         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7825         this.form.afterAction(this, false);
7826     },
7827
7828     processResponse : function(response){
7829         this.response = response;
7830         if(!response.responseText){
7831             return true;
7832         }
7833         this.result = this.handleResponse(response);
7834         return this.result;
7835     },
7836
7837     // utility functions used internally
7838     getUrl : function(appendParams){
7839         var url = this.options.url || this.form.url || this.form.el.dom.action;
7840         if(appendParams){
7841             var p = this.getParams();
7842             if(p){
7843                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7844             }
7845         }
7846         return url;
7847     },
7848
7849     getMethod : function(){
7850         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7851     },
7852
7853     getParams : function(){
7854         var bp = this.form.baseParams;
7855         var p = this.options.params;
7856         if(p){
7857             if(typeof p == "object"){
7858                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7859             }else if(typeof p == 'string' && bp){
7860                 p += '&' + Roo.urlEncode(bp);
7861             }
7862         }else if(bp){
7863             p = Roo.urlEncode(bp);
7864         }
7865         return p;
7866     },
7867
7868     createCallback : function(){
7869         return {
7870             success: this.success,
7871             failure: this.failure,
7872             scope: this,
7873             timeout: (this.form.timeout*1000),
7874             upload: this.form.fileUpload ? this.success : undefined
7875         };
7876     }
7877 };
7878
7879 Roo.form.Action.Submit = function(form, options){
7880     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7881 };
7882
7883 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7884     type : 'submit',
7885
7886     haveProgress : false,
7887     uploadComplete : false,
7888     
7889     // uploadProgress indicator.
7890     uploadProgress : function()
7891     {
7892         if (!this.form.progressUrl) {
7893             return;
7894         }
7895         
7896         if (!this.haveProgress) {
7897             Roo.MessageBox.progress("Uploading", "Uploading");
7898         }
7899         if (this.uploadComplete) {
7900            Roo.MessageBox.hide();
7901            return;
7902         }
7903         
7904         this.haveProgress = true;
7905    
7906         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7907         
7908         var c = new Roo.data.Connection();
7909         c.request({
7910             url : this.form.progressUrl,
7911             params: {
7912                 id : uid
7913             },
7914             method: 'GET',
7915             success : function(req){
7916                //console.log(data);
7917                 var rdata = false;
7918                 var edata;
7919                 try  {
7920                    rdata = Roo.decode(req.responseText)
7921                 } catch (e) {
7922                     Roo.log("Invalid data from server..");
7923                     Roo.log(edata);
7924                     return;
7925                 }
7926                 if (!rdata || !rdata.success) {
7927                     Roo.log(rdata);
7928                     Roo.MessageBox.alert(Roo.encode(rdata));
7929                     return;
7930                 }
7931                 var data = rdata.data;
7932                 
7933                 if (this.uploadComplete) {
7934                    Roo.MessageBox.hide();
7935                    return;
7936                 }
7937                    
7938                 if (data){
7939                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7940                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7941                     );
7942                 }
7943                 this.uploadProgress.defer(2000,this);
7944             },
7945        
7946             failure: function(data) {
7947                 Roo.log('progress url failed ');
7948                 Roo.log(data);
7949             },
7950             scope : this
7951         });
7952            
7953     },
7954     
7955     
7956     run : function()
7957     {
7958         // run get Values on the form, so it syncs any secondary forms.
7959         this.form.getValues();
7960         
7961         var o = this.options;
7962         var method = this.getMethod();
7963         var isPost = method == 'POST';
7964         if(o.clientValidation === false || this.form.isValid()){
7965             
7966             if (this.form.progressUrl) {
7967                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7968                     (new Date() * 1) + '' + Math.random());
7969                     
7970             } 
7971             
7972             
7973             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7974                 form:this.form.el.dom,
7975                 url:this.getUrl(!isPost),
7976                 method: method,
7977                 params:isPost ? this.getParams() : null,
7978                 isUpload: this.form.fileUpload,
7979                 formData : this.form.formData
7980             }));
7981             
7982             this.uploadProgress();
7983
7984         }else if (o.clientValidation !== false){ // client validation failed
7985             this.failureType = Roo.form.Action.CLIENT_INVALID;
7986             this.form.afterAction(this, false);
7987         }
7988     },
7989
7990     success : function(response)
7991     {
7992         this.uploadComplete= true;
7993         if (this.haveProgress) {
7994             Roo.MessageBox.hide();
7995         }
7996         
7997         
7998         var result = this.processResponse(response);
7999         if(result === true || result.success){
8000             this.form.afterAction(this, true);
8001             return;
8002         }
8003         if(result.errors){
8004             this.form.markInvalid(result.errors);
8005             this.failureType = Roo.form.Action.SERVER_INVALID;
8006         }
8007         this.form.afterAction(this, false);
8008     },
8009     failure : function(response)
8010     {
8011         this.uploadComplete= true;
8012         if (this.haveProgress) {
8013             Roo.MessageBox.hide();
8014         }
8015         
8016         this.response = response;
8017         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8018         this.form.afterAction(this, false);
8019     },
8020     
8021     handleResponse : function(response){
8022         if(this.form.errorReader){
8023             var rs = this.form.errorReader.read(response);
8024             var errors = [];
8025             if(rs.records){
8026                 for(var i = 0, len = rs.records.length; i < len; i++) {
8027                     var r = rs.records[i];
8028                     errors[i] = r.data;
8029                 }
8030             }
8031             if(errors.length < 1){
8032                 errors = null;
8033             }
8034             return {
8035                 success : rs.success,
8036                 errors : errors
8037             };
8038         }
8039         var ret = false;
8040         try {
8041             ret = Roo.decode(response.responseText);
8042         } catch (e) {
8043             ret = {
8044                 success: false,
8045                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8046                 errors : []
8047             };
8048         }
8049         return ret;
8050         
8051     }
8052 });
8053
8054
8055 Roo.form.Action.Load = function(form, options){
8056     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8057     this.reader = this.form.reader;
8058 };
8059
8060 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8061     type : 'load',
8062
8063     run : function(){
8064         
8065         Roo.Ajax.request(Roo.apply(
8066                 this.createCallback(), {
8067                     method:this.getMethod(),
8068                     url:this.getUrl(false),
8069                     params:this.getParams()
8070         }));
8071     },
8072
8073     success : function(response){
8074         
8075         var result = this.processResponse(response);
8076         if(result === true || !result.success || !result.data){
8077             this.failureType = Roo.form.Action.LOAD_FAILURE;
8078             this.form.afterAction(this, false);
8079             return;
8080         }
8081         this.form.clearInvalid();
8082         this.form.setValues(result.data);
8083         this.form.afterAction(this, true);
8084     },
8085
8086     handleResponse : function(response){
8087         if(this.form.reader){
8088             var rs = this.form.reader.read(response);
8089             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8090             return {
8091                 success : rs.success,
8092                 data : data
8093             };
8094         }
8095         return Roo.decode(response.responseText);
8096     }
8097 });
8098
8099 Roo.form.Action.ACTION_TYPES = {
8100     'load' : Roo.form.Action.Load,
8101     'submit' : Roo.form.Action.Submit
8102 };/*
8103  * - LGPL
8104  *
8105  * form
8106  *
8107  */
8108
8109 /**
8110  * @class Roo.bootstrap.Form
8111  * @extends Roo.bootstrap.Component
8112  * Bootstrap Form class
8113  * @cfg {String} method  GET | POST (default POST)
8114  * @cfg {String} labelAlign top | left (default top)
8115  * @cfg {String} align left  | right - for navbars
8116  * @cfg {Boolean} loadMask load mask when submit (default true)
8117
8118  *
8119  * @constructor
8120  * Create a new Form
8121  * @param {Object} config The config object
8122  */
8123
8124
8125 Roo.bootstrap.Form = function(config){
8126     
8127     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8128     
8129     Roo.bootstrap.Form.popover.apply();
8130     
8131     this.addEvents({
8132         /**
8133          * @event clientvalidation
8134          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8135          * @param {Form} this
8136          * @param {Boolean} valid true if the form has passed client-side validation
8137          */
8138         clientvalidation: true,
8139         /**
8140          * @event beforeaction
8141          * Fires before any action is performed. Return false to cancel the action.
8142          * @param {Form} this
8143          * @param {Action} action The action to be performed
8144          */
8145         beforeaction: true,
8146         /**
8147          * @event actionfailed
8148          * Fires when an action fails.
8149          * @param {Form} this
8150          * @param {Action} action The action that failed
8151          */
8152         actionfailed : true,
8153         /**
8154          * @event actioncomplete
8155          * Fires when an action is completed.
8156          * @param {Form} this
8157          * @param {Action} action The action that completed
8158          */
8159         actioncomplete : true
8160     });
8161 };
8162
8163 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8164
8165      /**
8166      * @cfg {String} method
8167      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8168      */
8169     method : 'POST',
8170     /**
8171      * @cfg {String} url
8172      * The URL to use for form actions if one isn't supplied in the action options.
8173      */
8174     /**
8175      * @cfg {Boolean} fileUpload
8176      * Set to true if this form is a file upload.
8177      */
8178
8179     /**
8180      * @cfg {Object} baseParams
8181      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8182      */
8183
8184     /**
8185      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8186      */
8187     timeout: 30,
8188     /**
8189      * @cfg {Sting} align (left|right) for navbar forms
8190      */
8191     align : 'left',
8192
8193     // private
8194     activeAction : null,
8195
8196     /**
8197      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8198      * element by passing it or its id or mask the form itself by passing in true.
8199      * @type Mixed
8200      */
8201     waitMsgTarget : false,
8202
8203     loadMask : true,
8204     
8205     /**
8206      * @cfg {Boolean} errorMask (true|false) default false
8207      */
8208     errorMask : false,
8209     
8210     /**
8211      * @cfg {Number} maskOffset Default 100
8212      */
8213     maskOffset : 100,
8214     
8215     /**
8216      * @cfg {Boolean} maskBody
8217      */
8218     maskBody : false,
8219
8220     getAutoCreate : function(){
8221
8222         var cfg = {
8223             tag: 'form',
8224             method : this.method || 'POST',
8225             id : this.id || Roo.id(),
8226             cls : ''
8227         };
8228         if (this.parent().xtype.match(/^Nav/)) {
8229             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8230
8231         }
8232
8233         if (this.labelAlign == 'left' ) {
8234             cfg.cls += ' form-horizontal';
8235         }
8236
8237
8238         return cfg;
8239     },
8240     initEvents : function()
8241     {
8242         this.el.on('submit', this.onSubmit, this);
8243         // this was added as random key presses on the form where triggering form submit.
8244         this.el.on('keypress', function(e) {
8245             if (e.getCharCode() != 13) {
8246                 return true;
8247             }
8248             // we might need to allow it for textareas.. and some other items.
8249             // check e.getTarget().
8250
8251             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8252                 return true;
8253             }
8254
8255             Roo.log("keypress blocked");
8256
8257             e.preventDefault();
8258             return false;
8259         });
8260         
8261     },
8262     // private
8263     onSubmit : function(e){
8264         e.stopEvent();
8265     },
8266
8267      /**
8268      * Returns true if client-side validation on the form is successful.
8269      * @return Boolean
8270      */
8271     isValid : function(){
8272         var items = this.getItems();
8273         var valid = true;
8274         var target = false;
8275         
8276         items.each(function(f){
8277             
8278             if(f.validate()){
8279                 return;
8280             }
8281             
8282             Roo.log('invalid field: ' + f.name);
8283             
8284             valid = false;
8285
8286             if(!target && f.el.isVisible(true)){
8287                 target = f;
8288             }
8289            
8290         });
8291         
8292         if(this.errorMask && !valid){
8293             Roo.bootstrap.Form.popover.mask(this, target);
8294         }
8295         
8296         return valid;
8297     },
8298     
8299     /**
8300      * Returns true if any fields in this form have changed since their original load.
8301      * @return Boolean
8302      */
8303     isDirty : function(){
8304         var dirty = false;
8305         var items = this.getItems();
8306         items.each(function(f){
8307            if(f.isDirty()){
8308                dirty = true;
8309                return false;
8310            }
8311            return true;
8312         });
8313         return dirty;
8314     },
8315      /**
8316      * Performs a predefined action (submit or load) or custom actions you define on this form.
8317      * @param {String} actionName The name of the action type
8318      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8319      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8320      * accept other config options):
8321      * <pre>
8322 Property          Type             Description
8323 ----------------  ---------------  ----------------------------------------------------------------------------------
8324 url               String           The url for the action (defaults to the form's url)
8325 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8326 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8327 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8328                                    validate the form on the client (defaults to false)
8329      * </pre>
8330      * @return {BasicForm} this
8331      */
8332     doAction : function(action, options){
8333         if(typeof action == 'string'){
8334             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8335         }
8336         if(this.fireEvent('beforeaction', this, action) !== false){
8337             this.beforeAction(action);
8338             action.run.defer(100, action);
8339         }
8340         return this;
8341     },
8342
8343     // private
8344     beforeAction : function(action){
8345         var o = action.options;
8346         
8347         if(this.loadMask){
8348             
8349             if(this.maskBody){
8350                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8351             } else {
8352                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8353             }
8354         }
8355         // not really supported yet.. ??
8356
8357         //if(this.waitMsgTarget === true){
8358         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8359         //}else if(this.waitMsgTarget){
8360         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8361         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8362         //}else {
8363         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8364        // }
8365
8366     },
8367
8368     // private
8369     afterAction : function(action, success){
8370         this.activeAction = null;
8371         var o = action.options;
8372
8373         if(this.loadMask){
8374             
8375             if(this.maskBody){
8376                 Roo.get(document.body).unmask();
8377             } else {
8378                 this.el.unmask();
8379             }
8380         }
8381         
8382         //if(this.waitMsgTarget === true){
8383 //            this.el.unmask();
8384         //}else if(this.waitMsgTarget){
8385         //    this.waitMsgTarget.unmask();
8386         //}else{
8387         //    Roo.MessageBox.updateProgress(1);
8388         //    Roo.MessageBox.hide();
8389        // }
8390         //
8391         if(success){
8392             if(o.reset){
8393                 this.reset();
8394             }
8395             Roo.callback(o.success, o.scope, [this, action]);
8396             this.fireEvent('actioncomplete', this, action);
8397
8398         }else{
8399
8400             // failure condition..
8401             // we have a scenario where updates need confirming.
8402             // eg. if a locking scenario exists..
8403             // we look for { errors : { needs_confirm : true }} in the response.
8404             if (
8405                 (typeof(action.result) != 'undefined')  &&
8406                 (typeof(action.result.errors) != 'undefined')  &&
8407                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8408            ){
8409                 var _t = this;
8410                 Roo.log("not supported yet");
8411                  /*
8412
8413                 Roo.MessageBox.confirm(
8414                     "Change requires confirmation",
8415                     action.result.errorMsg,
8416                     function(r) {
8417                         if (r != 'yes') {
8418                             return;
8419                         }
8420                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8421                     }
8422
8423                 );
8424                 */
8425
8426
8427                 return;
8428             }
8429
8430             Roo.callback(o.failure, o.scope, [this, action]);
8431             // show an error message if no failed handler is set..
8432             if (!this.hasListener('actionfailed')) {
8433                 Roo.log("need to add dialog support");
8434                 /*
8435                 Roo.MessageBox.alert("Error",
8436                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8437                         action.result.errorMsg :
8438                         "Saving Failed, please check your entries or try again"
8439                 );
8440                 */
8441             }
8442
8443             this.fireEvent('actionfailed', this, action);
8444         }
8445
8446     },
8447     /**
8448      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8449      * @param {String} id The value to search for
8450      * @return Field
8451      */
8452     findField : function(id){
8453         var items = this.getItems();
8454         var field = items.get(id);
8455         if(!field){
8456              items.each(function(f){
8457                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8458                     field = f;
8459                     return false;
8460                 }
8461                 return true;
8462             });
8463         }
8464         return field || null;
8465     },
8466      /**
8467      * Mark fields in this form invalid in bulk.
8468      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8469      * @return {BasicForm} this
8470      */
8471     markInvalid : function(errors){
8472         if(errors instanceof Array){
8473             for(var i = 0, len = errors.length; i < len; i++){
8474                 var fieldError = errors[i];
8475                 var f = this.findField(fieldError.id);
8476                 if(f){
8477                     f.markInvalid(fieldError.msg);
8478                 }
8479             }
8480         }else{
8481             var field, id;
8482             for(id in errors){
8483                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8484                     field.markInvalid(errors[id]);
8485                 }
8486             }
8487         }
8488         //Roo.each(this.childForms || [], function (f) {
8489         //    f.markInvalid(errors);
8490         //});
8491
8492         return this;
8493     },
8494
8495     /**
8496      * Set values for fields in this form in bulk.
8497      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8498      * @return {BasicForm} this
8499      */
8500     setValues : function(values){
8501         if(values instanceof Array){ // array of objects
8502             for(var i = 0, len = values.length; i < len; i++){
8503                 var v = values[i];
8504                 var f = this.findField(v.id);
8505                 if(f){
8506                     f.setValue(v.value);
8507                     if(this.trackResetOnLoad){
8508                         f.originalValue = f.getValue();
8509                     }
8510                 }
8511             }
8512         }else{ // object hash
8513             var field, id;
8514             for(id in values){
8515                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8516
8517                     if (field.setFromData &&
8518                         field.valueField &&
8519                         field.displayField &&
8520                         // combos' with local stores can
8521                         // be queried via setValue()
8522                         // to set their value..
8523                         (field.store && !field.store.isLocal)
8524                         ) {
8525                         // it's a combo
8526                         var sd = { };
8527                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8528                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8529                         field.setFromData(sd);
8530
8531                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8532                         
8533                         field.setFromData(values);
8534                         
8535                     } else {
8536                         field.setValue(values[id]);
8537                     }
8538
8539
8540                     if(this.trackResetOnLoad){
8541                         field.originalValue = field.getValue();
8542                     }
8543                 }
8544             }
8545         }
8546
8547         //Roo.each(this.childForms || [], function (f) {
8548         //    f.setValues(values);
8549         //});
8550
8551         return this;
8552     },
8553
8554     /**
8555      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8556      * they are returned as an array.
8557      * @param {Boolean} asString
8558      * @return {Object}
8559      */
8560     getValues : function(asString){
8561         //if (this.childForms) {
8562             // copy values from the child forms
8563         //    Roo.each(this.childForms, function (f) {
8564         //        this.setValues(f.getValues());
8565         //    }, this);
8566         //}
8567
8568
8569
8570         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8571         if(asString === true){
8572             return fs;
8573         }
8574         return Roo.urlDecode(fs);
8575     },
8576
8577     /**
8578      * Returns the fields in this form as an object with key/value pairs.
8579      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8580      * @return {Object}
8581      */
8582     getFieldValues : function(with_hidden)
8583     {
8584         var items = this.getItems();
8585         var ret = {};
8586         items.each(function(f){
8587             
8588             if (!f.getName()) {
8589                 return;
8590             }
8591             
8592             var v = f.getValue();
8593             
8594             if (f.inputType =='radio') {
8595                 if (typeof(ret[f.getName()]) == 'undefined') {
8596                     ret[f.getName()] = ''; // empty..
8597                 }
8598
8599                 if (!f.el.dom.checked) {
8600                     return;
8601
8602                 }
8603                 v = f.el.dom.value;
8604
8605             }
8606             
8607             if(f.xtype == 'MoneyField'){
8608                 ret[f.currencyName] = f.getCurrency();
8609             }
8610
8611             // not sure if this supported any more..
8612             if ((typeof(v) == 'object') && f.getRawValue) {
8613                 v = f.getRawValue() ; // dates..
8614             }
8615             // combo boxes where name != hiddenName...
8616             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8617                 ret[f.name] = f.getRawValue();
8618             }
8619             ret[f.getName()] = v;
8620         });
8621
8622         return ret;
8623     },
8624
8625     /**
8626      * Clears all invalid messages in this form.
8627      * @return {BasicForm} this
8628      */
8629     clearInvalid : function(){
8630         var items = this.getItems();
8631
8632         items.each(function(f){
8633            f.clearInvalid();
8634         });
8635
8636         return this;
8637     },
8638
8639     /**
8640      * Resets this form.
8641      * @return {BasicForm} this
8642      */
8643     reset : function(){
8644         var items = this.getItems();
8645         items.each(function(f){
8646             f.reset();
8647         });
8648
8649         Roo.each(this.childForms || [], function (f) {
8650             f.reset();
8651         });
8652
8653
8654         return this;
8655     },
8656     
8657     getItems : function()
8658     {
8659         var r=new Roo.util.MixedCollection(false, function(o){
8660             return o.id || (o.id = Roo.id());
8661         });
8662         var iter = function(el) {
8663             if (el.inputEl) {
8664                 r.add(el);
8665             }
8666             if (!el.items) {
8667                 return;
8668             }
8669             Roo.each(el.items,function(e) {
8670                 iter(e);
8671             });
8672         };
8673
8674         iter(this);
8675         return r;
8676     },
8677     
8678     hideFields : function(items)
8679     {
8680         Roo.each(items, function(i){
8681             
8682             var f = this.findField(i);
8683             
8684             if(!f){
8685                 return;
8686             }
8687             
8688             f.hide();
8689             
8690         }, this);
8691     },
8692     
8693     showFields : function(items)
8694     {
8695         Roo.each(items, function(i){
8696             
8697             var f = this.findField(i);
8698             
8699             if(!f){
8700                 return;
8701             }
8702             
8703             f.show();
8704             
8705         }, this);
8706     }
8707
8708 });
8709
8710 Roo.apply(Roo.bootstrap.Form, {
8711     
8712     popover : {
8713         
8714         padding : 5,
8715         
8716         isApplied : false,
8717         
8718         isMasked : false,
8719         
8720         form : false,
8721         
8722         target : false,
8723         
8724         toolTip : false,
8725         
8726         intervalID : false,
8727         
8728         maskEl : false,
8729         
8730         apply : function()
8731         {
8732             if(this.isApplied){
8733                 return;
8734             }
8735             
8736             this.maskEl = {
8737                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8738                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8739                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8740                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8741             };
8742             
8743             this.maskEl.top.enableDisplayMode("block");
8744             this.maskEl.left.enableDisplayMode("block");
8745             this.maskEl.bottom.enableDisplayMode("block");
8746             this.maskEl.right.enableDisplayMode("block");
8747             
8748             this.toolTip = new Roo.bootstrap.Tooltip({
8749                 cls : 'roo-form-error-popover',
8750                 alignment : {
8751                     'left' : ['r-l', [-2,0], 'right'],
8752                     'right' : ['l-r', [2,0], 'left'],
8753                     'bottom' : ['tl-bl', [0,2], 'top'],
8754                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8755                 }
8756             });
8757             
8758             this.toolTip.render(Roo.get(document.body));
8759
8760             this.toolTip.el.enableDisplayMode("block");
8761             
8762             Roo.get(document.body).on('click', function(){
8763                 this.unmask();
8764             }, this);
8765             
8766             Roo.get(document.body).on('touchstart', function(){
8767                 this.unmask();
8768             }, this);
8769             
8770             this.isApplied = true
8771         },
8772         
8773         mask : function(form, target)
8774         {
8775             this.form = form;
8776             
8777             this.target = target;
8778             
8779             if(!this.form.errorMask || !target.el){
8780                 return;
8781             }
8782             
8783             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8784             
8785             Roo.log(scrollable);
8786             
8787             var ot = this.target.el.calcOffsetsTo(scrollable);
8788             
8789             var scrollTo = ot[1] - this.form.maskOffset;
8790             
8791             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8792             
8793             scrollable.scrollTo('top', scrollTo);
8794             
8795             var box = this.target.el.getBox();
8796             Roo.log(box);
8797             var zIndex = Roo.bootstrap.Modal.zIndex++;
8798
8799             
8800             this.maskEl.top.setStyle('position', 'absolute');
8801             this.maskEl.top.setStyle('z-index', zIndex);
8802             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8803             this.maskEl.top.setLeft(0);
8804             this.maskEl.top.setTop(0);
8805             this.maskEl.top.show();
8806             
8807             this.maskEl.left.setStyle('position', 'absolute');
8808             this.maskEl.left.setStyle('z-index', zIndex);
8809             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8810             this.maskEl.left.setLeft(0);
8811             this.maskEl.left.setTop(box.y - this.padding);
8812             this.maskEl.left.show();
8813
8814             this.maskEl.bottom.setStyle('position', 'absolute');
8815             this.maskEl.bottom.setStyle('z-index', zIndex);
8816             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8817             this.maskEl.bottom.setLeft(0);
8818             this.maskEl.bottom.setTop(box.bottom + this.padding);
8819             this.maskEl.bottom.show();
8820
8821             this.maskEl.right.setStyle('position', 'absolute');
8822             this.maskEl.right.setStyle('z-index', zIndex);
8823             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8824             this.maskEl.right.setLeft(box.right + this.padding);
8825             this.maskEl.right.setTop(box.y - this.padding);
8826             this.maskEl.right.show();
8827
8828             this.toolTip.bindEl = this.target.el;
8829
8830             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8831
8832             var tip = this.target.blankText;
8833
8834             if(this.target.getValue() !== '' ) {
8835                 
8836                 if (this.target.invalidText.length) {
8837                     tip = this.target.invalidText;
8838                 } else if (this.target.regexText.length){
8839                     tip = this.target.regexText;
8840                 }
8841             }
8842
8843             this.toolTip.show(tip);
8844
8845             this.intervalID = window.setInterval(function() {
8846                 Roo.bootstrap.Form.popover.unmask();
8847             }, 10000);
8848
8849             window.onwheel = function(){ return false;};
8850             
8851             (function(){ this.isMasked = true; }).defer(500, this);
8852             
8853         },
8854         
8855         unmask : function()
8856         {
8857             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8858                 return;
8859             }
8860             
8861             this.maskEl.top.setStyle('position', 'absolute');
8862             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8863             this.maskEl.top.hide();
8864
8865             this.maskEl.left.setStyle('position', 'absolute');
8866             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8867             this.maskEl.left.hide();
8868
8869             this.maskEl.bottom.setStyle('position', 'absolute');
8870             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8871             this.maskEl.bottom.hide();
8872
8873             this.maskEl.right.setStyle('position', 'absolute');
8874             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8875             this.maskEl.right.hide();
8876             
8877             this.toolTip.hide();
8878             
8879             this.toolTip.el.hide();
8880             
8881             window.onwheel = function(){ return true;};
8882             
8883             if(this.intervalID){
8884                 window.clearInterval(this.intervalID);
8885                 this.intervalID = false;
8886             }
8887             
8888             this.isMasked = false;
8889             
8890         }
8891         
8892     }
8893     
8894 });
8895
8896 /*
8897  * Based on:
8898  * Ext JS Library 1.1.1
8899  * Copyright(c) 2006-2007, Ext JS, LLC.
8900  *
8901  * Originally Released Under LGPL - original licence link has changed is not relivant.
8902  *
8903  * Fork - LGPL
8904  * <script type="text/javascript">
8905  */
8906 /**
8907  * @class Roo.form.VTypes
8908  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8909  * @singleton
8910  */
8911 Roo.form.VTypes = function(){
8912     // closure these in so they are only created once.
8913     var alpha = /^[a-zA-Z_]+$/;
8914     var alphanum = /^[a-zA-Z0-9_]+$/;
8915     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8916     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8917
8918     // All these messages and functions are configurable
8919     return {
8920         /**
8921          * The function used to validate email addresses
8922          * @param {String} value The email address
8923          */
8924         'email' : function(v){
8925             return email.test(v);
8926         },
8927         /**
8928          * The error text to display when the email validation function returns false
8929          * @type String
8930          */
8931         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8932         /**
8933          * The keystroke filter mask to be applied on email input
8934          * @type RegExp
8935          */
8936         'emailMask' : /[a-z0-9_\.\-@]/i,
8937
8938         /**
8939          * The function used to validate URLs
8940          * @param {String} value The URL
8941          */
8942         'url' : function(v){
8943             return url.test(v);
8944         },
8945         /**
8946          * The error text to display when the url validation function returns false
8947          * @type String
8948          */
8949         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8950         
8951         /**
8952          * The function used to validate alpha values
8953          * @param {String} value The value
8954          */
8955         'alpha' : function(v){
8956             return alpha.test(v);
8957         },
8958         /**
8959          * The error text to display when the alpha validation function returns false
8960          * @type String
8961          */
8962         'alphaText' : 'This field should only contain letters and _',
8963         /**
8964          * The keystroke filter mask to be applied on alpha input
8965          * @type RegExp
8966          */
8967         'alphaMask' : /[a-z_]/i,
8968
8969         /**
8970          * The function used to validate alphanumeric values
8971          * @param {String} value The value
8972          */
8973         'alphanum' : function(v){
8974             return alphanum.test(v);
8975         },
8976         /**
8977          * The error text to display when the alphanumeric validation function returns false
8978          * @type String
8979          */
8980         'alphanumText' : 'This field should only contain letters, numbers and _',
8981         /**
8982          * The keystroke filter mask to be applied on alphanumeric input
8983          * @type RegExp
8984          */
8985         'alphanumMask' : /[a-z0-9_]/i
8986     };
8987 }();/*
8988  * - LGPL
8989  *
8990  * Input
8991  * 
8992  */
8993
8994 /**
8995  * @class Roo.bootstrap.Input
8996  * @extends Roo.bootstrap.Component
8997  * Bootstrap Input class
8998  * @cfg {Boolean} disabled is it disabled
8999  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9000  * @cfg {String} name name of the input
9001  * @cfg {string} fieldLabel - the label associated
9002  * @cfg {string} placeholder - placeholder to put in text.
9003  * @cfg {string}  before - input group add on before
9004  * @cfg {string} after - input group add on after
9005  * @cfg {string} size - (lg|sm) or leave empty..
9006  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9007  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9008  * @cfg {Number} md colspan out of 12 for computer-sized screens
9009  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9010  * @cfg {string} value default value of the input
9011  * @cfg {Number} labelWidth set the width of label 
9012  * @cfg {Number} labellg set the width of label (1-12)
9013  * @cfg {Number} labelmd set the width of label (1-12)
9014  * @cfg {Number} labelsm set the width of label (1-12)
9015  * @cfg {Number} labelxs set the width of label (1-12)
9016  * @cfg {String} labelAlign (top|left)
9017  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9018  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9019  * @cfg {String} indicatorpos (left|right) default left
9020  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9021  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9022
9023  * @cfg {String} align (left|center|right) Default left
9024  * @cfg {Boolean} forceFeedback (true|false) Default false
9025  * 
9026  * @constructor
9027  * Create a new Input
9028  * @param {Object} config The config object
9029  */
9030
9031 Roo.bootstrap.Input = function(config){
9032     
9033     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9034     
9035     this.addEvents({
9036         /**
9037          * @event focus
9038          * Fires when this field receives input focus.
9039          * @param {Roo.form.Field} this
9040          */
9041         focus : true,
9042         /**
9043          * @event blur
9044          * Fires when this field loses input focus.
9045          * @param {Roo.form.Field} this
9046          */
9047         blur : true,
9048         /**
9049          * @event specialkey
9050          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9051          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9052          * @param {Roo.form.Field} this
9053          * @param {Roo.EventObject} e The event object
9054          */
9055         specialkey : true,
9056         /**
9057          * @event change
9058          * Fires just before the field blurs if the field value has changed.
9059          * @param {Roo.form.Field} this
9060          * @param {Mixed} newValue The new value
9061          * @param {Mixed} oldValue The original value
9062          */
9063         change : true,
9064         /**
9065          * @event invalid
9066          * Fires after the field has been marked as invalid.
9067          * @param {Roo.form.Field} this
9068          * @param {String} msg The validation message
9069          */
9070         invalid : true,
9071         /**
9072          * @event valid
9073          * Fires after the field has been validated with no errors.
9074          * @param {Roo.form.Field} this
9075          */
9076         valid : true,
9077          /**
9078          * @event keyup
9079          * Fires after the key up
9080          * @param {Roo.form.Field} this
9081          * @param {Roo.EventObject}  e The event Object
9082          */
9083         keyup : true
9084     });
9085 };
9086
9087 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9088      /**
9089      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9090       automatic validation (defaults to "keyup").
9091      */
9092     validationEvent : "keyup",
9093      /**
9094      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9095      */
9096     validateOnBlur : true,
9097     /**
9098      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9099      */
9100     validationDelay : 250,
9101      /**
9102      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9103      */
9104     focusClass : "x-form-focus",  // not needed???
9105     
9106        
9107     /**
9108      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9109      */
9110     invalidClass : "has-warning",
9111     
9112     /**
9113      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9114      */
9115     validClass : "has-success",
9116     
9117     /**
9118      * @cfg {Boolean} hasFeedback (true|false) default true
9119      */
9120     hasFeedback : true,
9121     
9122     /**
9123      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9124      */
9125     invalidFeedbackClass : "glyphicon-warning-sign",
9126     
9127     /**
9128      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9129      */
9130     validFeedbackClass : "glyphicon-ok",
9131     
9132     /**
9133      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9134      */
9135     selectOnFocus : false,
9136     
9137      /**
9138      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9139      */
9140     maskRe : null,
9141        /**
9142      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9143      */
9144     vtype : null,
9145     
9146       /**
9147      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9148      */
9149     disableKeyFilter : false,
9150     
9151        /**
9152      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9153      */
9154     disabled : false,
9155      /**
9156      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9157      */
9158     allowBlank : true,
9159     /**
9160      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9161      */
9162     blankText : "Please complete this mandatory field",
9163     
9164      /**
9165      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9166      */
9167     minLength : 0,
9168     /**
9169      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9170      */
9171     maxLength : Number.MAX_VALUE,
9172     /**
9173      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9174      */
9175     minLengthText : "The minimum length for this field is {0}",
9176     /**
9177      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9178      */
9179     maxLengthText : "The maximum length for this field is {0}",
9180   
9181     
9182     /**
9183      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9184      * If available, this function will be called only after the basic validators all return true, and will be passed the
9185      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9186      */
9187     validator : null,
9188     /**
9189      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9190      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9191      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9192      */
9193     regex : null,
9194     /**
9195      * @cfg {String} regexText -- Depricated - use Invalid Text
9196      */
9197     regexText : "",
9198     
9199     /**
9200      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9201      */
9202     invalidText : "",
9203     
9204     
9205     
9206     autocomplete: false,
9207     
9208     
9209     fieldLabel : '',
9210     inputType : 'text',
9211     
9212     name : false,
9213     placeholder: false,
9214     before : false,
9215     after : false,
9216     size : false,
9217     hasFocus : false,
9218     preventMark: false,
9219     isFormField : true,
9220     value : '',
9221     labelWidth : 2,
9222     labelAlign : false,
9223     readOnly : false,
9224     align : false,
9225     formatedValue : false,
9226     forceFeedback : false,
9227     
9228     indicatorpos : 'left',
9229     
9230     labellg : 0,
9231     labelmd : 0,
9232     labelsm : 0,
9233     labelxs : 0,
9234     
9235     capture : '',
9236     accept : '',
9237     
9238     parentLabelAlign : function()
9239     {
9240         var parent = this;
9241         while (parent.parent()) {
9242             parent = parent.parent();
9243             if (typeof(parent.labelAlign) !='undefined') {
9244                 return parent.labelAlign;
9245             }
9246         }
9247         return 'left';
9248         
9249     },
9250     
9251     getAutoCreate : function()
9252     {
9253         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9254         
9255         var id = Roo.id();
9256         
9257         var cfg = {};
9258         
9259         if(this.inputType != 'hidden'){
9260             cfg.cls = 'form-group' //input-group
9261         }
9262         
9263         var input =  {
9264             tag: 'input',
9265             id : id,
9266             type : this.inputType,
9267             value : this.value,
9268             cls : 'form-control',
9269             placeholder : this.placeholder || '',
9270             autocomplete : this.autocomplete || 'new-password'
9271         };
9272         
9273         if(this.capture.length){
9274             input.capture = this.capture;
9275         }
9276         
9277         if(this.accept.length){
9278             input.accept = this.accept + "/*";
9279         }
9280         
9281         if(this.align){
9282             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9283         }
9284         
9285         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9286             input.maxLength = this.maxLength;
9287         }
9288         
9289         if (this.disabled) {
9290             input.disabled=true;
9291         }
9292         
9293         if (this.readOnly) {
9294             input.readonly=true;
9295         }
9296         
9297         if (this.name) {
9298             input.name = this.name;
9299         }
9300         
9301         if (this.size) {
9302             input.cls += ' input-' + this.size;
9303         }
9304         
9305         var settings=this;
9306         ['xs','sm','md','lg'].map(function(size){
9307             if (settings[size]) {
9308                 cfg.cls += ' col-' + size + '-' + settings[size];
9309             }
9310         });
9311         
9312         var inputblock = input;
9313         
9314         var feedback = {
9315             tag: 'span',
9316             cls: 'glyphicon form-control-feedback'
9317         };
9318             
9319         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9320             
9321             inputblock = {
9322                 cls : 'has-feedback',
9323                 cn :  [
9324                     input,
9325                     feedback
9326                 ] 
9327             };  
9328         }
9329         
9330         if (this.before || this.after) {
9331             
9332             inputblock = {
9333                 cls : 'input-group',
9334                 cn :  [] 
9335             };
9336             
9337             if (this.before && typeof(this.before) == 'string') {
9338                 
9339                 inputblock.cn.push({
9340                     tag :'span',
9341                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9342                     html : this.before
9343                 });
9344             }
9345             if (this.before && typeof(this.before) == 'object') {
9346                 this.before = Roo.factory(this.before);
9347                 
9348                 inputblock.cn.push({
9349                     tag :'span',
9350                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9351                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9352                 });
9353             }
9354             
9355             inputblock.cn.push(input);
9356             
9357             if (this.after && typeof(this.after) == 'string') {
9358                 inputblock.cn.push({
9359                     tag :'span',
9360                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9361                     html : this.after
9362                 });
9363             }
9364             if (this.after && typeof(this.after) == 'object') {
9365                 this.after = Roo.factory(this.after);
9366                 
9367                 inputblock.cn.push({
9368                     tag :'span',
9369                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9370                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9371                 });
9372             }
9373             
9374             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9375                 inputblock.cls += ' has-feedback';
9376                 inputblock.cn.push(feedback);
9377             }
9378         };
9379         var indicator = {
9380             tag : 'i',
9381             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9382             tooltip : 'This field is required'
9383         };
9384         if (Roo.bootstrap.version == 4) {
9385             indicator = {
9386                 tag : 'i',
9387                 style : 'display-none'
9388             };
9389         }
9390         if (align ==='left' && this.fieldLabel.length) {
9391             
9392             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9393             
9394             cfg.cn = [
9395                 indicator,
9396                 {
9397                     tag: 'label',
9398                     'for' :  id,
9399                     cls : 'control-label col-form-label',
9400                     html : this.fieldLabel
9401
9402                 },
9403                 {
9404                     cls : "", 
9405                     cn: [
9406                         inputblock
9407                     ]
9408                 }
9409             ];
9410             
9411             var labelCfg = cfg.cn[1];
9412             var contentCfg = cfg.cn[2];
9413             
9414             if(this.indicatorpos == 'right'){
9415                 cfg.cn = [
9416                     {
9417                         tag: 'label',
9418                         'for' :  id,
9419                         cls : 'control-label col-form-label',
9420                         cn : [
9421                             {
9422                                 tag : 'span',
9423                                 html : this.fieldLabel
9424                             },
9425                             indicator
9426                         ]
9427                     },
9428                     {
9429                         cls : "",
9430                         cn: [
9431                             inputblock
9432                         ]
9433                     }
9434
9435                 ];
9436                 
9437                 labelCfg = cfg.cn[0];
9438                 contentCfg = cfg.cn[1];
9439             
9440             }
9441             
9442             if(this.labelWidth > 12){
9443                 labelCfg.style = "width: " + this.labelWidth + 'px';
9444             }
9445             
9446             if(this.labelWidth < 13 && this.labelmd == 0){
9447                 this.labelmd = this.labelWidth;
9448             }
9449             
9450             if(this.labellg > 0){
9451                 labelCfg.cls += ' col-lg-' + this.labellg;
9452                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9453             }
9454             
9455             if(this.labelmd > 0){
9456                 labelCfg.cls += ' col-md-' + this.labelmd;
9457                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9458             }
9459             
9460             if(this.labelsm > 0){
9461                 labelCfg.cls += ' col-sm-' + this.labelsm;
9462                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9463             }
9464             
9465             if(this.labelxs > 0){
9466                 labelCfg.cls += ' col-xs-' + this.labelxs;
9467                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9468             }
9469             
9470             
9471         } else if ( this.fieldLabel.length) {
9472                 
9473             cfg.cn = [
9474                 {
9475                     tag : 'i',
9476                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9477                     tooltip : 'This field is required'
9478                 },
9479                 {
9480                     tag: 'label',
9481                    //cls : 'input-group-addon',
9482                     html : this.fieldLabel
9483
9484                 },
9485
9486                inputblock
9487
9488            ];
9489            
9490            if(this.indicatorpos == 'right'){
9491                 
9492                 cfg.cn = [
9493                     {
9494                         tag: 'label',
9495                        //cls : 'input-group-addon',
9496                         html : this.fieldLabel
9497
9498                     },
9499                     {
9500                         tag : 'i',
9501                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9502                         tooltip : 'This field is required'
9503                     },
9504
9505                    inputblock
9506
9507                ];
9508
9509             }
9510
9511         } else {
9512             
9513             cfg.cn = [
9514
9515                     inputblock
9516
9517             ];
9518                 
9519                 
9520         };
9521         
9522         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9523            cfg.cls += ' navbar-form';
9524         }
9525         
9526         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9527             // on BS4 we do this only if not form 
9528             cfg.cls += ' navbar-form';
9529             cfg.tag = 'li';
9530         }
9531         
9532         return cfg;
9533         
9534     },
9535     /**
9536      * return the real input element.
9537      */
9538     inputEl: function ()
9539     {
9540         return this.el.select('input.form-control',true).first();
9541     },
9542     
9543     tooltipEl : function()
9544     {
9545         return this.inputEl();
9546     },
9547     
9548     indicatorEl : function()
9549     {
9550         if (Roo.bootstrap.version == 4) {
9551             return false; // not enabled in v4 yet.
9552         }
9553         
9554         var indicator = this.el.select('i.roo-required-indicator',true).first();
9555         
9556         if(!indicator){
9557             return false;
9558         }
9559         
9560         return indicator;
9561         
9562     },
9563     
9564     setDisabled : function(v)
9565     {
9566         var i  = this.inputEl().dom;
9567         if (!v) {
9568             i.removeAttribute('disabled');
9569             return;
9570             
9571         }
9572         i.setAttribute('disabled','true');
9573     },
9574     initEvents : function()
9575     {
9576           
9577         this.inputEl().on("keydown" , this.fireKey,  this);
9578         this.inputEl().on("focus", this.onFocus,  this);
9579         this.inputEl().on("blur", this.onBlur,  this);
9580         
9581         this.inputEl().relayEvent('keyup', this);
9582         
9583         this.indicator = this.indicatorEl();
9584         
9585         if(this.indicator){
9586             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9587         }
9588  
9589         // reference to original value for reset
9590         this.originalValue = this.getValue();
9591         //Roo.form.TextField.superclass.initEvents.call(this);
9592         if(this.validationEvent == 'keyup'){
9593             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9594             this.inputEl().on('keyup', this.filterValidation, this);
9595         }
9596         else if(this.validationEvent !== false){
9597             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9598         }
9599         
9600         if(this.selectOnFocus){
9601             this.on("focus", this.preFocus, this);
9602             
9603         }
9604         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9605             this.inputEl().on("keypress", this.filterKeys, this);
9606         } else {
9607             this.inputEl().relayEvent('keypress', this);
9608         }
9609        /* if(this.grow){
9610             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9611             this.el.on("click", this.autoSize,  this);
9612         }
9613         */
9614         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9615             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9616         }
9617         
9618         if (typeof(this.before) == 'object') {
9619             this.before.render(this.el.select('.roo-input-before',true).first());
9620         }
9621         if (typeof(this.after) == 'object') {
9622             this.after.render(this.el.select('.roo-input-after',true).first());
9623         }
9624         
9625         this.inputEl().on('change', this.onChange, this);
9626         
9627     },
9628     filterValidation : function(e){
9629         if(!e.isNavKeyPress()){
9630             this.validationTask.delay(this.validationDelay);
9631         }
9632     },
9633      /**
9634      * Validates the field value
9635      * @return {Boolean} True if the value is valid, else false
9636      */
9637     validate : function(){
9638         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9639         if(this.disabled || this.validateValue(this.getRawValue())){
9640             this.markValid();
9641             return true;
9642         }
9643         
9644         this.markInvalid();
9645         return false;
9646     },
9647     
9648     
9649     /**
9650      * Validates a value according to the field's validation rules and marks the field as invalid
9651      * if the validation fails
9652      * @param {Mixed} value The value to validate
9653      * @return {Boolean} True if the value is valid, else false
9654      */
9655     validateValue : function(value)
9656     {
9657         if(this.getVisibilityEl().hasClass('hidden')){
9658             return true;
9659         }
9660         
9661         if(value.length < 1)  { // if it's blank
9662             if(this.allowBlank){
9663                 return true;
9664             }
9665             return false;
9666         }
9667         
9668         if(value.length < this.minLength){
9669             return false;
9670         }
9671         if(value.length > this.maxLength){
9672             return false;
9673         }
9674         if(this.vtype){
9675             var vt = Roo.form.VTypes;
9676             if(!vt[this.vtype](value, this)){
9677                 return false;
9678             }
9679         }
9680         if(typeof this.validator == "function"){
9681             var msg = this.validator(value);
9682             if(msg !== true){
9683                 return false;
9684             }
9685             if (typeof(msg) == 'string') {
9686                 this.invalidText = msg;
9687             }
9688         }
9689         
9690         if(this.regex && !this.regex.test(value)){
9691             return false;
9692         }
9693         
9694         return true;
9695     },
9696     
9697      // private
9698     fireKey : function(e){
9699         //Roo.log('field ' + e.getKey());
9700         if(e.isNavKeyPress()){
9701             this.fireEvent("specialkey", this, e);
9702         }
9703     },
9704     focus : function (selectText){
9705         if(this.rendered){
9706             this.inputEl().focus();
9707             if(selectText === true){
9708                 this.inputEl().dom.select();
9709             }
9710         }
9711         return this;
9712     } ,
9713     
9714     onFocus : function(){
9715         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9716            // this.el.addClass(this.focusClass);
9717         }
9718         if(!this.hasFocus){
9719             this.hasFocus = true;
9720             this.startValue = this.getValue();
9721             this.fireEvent("focus", this);
9722         }
9723     },
9724     
9725     beforeBlur : Roo.emptyFn,
9726
9727     
9728     // private
9729     onBlur : function(){
9730         this.beforeBlur();
9731         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9732             //this.el.removeClass(this.focusClass);
9733         }
9734         this.hasFocus = false;
9735         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9736             this.validate();
9737         }
9738         var v = this.getValue();
9739         if(String(v) !== String(this.startValue)){
9740             this.fireEvent('change', this, v, this.startValue);
9741         }
9742         this.fireEvent("blur", this);
9743     },
9744     
9745     onChange : function(e)
9746     {
9747         var v = this.getValue();
9748         if(String(v) !== String(this.startValue)){
9749             this.fireEvent('change', this, v, this.startValue);
9750         }
9751         
9752     },
9753     
9754     /**
9755      * Resets the current field value to the originally loaded value and clears any validation messages
9756      */
9757     reset : function(){
9758         this.setValue(this.originalValue);
9759         this.validate();
9760     },
9761      /**
9762      * Returns the name of the field
9763      * @return {Mixed} name The name field
9764      */
9765     getName: function(){
9766         return this.name;
9767     },
9768      /**
9769      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9770      * @return {Mixed} value The field value
9771      */
9772     getValue : function(){
9773         
9774         var v = this.inputEl().getValue();
9775         
9776         return v;
9777     },
9778     /**
9779      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9780      * @return {Mixed} value The field value
9781      */
9782     getRawValue : function(){
9783         var v = this.inputEl().getValue();
9784         
9785         return v;
9786     },
9787     
9788     /**
9789      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9790      * @param {Mixed} value The value to set
9791      */
9792     setRawValue : function(v){
9793         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9794     },
9795     
9796     selectText : function(start, end){
9797         var v = this.getRawValue();
9798         if(v.length > 0){
9799             start = start === undefined ? 0 : start;
9800             end = end === undefined ? v.length : end;
9801             var d = this.inputEl().dom;
9802             if(d.setSelectionRange){
9803                 d.setSelectionRange(start, end);
9804             }else if(d.createTextRange){
9805                 var range = d.createTextRange();
9806                 range.moveStart("character", start);
9807                 range.moveEnd("character", v.length-end);
9808                 range.select();
9809             }
9810         }
9811     },
9812     
9813     /**
9814      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9815      * @param {Mixed} value The value to set
9816      */
9817     setValue : function(v){
9818         this.value = v;
9819         if(this.rendered){
9820             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9821             this.validate();
9822         }
9823     },
9824     
9825     /*
9826     processValue : function(value){
9827         if(this.stripCharsRe){
9828             var newValue = value.replace(this.stripCharsRe, '');
9829             if(newValue !== value){
9830                 this.setRawValue(newValue);
9831                 return newValue;
9832             }
9833         }
9834         return value;
9835     },
9836   */
9837     preFocus : function(){
9838         
9839         if(this.selectOnFocus){
9840             this.inputEl().dom.select();
9841         }
9842     },
9843     filterKeys : function(e){
9844         var k = e.getKey();
9845         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9846             return;
9847         }
9848         var c = e.getCharCode(), cc = String.fromCharCode(c);
9849         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9850             return;
9851         }
9852         if(!this.maskRe.test(cc)){
9853             e.stopEvent();
9854         }
9855     },
9856      /**
9857      * Clear any invalid styles/messages for this field
9858      */
9859     clearInvalid : function(){
9860         
9861         if(!this.el || this.preventMark){ // not rendered
9862             return;
9863         }
9864         
9865         
9866         this.el.removeClass([this.invalidClass, 'is-invalid']);
9867         
9868         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9869             
9870             var feedback = this.el.select('.form-control-feedback', true).first();
9871             
9872             if(feedback){
9873                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9874             }
9875             
9876         }
9877         
9878         if(this.indicator){
9879             this.indicator.removeClass('visible');
9880             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9881         }
9882         
9883         this.fireEvent('valid', this);
9884     },
9885     
9886      /**
9887      * Mark this field as valid
9888      */
9889     markValid : function()
9890     {
9891         if(!this.el  || this.preventMark){ // not rendered...
9892             return;
9893         }
9894         
9895         this.el.removeClass([this.invalidClass, this.validClass]);
9896         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9897
9898         var feedback = this.el.select('.form-control-feedback', true).first();
9899             
9900         if(feedback){
9901             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9902         }
9903         
9904         if(this.indicator){
9905             this.indicator.removeClass('visible');
9906             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9907         }
9908         
9909         if(this.disabled){
9910             return;
9911         }
9912         
9913         if(this.allowBlank && !this.getRawValue().length){
9914             return;
9915         }
9916         if (Roo.bootstrap.version == 3) {
9917             this.el.addClass(this.validClass);
9918         } else {
9919             this.inputEl().addClass('is-valid');
9920         }
9921
9922         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9923             
9924             var feedback = this.el.select('.form-control-feedback', true).first();
9925             
9926             if(feedback){
9927                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9928                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9929             }
9930             
9931         }
9932         
9933         this.fireEvent('valid', this);
9934     },
9935     
9936      /**
9937      * Mark this field as invalid
9938      * @param {String} msg The validation message
9939      */
9940     markInvalid : function(msg)
9941     {
9942         if(!this.el  || this.preventMark){ // not rendered
9943             return;
9944         }
9945         
9946         this.el.removeClass([this.invalidClass, this.validClass]);
9947         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9948         
9949         var feedback = this.el.select('.form-control-feedback', true).first();
9950             
9951         if(feedback){
9952             this.el.select('.form-control-feedback', true).first().removeClass(
9953                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9954         }
9955
9956         if(this.disabled){
9957             return;
9958         }
9959         
9960         if(this.allowBlank && !this.getRawValue().length){
9961             return;
9962         }
9963         
9964         if(this.indicator){
9965             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9966             this.indicator.addClass('visible');
9967         }
9968         if (Roo.bootstrap.version == 3) {
9969             this.el.addClass(this.invalidClass);
9970         } else {
9971             this.inputEl().addClass('is-invalid');
9972         }
9973         
9974         
9975         
9976         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9977             
9978             var feedback = this.el.select('.form-control-feedback', true).first();
9979             
9980             if(feedback){
9981                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9982                 
9983                 if(this.getValue().length || this.forceFeedback){
9984                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9985                 }
9986                 
9987             }
9988             
9989         }
9990         
9991         this.fireEvent('invalid', this, msg);
9992     },
9993     // private
9994     SafariOnKeyDown : function(event)
9995     {
9996         // this is a workaround for a password hang bug on chrome/ webkit.
9997         if (this.inputEl().dom.type != 'password') {
9998             return;
9999         }
10000         
10001         var isSelectAll = false;
10002         
10003         if(this.inputEl().dom.selectionEnd > 0){
10004             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10005         }
10006         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10007             event.preventDefault();
10008             this.setValue('');
10009             return;
10010         }
10011         
10012         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10013             
10014             event.preventDefault();
10015             // this is very hacky as keydown always get's upper case.
10016             //
10017             var cc = String.fromCharCode(event.getCharCode());
10018             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10019             
10020         }
10021     },
10022     adjustWidth : function(tag, w){
10023         tag = tag.toLowerCase();
10024         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10025             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10026                 if(tag == 'input'){
10027                     return w + 2;
10028                 }
10029                 if(tag == 'textarea'){
10030                     return w-2;
10031                 }
10032             }else if(Roo.isOpera){
10033                 if(tag == 'input'){
10034                     return w + 2;
10035                 }
10036                 if(tag == 'textarea'){
10037                     return w-2;
10038                 }
10039             }
10040         }
10041         return w;
10042     },
10043     
10044     setFieldLabel : function(v)
10045     {
10046         if(!this.rendered){
10047             return;
10048         }
10049         
10050         if(this.indicatorEl()){
10051             var ar = this.el.select('label > span',true);
10052             
10053             if (ar.elements.length) {
10054                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10055                 this.fieldLabel = v;
10056                 return;
10057             }
10058             
10059             var br = this.el.select('label',true);
10060             
10061             if(br.elements.length) {
10062                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10063                 this.fieldLabel = v;
10064                 return;
10065             }
10066             
10067             Roo.log('Cannot Found any of label > span || label in input');
10068             return;
10069         }
10070         
10071         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10072         this.fieldLabel = v;
10073         
10074         
10075     }
10076 });
10077
10078  
10079 /*
10080  * - LGPL
10081  *
10082  * Input
10083  * 
10084  */
10085
10086 /**
10087  * @class Roo.bootstrap.TextArea
10088  * @extends Roo.bootstrap.Input
10089  * Bootstrap TextArea class
10090  * @cfg {Number} cols Specifies the visible width of a text area
10091  * @cfg {Number} rows Specifies the visible number of lines in a text area
10092  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10093  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10094  * @cfg {string} html text
10095  * 
10096  * @constructor
10097  * Create a new TextArea
10098  * @param {Object} config The config object
10099  */
10100
10101 Roo.bootstrap.TextArea = function(config){
10102     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10103    
10104 };
10105
10106 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10107      
10108     cols : false,
10109     rows : 5,
10110     readOnly : false,
10111     warp : 'soft',
10112     resize : false,
10113     value: false,
10114     html: false,
10115     
10116     getAutoCreate : function(){
10117         
10118         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10119         
10120         var id = Roo.id();
10121         
10122         var cfg = {};
10123         
10124         if(this.inputType != 'hidden'){
10125             cfg.cls = 'form-group' //input-group
10126         }
10127         
10128         var input =  {
10129             tag: 'textarea',
10130             id : id,
10131             warp : this.warp,
10132             rows : this.rows,
10133             value : this.value || '',
10134             html: this.html || '',
10135             cls : 'form-control',
10136             placeholder : this.placeholder || '' 
10137             
10138         };
10139         
10140         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10141             input.maxLength = this.maxLength;
10142         }
10143         
10144         if(this.resize){
10145             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10146         }
10147         
10148         if(this.cols){
10149             input.cols = this.cols;
10150         }
10151         
10152         if (this.readOnly) {
10153             input.readonly = true;
10154         }
10155         
10156         if (this.name) {
10157             input.name = this.name;
10158         }
10159         
10160         if (this.size) {
10161             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10162         }
10163         
10164         var settings=this;
10165         ['xs','sm','md','lg'].map(function(size){
10166             if (settings[size]) {
10167                 cfg.cls += ' col-' + size + '-' + settings[size];
10168             }
10169         });
10170         
10171         var inputblock = input;
10172         
10173         if(this.hasFeedback && !this.allowBlank){
10174             
10175             var feedback = {
10176                 tag: 'span',
10177                 cls: 'glyphicon form-control-feedback'
10178             };
10179
10180             inputblock = {
10181                 cls : 'has-feedback',
10182                 cn :  [
10183                     input,
10184                     feedback
10185                 ] 
10186             };  
10187         }
10188         
10189         
10190         if (this.before || this.after) {
10191             
10192             inputblock = {
10193                 cls : 'input-group',
10194                 cn :  [] 
10195             };
10196             if (this.before) {
10197                 inputblock.cn.push({
10198                     tag :'span',
10199                     cls : 'input-group-addon',
10200                     html : this.before
10201                 });
10202             }
10203             
10204             inputblock.cn.push(input);
10205             
10206             if(this.hasFeedback && !this.allowBlank){
10207                 inputblock.cls += ' has-feedback';
10208                 inputblock.cn.push(feedback);
10209             }
10210             
10211             if (this.after) {
10212                 inputblock.cn.push({
10213                     tag :'span',
10214                     cls : 'input-group-addon',
10215                     html : this.after
10216                 });
10217             }
10218             
10219         }
10220         
10221         if (align ==='left' && this.fieldLabel.length) {
10222             cfg.cn = [
10223                 {
10224                     tag: 'label',
10225                     'for' :  id,
10226                     cls : 'control-label',
10227                     html : this.fieldLabel
10228                 },
10229                 {
10230                     cls : "",
10231                     cn: [
10232                         inputblock
10233                     ]
10234                 }
10235
10236             ];
10237             
10238             if(this.labelWidth > 12){
10239                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10240             }
10241
10242             if(this.labelWidth < 13 && this.labelmd == 0){
10243                 this.labelmd = this.labelWidth;
10244             }
10245
10246             if(this.labellg > 0){
10247                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10248                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10249             }
10250
10251             if(this.labelmd > 0){
10252                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10253                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10254             }
10255
10256             if(this.labelsm > 0){
10257                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10258                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10259             }
10260
10261             if(this.labelxs > 0){
10262                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10263                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10264             }
10265             
10266         } else if ( this.fieldLabel.length) {
10267             cfg.cn = [
10268
10269                {
10270                    tag: 'label',
10271                    //cls : 'input-group-addon',
10272                    html : this.fieldLabel
10273
10274                },
10275
10276                inputblock
10277
10278            ];
10279
10280         } else {
10281
10282             cfg.cn = [
10283
10284                 inputblock
10285
10286             ];
10287                 
10288         }
10289         
10290         if (this.disabled) {
10291             input.disabled=true;
10292         }
10293         
10294         return cfg;
10295         
10296     },
10297     /**
10298      * return the real textarea element.
10299      */
10300     inputEl: function ()
10301     {
10302         return this.el.select('textarea.form-control',true).first();
10303     },
10304     
10305     /**
10306      * Clear any invalid styles/messages for this field
10307      */
10308     clearInvalid : function()
10309     {
10310         
10311         if(!this.el || this.preventMark){ // not rendered
10312             return;
10313         }
10314         
10315         var label = this.el.select('label', true).first();
10316         var icon = this.el.select('i.fa-star', true).first();
10317         
10318         if(label && icon){
10319             icon.remove();
10320         }
10321         this.el.removeClass( this.validClass);
10322         this.inputEl().removeClass('is-invalid');
10323          
10324         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10325             
10326             var feedback = this.el.select('.form-control-feedback', true).first();
10327             
10328             if(feedback){
10329                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10330             }
10331             
10332         }
10333         
10334         this.fireEvent('valid', this);
10335     },
10336     
10337      /**
10338      * Mark this field as valid
10339      */
10340     markValid : function()
10341     {
10342         if(!this.el  || this.preventMark){ // not rendered
10343             return;
10344         }
10345         
10346         this.el.removeClass([this.invalidClass, this.validClass]);
10347         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10348         
10349         var feedback = this.el.select('.form-control-feedback', true).first();
10350             
10351         if(feedback){
10352             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10353         }
10354
10355         if(this.disabled || this.allowBlank){
10356             return;
10357         }
10358         
10359         var label = this.el.select('label', true).first();
10360         var icon = this.el.select('i.fa-star', true).first();
10361         
10362         if(label && icon){
10363             icon.remove();
10364         }
10365         if (Roo.bootstrap.version == 3) {
10366             this.el.addClass(this.validClass);
10367         } else {
10368             this.inputEl().addClass('is-valid');
10369         }
10370         
10371         
10372         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10373             
10374             var feedback = this.el.select('.form-control-feedback', true).first();
10375             
10376             if(feedback){
10377                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10378                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10379             }
10380             
10381         }
10382         
10383         this.fireEvent('valid', this);
10384     },
10385     
10386      /**
10387      * Mark this field as invalid
10388      * @param {String} msg The validation message
10389      */
10390     markInvalid : function(msg)
10391     {
10392         if(!this.el  || this.preventMark){ // not rendered
10393             return;
10394         }
10395         
10396         this.el.removeClass([this.invalidClass, this.validClass]);
10397         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10398         
10399         var feedback = this.el.select('.form-control-feedback', true).first();
10400             
10401         if(feedback){
10402             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10403         }
10404
10405         if(this.disabled || this.allowBlank){
10406             return;
10407         }
10408         
10409         var label = this.el.select('label', true).first();
10410         var icon = this.el.select('i.fa-star', true).first();
10411         
10412         if(!this.getValue().length && label && !icon){
10413             this.el.createChild({
10414                 tag : 'i',
10415                 cls : 'text-danger fa fa-lg fa-star',
10416                 tooltip : 'This field is required',
10417                 style : 'margin-right:5px;'
10418             }, label, true);
10419         }
10420         
10421         if (Roo.bootstrap.version == 3) {
10422             this.el.addClass(this.invalidClass);
10423         } else {
10424             this.inputEl().addClass('is-invalid');
10425         }
10426         
10427         // fixme ... this may be depricated need to test..
10428         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10429             
10430             var feedback = this.el.select('.form-control-feedback', true).first();
10431             
10432             if(feedback){
10433                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10434                 
10435                 if(this.getValue().length || this.forceFeedback){
10436                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10437                 }
10438                 
10439             }
10440             
10441         }
10442         
10443         this.fireEvent('invalid', this, msg);
10444     }
10445 });
10446
10447  
10448 /*
10449  * - LGPL
10450  *
10451  * trigger field - base class for combo..
10452  * 
10453  */
10454  
10455 /**
10456  * @class Roo.bootstrap.TriggerField
10457  * @extends Roo.bootstrap.Input
10458  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10459  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10460  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10461  * for which you can provide a custom implementation.  For example:
10462  * <pre><code>
10463 var trigger = new Roo.bootstrap.TriggerField();
10464 trigger.onTriggerClick = myTriggerFn;
10465 trigger.applyTo('my-field');
10466 </code></pre>
10467  *
10468  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10469  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10470  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10471  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10472  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10473
10474  * @constructor
10475  * Create a new TriggerField.
10476  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10477  * to the base TextField)
10478  */
10479 Roo.bootstrap.TriggerField = function(config){
10480     this.mimicing = false;
10481     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10482 };
10483
10484 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10485     /**
10486      * @cfg {String} triggerClass A CSS class to apply to the trigger
10487      */
10488      /**
10489      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10490      */
10491     hideTrigger:false,
10492
10493     /**
10494      * @cfg {Boolean} removable (true|false) special filter default false
10495      */
10496     removable : false,
10497     
10498     /** @cfg {Boolean} grow @hide */
10499     /** @cfg {Number} growMin @hide */
10500     /** @cfg {Number} growMax @hide */
10501
10502     /**
10503      * @hide 
10504      * @method
10505      */
10506     autoSize: Roo.emptyFn,
10507     // private
10508     monitorTab : true,
10509     // private
10510     deferHeight : true,
10511
10512     
10513     actionMode : 'wrap',
10514     
10515     caret : false,
10516     
10517     
10518     getAutoCreate : function(){
10519        
10520         var align = this.labelAlign || this.parentLabelAlign();
10521         
10522         var id = Roo.id();
10523         
10524         var cfg = {
10525             cls: 'form-group' //input-group
10526         };
10527         
10528         
10529         var input =  {
10530             tag: 'input',
10531             id : id,
10532             type : this.inputType,
10533             cls : 'form-control',
10534             autocomplete: 'new-password',
10535             placeholder : this.placeholder || '' 
10536             
10537         };
10538         if (this.name) {
10539             input.name = this.name;
10540         }
10541         if (this.size) {
10542             input.cls += ' input-' + this.size;
10543         }
10544         
10545         if (this.disabled) {
10546             input.disabled=true;
10547         }
10548         
10549         var inputblock = input;
10550         
10551         if(this.hasFeedback && !this.allowBlank){
10552             
10553             var feedback = {
10554                 tag: 'span',
10555                 cls: 'glyphicon form-control-feedback'
10556             };
10557             
10558             if(this.removable && !this.editable && !this.tickable){
10559                 inputblock = {
10560                     cls : 'has-feedback',
10561                     cn :  [
10562                         inputblock,
10563                         {
10564                             tag: 'button',
10565                             html : 'x',
10566                             cls : 'roo-combo-removable-btn close'
10567                         },
10568                         feedback
10569                     ] 
10570                 };
10571             } else {
10572                 inputblock = {
10573                     cls : 'has-feedback',
10574                     cn :  [
10575                         inputblock,
10576                         feedback
10577                     ] 
10578                 };
10579             }
10580
10581         } else {
10582             if(this.removable && !this.editable && !this.tickable){
10583                 inputblock = {
10584                     cls : 'roo-removable',
10585                     cn :  [
10586                         inputblock,
10587                         {
10588                             tag: 'button',
10589                             html : 'x',
10590                             cls : 'roo-combo-removable-btn close'
10591                         }
10592                     ] 
10593                 };
10594             }
10595         }
10596         
10597         if (this.before || this.after) {
10598             
10599             inputblock = {
10600                 cls : 'input-group',
10601                 cn :  [] 
10602             };
10603             if (this.before) {
10604                 inputblock.cn.push({
10605                     tag :'span',
10606                     cls : 'input-group-addon input-group-prepend input-group-text',
10607                     html : this.before
10608                 });
10609             }
10610             
10611             inputblock.cn.push(input);
10612             
10613             if(this.hasFeedback && !this.allowBlank){
10614                 inputblock.cls += ' has-feedback';
10615                 inputblock.cn.push(feedback);
10616             }
10617             
10618             if (this.after) {
10619                 inputblock.cn.push({
10620                     tag :'span',
10621                     cls : 'input-group-addon input-group-append input-group-text',
10622                     html : this.after
10623                 });
10624             }
10625             
10626         };
10627         
10628       
10629         
10630         var ibwrap = inputblock;
10631         
10632         if(this.multiple){
10633             ibwrap = {
10634                 tag: 'ul',
10635                 cls: 'roo-select2-choices',
10636                 cn:[
10637                     {
10638                         tag: 'li',
10639                         cls: 'roo-select2-search-field',
10640                         cn: [
10641
10642                             inputblock
10643                         ]
10644                     }
10645                 ]
10646             };
10647                 
10648         }
10649         
10650         var combobox = {
10651             cls: 'roo-select2-container input-group',
10652             cn: [
10653                  {
10654                     tag: 'input',
10655                     type : 'hidden',
10656                     cls: 'form-hidden-field'
10657                 },
10658                 ibwrap
10659             ]
10660         };
10661         
10662         if(!this.multiple && this.showToggleBtn){
10663             
10664             var caret = {
10665                         tag: 'span',
10666                         cls: 'caret'
10667              };
10668             if (this.caret != false) {
10669                 caret = {
10670                      tag: 'i',
10671                      cls: 'fa fa-' + this.caret
10672                 };
10673                 
10674             }
10675             
10676             combobox.cn.push({
10677                 tag :'span',
10678                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10679                 cn : [
10680                     Roo.bootstrap.version == 3 ? caret : '',
10681                     {
10682                         tag: 'span',
10683                         cls: 'combobox-clear',
10684                         cn  : [
10685                             {
10686                                 tag : 'i',
10687                                 cls: 'icon-remove'
10688                             }
10689                         ]
10690                     }
10691                 ]
10692
10693             })
10694         }
10695         
10696         if(this.multiple){
10697             combobox.cls += ' roo-select2-container-multi';
10698         }
10699          var indicator = {
10700             tag : 'i',
10701             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10702             tooltip : 'This field is required'
10703         };
10704         if (Roo.bootstrap.version == 4) {
10705             indicator = {
10706                 tag : 'i',
10707                 style : 'display:none'
10708             };
10709         }
10710         
10711         
10712         if (align ==='left' && this.fieldLabel.length) {
10713             
10714             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10715
10716             cfg.cn = [
10717                 indicator,
10718                 {
10719                     tag: 'label',
10720                     'for' :  id,
10721                     cls : 'control-label',
10722                     html : this.fieldLabel
10723
10724                 },
10725                 {
10726                     cls : "", 
10727                     cn: [
10728                         combobox
10729                     ]
10730                 }
10731
10732             ];
10733             
10734             var labelCfg = cfg.cn[1];
10735             var contentCfg = cfg.cn[2];
10736             
10737             if(this.indicatorpos == 'right'){
10738                 cfg.cn = [
10739                     {
10740                         tag: 'label',
10741                         'for' :  id,
10742                         cls : 'control-label',
10743                         cn : [
10744                             {
10745                                 tag : 'span',
10746                                 html : this.fieldLabel
10747                             },
10748                             indicator
10749                         ]
10750                     },
10751                     {
10752                         cls : "", 
10753                         cn: [
10754                             combobox
10755                         ]
10756                     }
10757
10758                 ];
10759                 
10760                 labelCfg = cfg.cn[0];
10761                 contentCfg = cfg.cn[1];
10762             }
10763             
10764             if(this.labelWidth > 12){
10765                 labelCfg.style = "width: " + this.labelWidth + 'px';
10766             }
10767             
10768             if(this.labelWidth < 13 && this.labelmd == 0){
10769                 this.labelmd = this.labelWidth;
10770             }
10771             
10772             if(this.labellg > 0){
10773                 labelCfg.cls += ' col-lg-' + this.labellg;
10774                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10775             }
10776             
10777             if(this.labelmd > 0){
10778                 labelCfg.cls += ' col-md-' + this.labelmd;
10779                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10780             }
10781             
10782             if(this.labelsm > 0){
10783                 labelCfg.cls += ' col-sm-' + this.labelsm;
10784                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10785             }
10786             
10787             if(this.labelxs > 0){
10788                 labelCfg.cls += ' col-xs-' + this.labelxs;
10789                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10790             }
10791             
10792         } else if ( this.fieldLabel.length) {
10793 //                Roo.log(" label");
10794             cfg.cn = [
10795                 indicator,
10796                {
10797                    tag: 'label',
10798                    //cls : 'input-group-addon',
10799                    html : this.fieldLabel
10800
10801                },
10802
10803                combobox
10804
10805             ];
10806             
10807             if(this.indicatorpos == 'right'){
10808                 
10809                 cfg.cn = [
10810                     {
10811                        tag: 'label',
10812                        cn : [
10813                            {
10814                                tag : 'span',
10815                                html : this.fieldLabel
10816                            },
10817                            indicator
10818                        ]
10819
10820                     },
10821                     combobox
10822
10823                 ];
10824
10825             }
10826
10827         } else {
10828             
10829 //                Roo.log(" no label && no align");
10830                 cfg = combobox
10831                      
10832                 
10833         }
10834         
10835         var settings=this;
10836         ['xs','sm','md','lg'].map(function(size){
10837             if (settings[size]) {
10838                 cfg.cls += ' col-' + size + '-' + settings[size];
10839             }
10840         });
10841         
10842         return cfg;
10843         
10844     },
10845     
10846     
10847     
10848     // private
10849     onResize : function(w, h){
10850 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10851 //        if(typeof w == 'number'){
10852 //            var x = w - this.trigger.getWidth();
10853 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10854 //            this.trigger.setStyle('left', x+'px');
10855 //        }
10856     },
10857
10858     // private
10859     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10860
10861     // private
10862     getResizeEl : function(){
10863         return this.inputEl();
10864     },
10865
10866     // private
10867     getPositionEl : function(){
10868         return this.inputEl();
10869     },
10870
10871     // private
10872     alignErrorIcon : function(){
10873         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10874     },
10875
10876     // private
10877     initEvents : function(){
10878         
10879         this.createList();
10880         
10881         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10882         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10883         if(!this.multiple && this.showToggleBtn){
10884             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10885             if(this.hideTrigger){
10886                 this.trigger.setDisplayed(false);
10887             }
10888             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10889         }
10890         
10891         if(this.multiple){
10892             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10893         }
10894         
10895         if(this.removable && !this.editable && !this.tickable){
10896             var close = this.closeTriggerEl();
10897             
10898             if(close){
10899                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10900                 close.on('click', this.removeBtnClick, this, close);
10901             }
10902         }
10903         
10904         //this.trigger.addClassOnOver('x-form-trigger-over');
10905         //this.trigger.addClassOnClick('x-form-trigger-click');
10906         
10907         //if(!this.width){
10908         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10909         //}
10910     },
10911     
10912     closeTriggerEl : function()
10913     {
10914         var close = this.el.select('.roo-combo-removable-btn', true).first();
10915         return close ? close : false;
10916     },
10917     
10918     removeBtnClick : function(e, h, el)
10919     {
10920         e.preventDefault();
10921         
10922         if(this.fireEvent("remove", this) !== false){
10923             this.reset();
10924             this.fireEvent("afterremove", this)
10925         }
10926     },
10927     
10928     createList : function()
10929     {
10930         this.list = Roo.get(document.body).createChild({
10931             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10932             cls: 'typeahead typeahead-long dropdown-menu',
10933             style: 'display:none'
10934         });
10935         
10936         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10937         
10938     },
10939
10940     // private
10941     initTrigger : function(){
10942        
10943     },
10944
10945     // private
10946     onDestroy : function(){
10947         if(this.trigger){
10948             this.trigger.removeAllListeners();
10949           //  this.trigger.remove();
10950         }
10951         //if(this.wrap){
10952         //    this.wrap.remove();
10953         //}
10954         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10955     },
10956
10957     // private
10958     onFocus : function(){
10959         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10960         /*
10961         if(!this.mimicing){
10962             this.wrap.addClass('x-trigger-wrap-focus');
10963             this.mimicing = true;
10964             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10965             if(this.monitorTab){
10966                 this.el.on("keydown", this.checkTab, this);
10967             }
10968         }
10969         */
10970     },
10971
10972     // private
10973     checkTab : function(e){
10974         if(e.getKey() == e.TAB){
10975             this.triggerBlur();
10976         }
10977     },
10978
10979     // private
10980     onBlur : function(){
10981         // do nothing
10982     },
10983
10984     // private
10985     mimicBlur : function(e, t){
10986         /*
10987         if(!this.wrap.contains(t) && this.validateBlur()){
10988             this.triggerBlur();
10989         }
10990         */
10991     },
10992
10993     // private
10994     triggerBlur : function(){
10995         this.mimicing = false;
10996         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10997         if(this.monitorTab){
10998             this.el.un("keydown", this.checkTab, this);
10999         }
11000         //this.wrap.removeClass('x-trigger-wrap-focus');
11001         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11002     },
11003
11004     // private
11005     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11006     validateBlur : function(e, t){
11007         return true;
11008     },
11009
11010     // private
11011     onDisable : function(){
11012         this.inputEl().dom.disabled = true;
11013         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11014         //if(this.wrap){
11015         //    this.wrap.addClass('x-item-disabled');
11016         //}
11017     },
11018
11019     // private
11020     onEnable : function(){
11021         this.inputEl().dom.disabled = false;
11022         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11023         //if(this.wrap){
11024         //    this.el.removeClass('x-item-disabled');
11025         //}
11026     },
11027
11028     // private
11029     onShow : function(){
11030         var ae = this.getActionEl();
11031         
11032         if(ae){
11033             ae.dom.style.display = '';
11034             ae.dom.style.visibility = 'visible';
11035         }
11036     },
11037
11038     // private
11039     
11040     onHide : function(){
11041         var ae = this.getActionEl();
11042         ae.dom.style.display = 'none';
11043     },
11044
11045     /**
11046      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11047      * by an implementing function.
11048      * @method
11049      * @param {EventObject} e
11050      */
11051     onTriggerClick : Roo.emptyFn
11052 });
11053  /*
11054  * Based on:
11055  * Ext JS Library 1.1.1
11056  * Copyright(c) 2006-2007, Ext JS, LLC.
11057  *
11058  * Originally Released Under LGPL - original licence link has changed is not relivant.
11059  *
11060  * Fork - LGPL
11061  * <script type="text/javascript">
11062  */
11063
11064
11065 /**
11066  * @class Roo.data.SortTypes
11067  * @singleton
11068  * Defines the default sorting (casting?) comparison functions used when sorting data.
11069  */
11070 Roo.data.SortTypes = {
11071     /**
11072      * Default sort that does nothing
11073      * @param {Mixed} s The value being converted
11074      * @return {Mixed} The comparison value
11075      */
11076     none : function(s){
11077         return s;
11078     },
11079     
11080     /**
11081      * The regular expression used to strip tags
11082      * @type {RegExp}
11083      * @property
11084      */
11085     stripTagsRE : /<\/?[^>]+>/gi,
11086     
11087     /**
11088      * Strips all HTML tags to sort on text only
11089      * @param {Mixed} s The value being converted
11090      * @return {String} The comparison value
11091      */
11092     asText : function(s){
11093         return String(s).replace(this.stripTagsRE, "");
11094     },
11095     
11096     /**
11097      * Strips all HTML tags to sort on text only - Case insensitive
11098      * @param {Mixed} s The value being converted
11099      * @return {String} The comparison value
11100      */
11101     asUCText : function(s){
11102         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11103     },
11104     
11105     /**
11106      * Case insensitive string
11107      * @param {Mixed} s The value being converted
11108      * @return {String} The comparison value
11109      */
11110     asUCString : function(s) {
11111         return String(s).toUpperCase();
11112     },
11113     
11114     /**
11115      * Date sorting
11116      * @param {Mixed} s The value being converted
11117      * @return {Number} The comparison value
11118      */
11119     asDate : function(s) {
11120         if(!s){
11121             return 0;
11122         }
11123         if(s instanceof Date){
11124             return s.getTime();
11125         }
11126         return Date.parse(String(s));
11127     },
11128     
11129     /**
11130      * Float sorting
11131      * @param {Mixed} s The value being converted
11132      * @return {Float} The comparison value
11133      */
11134     asFloat : function(s) {
11135         var val = parseFloat(String(s).replace(/,/g, ""));
11136         if(isNaN(val)) {
11137             val = 0;
11138         }
11139         return val;
11140     },
11141     
11142     /**
11143      * Integer sorting
11144      * @param {Mixed} s The value being converted
11145      * @return {Number} The comparison value
11146      */
11147     asInt : function(s) {
11148         var val = parseInt(String(s).replace(/,/g, ""));
11149         if(isNaN(val)) {
11150             val = 0;
11151         }
11152         return val;
11153     }
11154 };/*
11155  * Based on:
11156  * Ext JS Library 1.1.1
11157  * Copyright(c) 2006-2007, Ext JS, LLC.
11158  *
11159  * Originally Released Under LGPL - original licence link has changed is not relivant.
11160  *
11161  * Fork - LGPL
11162  * <script type="text/javascript">
11163  */
11164
11165 /**
11166 * @class Roo.data.Record
11167  * Instances of this class encapsulate both record <em>definition</em> information, and record
11168  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11169  * to access Records cached in an {@link Roo.data.Store} object.<br>
11170  * <p>
11171  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11172  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11173  * objects.<br>
11174  * <p>
11175  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11176  * @constructor
11177  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11178  * {@link #create}. The parameters are the same.
11179  * @param {Array} data An associative Array of data values keyed by the field name.
11180  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11181  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11182  * not specified an integer id is generated.
11183  */
11184 Roo.data.Record = function(data, id){
11185     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11186     this.data = data;
11187 };
11188
11189 /**
11190  * Generate a constructor for a specific record layout.
11191  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11192  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11193  * Each field definition object may contain the following properties: <ul>
11194  * <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,
11195  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11196  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11197  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11198  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11199  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11200  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11201  * this may be omitted.</p></li>
11202  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11203  * <ul><li>auto (Default, implies no conversion)</li>
11204  * <li>string</li>
11205  * <li>int</li>
11206  * <li>float</li>
11207  * <li>boolean</li>
11208  * <li>date</li></ul></p></li>
11209  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11210  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11211  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11212  * by the Reader into an object that will be stored in the Record. It is passed the
11213  * following parameters:<ul>
11214  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11215  * </ul></p></li>
11216  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11217  * </ul>
11218  * <br>usage:<br><pre><code>
11219 var TopicRecord = Roo.data.Record.create(
11220     {name: 'title', mapping: 'topic_title'},
11221     {name: 'author', mapping: 'username'},
11222     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11223     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11224     {name: 'lastPoster', mapping: 'user2'},
11225     {name: 'excerpt', mapping: 'post_text'}
11226 );
11227
11228 var myNewRecord = new TopicRecord({
11229     title: 'Do my job please',
11230     author: 'noobie',
11231     totalPosts: 1,
11232     lastPost: new Date(),
11233     lastPoster: 'Animal',
11234     excerpt: 'No way dude!'
11235 });
11236 myStore.add(myNewRecord);
11237 </code></pre>
11238  * @method create
11239  * @static
11240  */
11241 Roo.data.Record.create = function(o){
11242     var f = function(){
11243         f.superclass.constructor.apply(this, arguments);
11244     };
11245     Roo.extend(f, Roo.data.Record);
11246     var p = f.prototype;
11247     p.fields = new Roo.util.MixedCollection(false, function(field){
11248         return field.name;
11249     });
11250     for(var i = 0, len = o.length; i < len; i++){
11251         p.fields.add(new Roo.data.Field(o[i]));
11252     }
11253     f.getField = function(name){
11254         return p.fields.get(name);  
11255     };
11256     return f;
11257 };
11258
11259 Roo.data.Record.AUTO_ID = 1000;
11260 Roo.data.Record.EDIT = 'edit';
11261 Roo.data.Record.REJECT = 'reject';
11262 Roo.data.Record.COMMIT = 'commit';
11263
11264 Roo.data.Record.prototype = {
11265     /**
11266      * Readonly flag - true if this record has been modified.
11267      * @type Boolean
11268      */
11269     dirty : false,
11270     editing : false,
11271     error: null,
11272     modified: null,
11273
11274     // private
11275     join : function(store){
11276         this.store = store;
11277     },
11278
11279     /**
11280      * Set the named field to the specified value.
11281      * @param {String} name The name of the field to set.
11282      * @param {Object} value The value to set the field to.
11283      */
11284     set : function(name, value){
11285         if(this.data[name] == value){
11286             return;
11287         }
11288         this.dirty = true;
11289         if(!this.modified){
11290             this.modified = {};
11291         }
11292         if(typeof this.modified[name] == 'undefined'){
11293             this.modified[name] = this.data[name];
11294         }
11295         this.data[name] = value;
11296         if(!this.editing && this.store){
11297             this.store.afterEdit(this);
11298         }       
11299     },
11300
11301     /**
11302      * Get the value of the named field.
11303      * @param {String} name The name of the field to get the value of.
11304      * @return {Object} The value of the field.
11305      */
11306     get : function(name){
11307         return this.data[name]; 
11308     },
11309
11310     // private
11311     beginEdit : function(){
11312         this.editing = true;
11313         this.modified = {}; 
11314     },
11315
11316     // private
11317     cancelEdit : function(){
11318         this.editing = false;
11319         delete this.modified;
11320     },
11321
11322     // private
11323     endEdit : function(){
11324         this.editing = false;
11325         if(this.dirty && this.store){
11326             this.store.afterEdit(this);
11327         }
11328     },
11329
11330     /**
11331      * Usually called by the {@link Roo.data.Store} which owns the Record.
11332      * Rejects all changes made to the Record since either creation, or the last commit operation.
11333      * Modified fields are reverted to their original values.
11334      * <p>
11335      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11336      * of reject operations.
11337      */
11338     reject : function(){
11339         var m = this.modified;
11340         for(var n in m){
11341             if(typeof m[n] != "function"){
11342                 this.data[n] = m[n];
11343             }
11344         }
11345         this.dirty = false;
11346         delete this.modified;
11347         this.editing = false;
11348         if(this.store){
11349             this.store.afterReject(this);
11350         }
11351     },
11352
11353     /**
11354      * Usually called by the {@link Roo.data.Store} which owns the Record.
11355      * Commits all changes made to the Record since either creation, or the last commit operation.
11356      * <p>
11357      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11358      * of commit operations.
11359      */
11360     commit : function(){
11361         this.dirty = false;
11362         delete this.modified;
11363         this.editing = false;
11364         if(this.store){
11365             this.store.afterCommit(this);
11366         }
11367     },
11368
11369     // private
11370     hasError : function(){
11371         return this.error != null;
11372     },
11373
11374     // private
11375     clearError : function(){
11376         this.error = null;
11377     },
11378
11379     /**
11380      * Creates a copy of this record.
11381      * @param {String} id (optional) A new record id if you don't want to use this record's id
11382      * @return {Record}
11383      */
11384     copy : function(newId) {
11385         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11386     }
11387 };/*
11388  * Based on:
11389  * Ext JS Library 1.1.1
11390  * Copyright(c) 2006-2007, Ext JS, LLC.
11391  *
11392  * Originally Released Under LGPL - original licence link has changed is not relivant.
11393  *
11394  * Fork - LGPL
11395  * <script type="text/javascript">
11396  */
11397
11398
11399
11400 /**
11401  * @class Roo.data.Store
11402  * @extends Roo.util.Observable
11403  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11404  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11405  * <p>
11406  * 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
11407  * has no knowledge of the format of the data returned by the Proxy.<br>
11408  * <p>
11409  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11410  * instances from the data object. These records are cached and made available through accessor functions.
11411  * @constructor
11412  * Creates a new Store.
11413  * @param {Object} config A config object containing the objects needed for the Store to access data,
11414  * and read the data into Records.
11415  */
11416 Roo.data.Store = function(config){
11417     this.data = new Roo.util.MixedCollection(false);
11418     this.data.getKey = function(o){
11419         return o.id;
11420     };
11421     this.baseParams = {};
11422     // private
11423     this.paramNames = {
11424         "start" : "start",
11425         "limit" : "limit",
11426         "sort" : "sort",
11427         "dir" : "dir",
11428         "multisort" : "_multisort"
11429     };
11430
11431     if(config && config.data){
11432         this.inlineData = config.data;
11433         delete config.data;
11434     }
11435
11436     Roo.apply(this, config);
11437     
11438     if(this.reader){ // reader passed
11439         this.reader = Roo.factory(this.reader, Roo.data);
11440         this.reader.xmodule = this.xmodule || false;
11441         if(!this.recordType){
11442             this.recordType = this.reader.recordType;
11443         }
11444         if(this.reader.onMetaChange){
11445             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11446         }
11447     }
11448
11449     if(this.recordType){
11450         this.fields = this.recordType.prototype.fields;
11451     }
11452     this.modified = [];
11453
11454     this.addEvents({
11455         /**
11456          * @event datachanged
11457          * Fires when the data cache has changed, and a widget which is using this Store
11458          * as a Record cache should refresh its view.
11459          * @param {Store} this
11460          */
11461         datachanged : true,
11462         /**
11463          * @event metachange
11464          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11465          * @param {Store} this
11466          * @param {Object} meta The JSON metadata
11467          */
11468         metachange : true,
11469         /**
11470          * @event add
11471          * Fires when Records have been added to the Store
11472          * @param {Store} this
11473          * @param {Roo.data.Record[]} records The array of Records added
11474          * @param {Number} index The index at which the record(s) were added
11475          */
11476         add : true,
11477         /**
11478          * @event remove
11479          * Fires when a Record has been removed from the Store
11480          * @param {Store} this
11481          * @param {Roo.data.Record} record The Record that was removed
11482          * @param {Number} index The index at which the record was removed
11483          */
11484         remove : true,
11485         /**
11486          * @event update
11487          * Fires when a Record has been updated
11488          * @param {Store} this
11489          * @param {Roo.data.Record} record The Record that was updated
11490          * @param {String} operation The update operation being performed.  Value may be one of:
11491          * <pre><code>
11492  Roo.data.Record.EDIT
11493  Roo.data.Record.REJECT
11494  Roo.data.Record.COMMIT
11495          * </code></pre>
11496          */
11497         update : true,
11498         /**
11499          * @event clear
11500          * Fires when the data cache has been cleared.
11501          * @param {Store} this
11502          */
11503         clear : true,
11504         /**
11505          * @event beforeload
11506          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11507          * the load action will be canceled.
11508          * @param {Store} this
11509          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11510          */
11511         beforeload : true,
11512         /**
11513          * @event beforeloadadd
11514          * Fires after a new set of Records has been loaded.
11515          * @param {Store} this
11516          * @param {Roo.data.Record[]} records The Records that were loaded
11517          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11518          */
11519         beforeloadadd : true,
11520         /**
11521          * @event load
11522          * Fires after a new set of Records has been loaded, before they are added to the store.
11523          * @param {Store} this
11524          * @param {Roo.data.Record[]} records The Records that were loaded
11525          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11526          * @params {Object} return from reader
11527          */
11528         load : true,
11529         /**
11530          * @event loadexception
11531          * Fires if an exception occurs in the Proxy during loading.
11532          * Called with the signature of the Proxy's "loadexception" event.
11533          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11534          * 
11535          * @param {Proxy} 
11536          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11537          * @param {Object} load options 
11538          * @param {Object} jsonData from your request (normally this contains the Exception)
11539          */
11540         loadexception : true
11541     });
11542     
11543     if(this.proxy){
11544         this.proxy = Roo.factory(this.proxy, Roo.data);
11545         this.proxy.xmodule = this.xmodule || false;
11546         this.relayEvents(this.proxy,  ["loadexception"]);
11547     }
11548     this.sortToggle = {};
11549     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11550
11551     Roo.data.Store.superclass.constructor.call(this);
11552
11553     if(this.inlineData){
11554         this.loadData(this.inlineData);
11555         delete this.inlineData;
11556     }
11557 };
11558
11559 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11560      /**
11561     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11562     * without a remote query - used by combo/forms at present.
11563     */
11564     
11565     /**
11566     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11567     */
11568     /**
11569     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11570     */
11571     /**
11572     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11573     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11574     */
11575     /**
11576     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11577     * on any HTTP request
11578     */
11579     /**
11580     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11581     */
11582     /**
11583     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11584     */
11585     multiSort: false,
11586     /**
11587     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11588     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11589     */
11590     remoteSort : false,
11591
11592     /**
11593     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11594      * loaded or when a record is removed. (defaults to false).
11595     */
11596     pruneModifiedRecords : false,
11597
11598     // private
11599     lastOptions : null,
11600
11601     /**
11602      * Add Records to the Store and fires the add event.
11603      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11604      */
11605     add : function(records){
11606         records = [].concat(records);
11607         for(var i = 0, len = records.length; i < len; i++){
11608             records[i].join(this);
11609         }
11610         var index = this.data.length;
11611         this.data.addAll(records);
11612         this.fireEvent("add", this, records, index);
11613     },
11614
11615     /**
11616      * Remove a Record from the Store and fires the remove event.
11617      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11618      */
11619     remove : function(record){
11620         var index = this.data.indexOf(record);
11621         this.data.removeAt(index);
11622  
11623         if(this.pruneModifiedRecords){
11624             this.modified.remove(record);
11625         }
11626         this.fireEvent("remove", this, record, index);
11627     },
11628
11629     /**
11630      * Remove all Records from the Store and fires the clear event.
11631      */
11632     removeAll : function(){
11633         this.data.clear();
11634         if(this.pruneModifiedRecords){
11635             this.modified = [];
11636         }
11637         this.fireEvent("clear", this);
11638     },
11639
11640     /**
11641      * Inserts Records to the Store at the given index and fires the add event.
11642      * @param {Number} index The start index at which to insert the passed Records.
11643      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11644      */
11645     insert : function(index, records){
11646         records = [].concat(records);
11647         for(var i = 0, len = records.length; i < len; i++){
11648             this.data.insert(index, records[i]);
11649             records[i].join(this);
11650         }
11651         this.fireEvent("add", this, records, index);
11652     },
11653
11654     /**
11655      * Get the index within the cache of the passed Record.
11656      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11657      * @return {Number} The index of the passed Record. Returns -1 if not found.
11658      */
11659     indexOf : function(record){
11660         return this.data.indexOf(record);
11661     },
11662
11663     /**
11664      * Get the index within the cache of the Record with the passed id.
11665      * @param {String} id The id of the Record to find.
11666      * @return {Number} The index of the Record. Returns -1 if not found.
11667      */
11668     indexOfId : function(id){
11669         return this.data.indexOfKey(id);
11670     },
11671
11672     /**
11673      * Get the Record with the specified id.
11674      * @param {String} id The id of the Record to find.
11675      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11676      */
11677     getById : function(id){
11678         return this.data.key(id);
11679     },
11680
11681     /**
11682      * Get the Record at the specified index.
11683      * @param {Number} index The index of the Record to find.
11684      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11685      */
11686     getAt : function(index){
11687         return this.data.itemAt(index);
11688     },
11689
11690     /**
11691      * Returns a range of Records between specified indices.
11692      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11693      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11694      * @return {Roo.data.Record[]} An array of Records
11695      */
11696     getRange : function(start, end){
11697         return this.data.getRange(start, end);
11698     },
11699
11700     // private
11701     storeOptions : function(o){
11702         o = Roo.apply({}, o);
11703         delete o.callback;
11704         delete o.scope;
11705         this.lastOptions = o;
11706     },
11707
11708     /**
11709      * Loads the Record cache from the configured Proxy using the configured Reader.
11710      * <p>
11711      * If using remote paging, then the first load call must specify the <em>start</em>
11712      * and <em>limit</em> properties in the options.params property to establish the initial
11713      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11714      * <p>
11715      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11716      * and this call will return before the new data has been loaded. Perform any post-processing
11717      * in a callback function, or in a "load" event handler.</strong>
11718      * <p>
11719      * @param {Object} options An object containing properties which control loading options:<ul>
11720      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11721      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11722      * passed the following arguments:<ul>
11723      * <li>r : Roo.data.Record[]</li>
11724      * <li>options: Options object from the load call</li>
11725      * <li>success: Boolean success indicator</li></ul></li>
11726      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11727      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11728      * </ul>
11729      */
11730     load : function(options){
11731         options = options || {};
11732         if(this.fireEvent("beforeload", this, options) !== false){
11733             this.storeOptions(options);
11734             var p = Roo.apply(options.params || {}, this.baseParams);
11735             // if meta was not loaded from remote source.. try requesting it.
11736             if (!this.reader.metaFromRemote) {
11737                 p._requestMeta = 1;
11738             }
11739             if(this.sortInfo && this.remoteSort){
11740                 var pn = this.paramNames;
11741                 p[pn["sort"]] = this.sortInfo.field;
11742                 p[pn["dir"]] = this.sortInfo.direction;
11743             }
11744             if (this.multiSort) {
11745                 var pn = this.paramNames;
11746                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11747             }
11748             
11749             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11750         }
11751     },
11752
11753     /**
11754      * Reloads the Record cache from the configured Proxy using the configured Reader and
11755      * the options from the last load operation performed.
11756      * @param {Object} options (optional) An object containing properties which may override the options
11757      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11758      * the most recently used options are reused).
11759      */
11760     reload : function(options){
11761         this.load(Roo.applyIf(options||{}, this.lastOptions));
11762     },
11763
11764     // private
11765     // Called as a callback by the Reader during a load operation.
11766     loadRecords : function(o, options, success){
11767         if(!o || success === false){
11768             if(success !== false){
11769                 this.fireEvent("load", this, [], options, o);
11770             }
11771             if(options.callback){
11772                 options.callback.call(options.scope || this, [], options, false);
11773             }
11774             return;
11775         }
11776         // if data returned failure - throw an exception.
11777         if (o.success === false) {
11778             // show a message if no listener is registered.
11779             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11780                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11781             }
11782             // loadmask wil be hooked into this..
11783             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11784             return;
11785         }
11786         var r = o.records, t = o.totalRecords || r.length;
11787         
11788         this.fireEvent("beforeloadadd", this, r, options, o);
11789         
11790         if(!options || options.add !== true){
11791             if(this.pruneModifiedRecords){
11792                 this.modified = [];
11793             }
11794             for(var i = 0, len = r.length; i < len; i++){
11795                 r[i].join(this);
11796             }
11797             if(this.snapshot){
11798                 this.data = this.snapshot;
11799                 delete this.snapshot;
11800             }
11801             this.data.clear();
11802             this.data.addAll(r);
11803             this.totalLength = t;
11804             this.applySort();
11805             this.fireEvent("datachanged", this);
11806         }else{
11807             this.totalLength = Math.max(t, this.data.length+r.length);
11808             this.add(r);
11809         }
11810         
11811         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11812                 
11813             var e = new Roo.data.Record({});
11814
11815             e.set(this.parent.displayField, this.parent.emptyTitle);
11816             e.set(this.parent.valueField, '');
11817
11818             this.insert(0, e);
11819         }
11820             
11821         this.fireEvent("load", this, r, options, o);
11822         if(options.callback){
11823             options.callback.call(options.scope || this, r, options, true);
11824         }
11825     },
11826
11827
11828     /**
11829      * Loads data from a passed data block. A Reader which understands the format of the data
11830      * must have been configured in the constructor.
11831      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11832      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11833      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11834      */
11835     loadData : function(o, append){
11836         var r = this.reader.readRecords(o);
11837         this.loadRecords(r, {add: append}, true);
11838     },
11839
11840     /**
11841      * Gets the number of cached records.
11842      * <p>
11843      * <em>If using paging, this may not be the total size of the dataset. If the data object
11844      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11845      * the data set size</em>
11846      */
11847     getCount : function(){
11848         return this.data.length || 0;
11849     },
11850
11851     /**
11852      * Gets the total number of records in the dataset as returned by the server.
11853      * <p>
11854      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11855      * the dataset size</em>
11856      */
11857     getTotalCount : function(){
11858         return this.totalLength || 0;
11859     },
11860
11861     /**
11862      * Returns the sort state of the Store as an object with two properties:
11863      * <pre><code>
11864  field {String} The name of the field by which the Records are sorted
11865  direction {String} The sort order, "ASC" or "DESC"
11866      * </code></pre>
11867      */
11868     getSortState : function(){
11869         return this.sortInfo;
11870     },
11871
11872     // private
11873     applySort : function(){
11874         if(this.sortInfo && !this.remoteSort){
11875             var s = this.sortInfo, f = s.field;
11876             var st = this.fields.get(f).sortType;
11877             var fn = function(r1, r2){
11878                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11879                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11880             };
11881             this.data.sort(s.direction, fn);
11882             if(this.snapshot && this.snapshot != this.data){
11883                 this.snapshot.sort(s.direction, fn);
11884             }
11885         }
11886     },
11887
11888     /**
11889      * Sets the default sort column and order to be used by the next load operation.
11890      * @param {String} fieldName The name of the field to sort by.
11891      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11892      */
11893     setDefaultSort : function(field, dir){
11894         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11895     },
11896
11897     /**
11898      * Sort the Records.
11899      * If remote sorting is used, the sort is performed on the server, and the cache is
11900      * reloaded. If local sorting is used, the cache is sorted internally.
11901      * @param {String} fieldName The name of the field to sort by.
11902      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11903      */
11904     sort : function(fieldName, dir){
11905         var f = this.fields.get(fieldName);
11906         if(!dir){
11907             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11908             
11909             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11910                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11911             }else{
11912                 dir = f.sortDir;
11913             }
11914         }
11915         this.sortToggle[f.name] = dir;
11916         this.sortInfo = {field: f.name, direction: dir};
11917         if(!this.remoteSort){
11918             this.applySort();
11919             this.fireEvent("datachanged", this);
11920         }else{
11921             this.load(this.lastOptions);
11922         }
11923     },
11924
11925     /**
11926      * Calls the specified function for each of the Records in the cache.
11927      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11928      * Returning <em>false</em> aborts and exits the iteration.
11929      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11930      */
11931     each : function(fn, scope){
11932         this.data.each(fn, scope);
11933     },
11934
11935     /**
11936      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11937      * (e.g., during paging).
11938      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11939      */
11940     getModifiedRecords : function(){
11941         return this.modified;
11942     },
11943
11944     // private
11945     createFilterFn : function(property, value, anyMatch){
11946         if(!value.exec){ // not a regex
11947             value = String(value);
11948             if(value.length == 0){
11949                 return false;
11950             }
11951             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11952         }
11953         return function(r){
11954             return value.test(r.data[property]);
11955         };
11956     },
11957
11958     /**
11959      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11960      * @param {String} property A field on your records
11961      * @param {Number} start The record index to start at (defaults to 0)
11962      * @param {Number} end The last record index to include (defaults to length - 1)
11963      * @return {Number} The sum
11964      */
11965     sum : function(property, start, end){
11966         var rs = this.data.items, v = 0;
11967         start = start || 0;
11968         end = (end || end === 0) ? end : rs.length-1;
11969
11970         for(var i = start; i <= end; i++){
11971             v += (rs[i].data[property] || 0);
11972         }
11973         return v;
11974     },
11975
11976     /**
11977      * Filter the records by a specified property.
11978      * @param {String} field A field on your records
11979      * @param {String/RegExp} value Either a string that the field
11980      * should start with or a RegExp to test against the field
11981      * @param {Boolean} anyMatch True to match any part not just the beginning
11982      */
11983     filter : function(property, value, anyMatch){
11984         var fn = this.createFilterFn(property, value, anyMatch);
11985         return fn ? this.filterBy(fn) : this.clearFilter();
11986     },
11987
11988     /**
11989      * Filter by a function. The specified function will be called with each
11990      * record in this data source. If the function returns true the record is included,
11991      * otherwise it is filtered.
11992      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11993      * @param {Object} scope (optional) The scope of the function (defaults to this)
11994      */
11995     filterBy : function(fn, scope){
11996         this.snapshot = this.snapshot || this.data;
11997         this.data = this.queryBy(fn, scope||this);
11998         this.fireEvent("datachanged", this);
11999     },
12000
12001     /**
12002      * Query the records by a specified property.
12003      * @param {String} field A field on your records
12004      * @param {String/RegExp} value Either a string that the field
12005      * should start with or a RegExp to test against the field
12006      * @param {Boolean} anyMatch True to match any part not just the beginning
12007      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12008      */
12009     query : function(property, value, anyMatch){
12010         var fn = this.createFilterFn(property, value, anyMatch);
12011         return fn ? this.queryBy(fn) : this.data.clone();
12012     },
12013
12014     /**
12015      * Query by a function. The specified function will be called with each
12016      * record in this data source. If the function returns true the record is included
12017      * in the results.
12018      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12019      * @param {Object} scope (optional) The scope of the function (defaults to this)
12020       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12021      **/
12022     queryBy : function(fn, scope){
12023         var data = this.snapshot || this.data;
12024         return data.filterBy(fn, scope||this);
12025     },
12026
12027     /**
12028      * Collects unique values for a particular dataIndex from this store.
12029      * @param {String} dataIndex The property to collect
12030      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12031      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12032      * @return {Array} An array of the unique values
12033      **/
12034     collect : function(dataIndex, allowNull, bypassFilter){
12035         var d = (bypassFilter === true && this.snapshot) ?
12036                 this.snapshot.items : this.data.items;
12037         var v, sv, r = [], l = {};
12038         for(var i = 0, len = d.length; i < len; i++){
12039             v = d[i].data[dataIndex];
12040             sv = String(v);
12041             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12042                 l[sv] = true;
12043                 r[r.length] = v;
12044             }
12045         }
12046         return r;
12047     },
12048
12049     /**
12050      * Revert to a view of the Record cache with no filtering applied.
12051      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12052      */
12053     clearFilter : function(suppressEvent){
12054         if(this.snapshot && this.snapshot != this.data){
12055             this.data = this.snapshot;
12056             delete this.snapshot;
12057             if(suppressEvent !== true){
12058                 this.fireEvent("datachanged", this);
12059             }
12060         }
12061     },
12062
12063     // private
12064     afterEdit : function(record){
12065         if(this.modified.indexOf(record) == -1){
12066             this.modified.push(record);
12067         }
12068         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12069     },
12070     
12071     // private
12072     afterReject : function(record){
12073         this.modified.remove(record);
12074         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12075     },
12076
12077     // private
12078     afterCommit : function(record){
12079         this.modified.remove(record);
12080         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12081     },
12082
12083     /**
12084      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12085      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12086      */
12087     commitChanges : function(){
12088         var m = this.modified.slice(0);
12089         this.modified = [];
12090         for(var i = 0, len = m.length; i < len; i++){
12091             m[i].commit();
12092         }
12093     },
12094
12095     /**
12096      * Cancel outstanding changes on all changed records.
12097      */
12098     rejectChanges : function(){
12099         var m = this.modified.slice(0);
12100         this.modified = [];
12101         for(var i = 0, len = m.length; i < len; i++){
12102             m[i].reject();
12103         }
12104     },
12105
12106     onMetaChange : function(meta, rtype, o){
12107         this.recordType = rtype;
12108         this.fields = rtype.prototype.fields;
12109         delete this.snapshot;
12110         this.sortInfo = meta.sortInfo || this.sortInfo;
12111         this.modified = [];
12112         this.fireEvent('metachange', this, this.reader.meta);
12113     },
12114     
12115     moveIndex : function(data, type)
12116     {
12117         var index = this.indexOf(data);
12118         
12119         var newIndex = index + type;
12120         
12121         this.remove(data);
12122         
12123         this.insert(newIndex, data);
12124         
12125     }
12126 });/*
12127  * Based on:
12128  * Ext JS Library 1.1.1
12129  * Copyright(c) 2006-2007, Ext JS, LLC.
12130  *
12131  * Originally Released Under LGPL - original licence link has changed is not relivant.
12132  *
12133  * Fork - LGPL
12134  * <script type="text/javascript">
12135  */
12136
12137 /**
12138  * @class Roo.data.SimpleStore
12139  * @extends Roo.data.Store
12140  * Small helper class to make creating Stores from Array data easier.
12141  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12142  * @cfg {Array} fields An array of field definition objects, or field name strings.
12143  * @cfg {Array} data The multi-dimensional array of data
12144  * @constructor
12145  * @param {Object} config
12146  */
12147 Roo.data.SimpleStore = function(config){
12148     Roo.data.SimpleStore.superclass.constructor.call(this, {
12149         isLocal : true,
12150         reader: new Roo.data.ArrayReader({
12151                 id: config.id
12152             },
12153             Roo.data.Record.create(config.fields)
12154         ),
12155         proxy : new Roo.data.MemoryProxy(config.data)
12156     });
12157     this.load();
12158 };
12159 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12160  * Based on:
12161  * Ext JS Library 1.1.1
12162  * Copyright(c) 2006-2007, Ext JS, LLC.
12163  *
12164  * Originally Released Under LGPL - original licence link has changed is not relivant.
12165  *
12166  * Fork - LGPL
12167  * <script type="text/javascript">
12168  */
12169
12170 /**
12171 /**
12172  * @extends Roo.data.Store
12173  * @class Roo.data.JsonStore
12174  * Small helper class to make creating Stores for JSON data easier. <br/>
12175 <pre><code>
12176 var store = new Roo.data.JsonStore({
12177     url: 'get-images.php',
12178     root: 'images',
12179     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12180 });
12181 </code></pre>
12182  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12183  * JsonReader and HttpProxy (unless inline data is provided).</b>
12184  * @cfg {Array} fields An array of field definition objects, or field name strings.
12185  * @constructor
12186  * @param {Object} config
12187  */
12188 Roo.data.JsonStore = function(c){
12189     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12190         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12191         reader: new Roo.data.JsonReader(c, c.fields)
12192     }));
12193 };
12194 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12195  * Based on:
12196  * Ext JS Library 1.1.1
12197  * Copyright(c) 2006-2007, Ext JS, LLC.
12198  *
12199  * Originally Released Under LGPL - original licence link has changed is not relivant.
12200  *
12201  * Fork - LGPL
12202  * <script type="text/javascript">
12203  */
12204
12205  
12206 Roo.data.Field = function(config){
12207     if(typeof config == "string"){
12208         config = {name: config};
12209     }
12210     Roo.apply(this, config);
12211     
12212     if(!this.type){
12213         this.type = "auto";
12214     }
12215     
12216     var st = Roo.data.SortTypes;
12217     // named sortTypes are supported, here we look them up
12218     if(typeof this.sortType == "string"){
12219         this.sortType = st[this.sortType];
12220     }
12221     
12222     // set default sortType for strings and dates
12223     if(!this.sortType){
12224         switch(this.type){
12225             case "string":
12226                 this.sortType = st.asUCString;
12227                 break;
12228             case "date":
12229                 this.sortType = st.asDate;
12230                 break;
12231             default:
12232                 this.sortType = st.none;
12233         }
12234     }
12235
12236     // define once
12237     var stripRe = /[\$,%]/g;
12238
12239     // prebuilt conversion function for this field, instead of
12240     // switching every time we're reading a value
12241     if(!this.convert){
12242         var cv, dateFormat = this.dateFormat;
12243         switch(this.type){
12244             case "":
12245             case "auto":
12246             case undefined:
12247                 cv = function(v){ return v; };
12248                 break;
12249             case "string":
12250                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12251                 break;
12252             case "int":
12253                 cv = function(v){
12254                     return v !== undefined && v !== null && v !== '' ?
12255                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12256                     };
12257                 break;
12258             case "float":
12259                 cv = function(v){
12260                     return v !== undefined && v !== null && v !== '' ?
12261                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12262                     };
12263                 break;
12264             case "bool":
12265             case "boolean":
12266                 cv = function(v){ return v === true || v === "true" || v == 1; };
12267                 break;
12268             case "date":
12269                 cv = function(v){
12270                     if(!v){
12271                         return '';
12272                     }
12273                     if(v instanceof Date){
12274                         return v;
12275                     }
12276                     if(dateFormat){
12277                         if(dateFormat == "timestamp"){
12278                             return new Date(v*1000);
12279                         }
12280                         return Date.parseDate(v, dateFormat);
12281                     }
12282                     var parsed = Date.parse(v);
12283                     return parsed ? new Date(parsed) : null;
12284                 };
12285              break;
12286             
12287         }
12288         this.convert = cv;
12289     }
12290 };
12291
12292 Roo.data.Field.prototype = {
12293     dateFormat: null,
12294     defaultValue: "",
12295     mapping: null,
12296     sortType : null,
12297     sortDir : "ASC"
12298 };/*
12299  * Based on:
12300  * Ext JS Library 1.1.1
12301  * Copyright(c) 2006-2007, Ext JS, LLC.
12302  *
12303  * Originally Released Under LGPL - original licence link has changed is not relivant.
12304  *
12305  * Fork - LGPL
12306  * <script type="text/javascript">
12307  */
12308  
12309 // Base class for reading structured data from a data source.  This class is intended to be
12310 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12311
12312 /**
12313  * @class Roo.data.DataReader
12314  * Base class for reading structured data from a data source.  This class is intended to be
12315  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12316  */
12317
12318 Roo.data.DataReader = function(meta, recordType){
12319     
12320     this.meta = meta;
12321     
12322     this.recordType = recordType instanceof Array ? 
12323         Roo.data.Record.create(recordType) : recordType;
12324 };
12325
12326 Roo.data.DataReader.prototype = {
12327      /**
12328      * Create an empty record
12329      * @param {Object} data (optional) - overlay some values
12330      * @return {Roo.data.Record} record created.
12331      */
12332     newRow :  function(d) {
12333         var da =  {};
12334         this.recordType.prototype.fields.each(function(c) {
12335             switch( c.type) {
12336                 case 'int' : da[c.name] = 0; break;
12337                 case 'date' : da[c.name] = new Date(); break;
12338                 case 'float' : da[c.name] = 0.0; break;
12339                 case 'boolean' : da[c.name] = false; break;
12340                 default : da[c.name] = ""; break;
12341             }
12342             
12343         });
12344         return new this.recordType(Roo.apply(da, d));
12345     }
12346     
12347 };/*
12348  * Based on:
12349  * Ext JS Library 1.1.1
12350  * Copyright(c) 2006-2007, Ext JS, LLC.
12351  *
12352  * Originally Released Under LGPL - original licence link has changed is not relivant.
12353  *
12354  * Fork - LGPL
12355  * <script type="text/javascript">
12356  */
12357
12358 /**
12359  * @class Roo.data.DataProxy
12360  * @extends Roo.data.Observable
12361  * This class is an abstract base class for implementations which provide retrieval of
12362  * unformatted data objects.<br>
12363  * <p>
12364  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12365  * (of the appropriate type which knows how to parse the data object) to provide a block of
12366  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12367  * <p>
12368  * Custom implementations must implement the load method as described in
12369  * {@link Roo.data.HttpProxy#load}.
12370  */
12371 Roo.data.DataProxy = function(){
12372     this.addEvents({
12373         /**
12374          * @event beforeload
12375          * Fires before a network request is made to retrieve a data object.
12376          * @param {Object} This DataProxy object.
12377          * @param {Object} params The params parameter to the load function.
12378          */
12379         beforeload : true,
12380         /**
12381          * @event load
12382          * Fires before the load method's callback is called.
12383          * @param {Object} This DataProxy object.
12384          * @param {Object} o The data object.
12385          * @param {Object} arg The callback argument object passed to the load function.
12386          */
12387         load : true,
12388         /**
12389          * @event loadexception
12390          * Fires if an Exception occurs during data retrieval.
12391          * @param {Object} This DataProxy object.
12392          * @param {Object} o The data object.
12393          * @param {Object} arg The callback argument object passed to the load function.
12394          * @param {Object} e The Exception.
12395          */
12396         loadexception : true
12397     });
12398     Roo.data.DataProxy.superclass.constructor.call(this);
12399 };
12400
12401 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12402
12403     /**
12404      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12405      */
12406 /*
12407  * Based on:
12408  * Ext JS Library 1.1.1
12409  * Copyright(c) 2006-2007, Ext JS, LLC.
12410  *
12411  * Originally Released Under LGPL - original licence link has changed is not relivant.
12412  *
12413  * Fork - LGPL
12414  * <script type="text/javascript">
12415  */
12416 /**
12417  * @class Roo.data.MemoryProxy
12418  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12419  * to the Reader when its load method is called.
12420  * @constructor
12421  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12422  */
12423 Roo.data.MemoryProxy = function(data){
12424     if (data.data) {
12425         data = data.data;
12426     }
12427     Roo.data.MemoryProxy.superclass.constructor.call(this);
12428     this.data = data;
12429 };
12430
12431 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12432     
12433     /**
12434      * Load data from the requested source (in this case an in-memory
12435      * data object passed to the constructor), read the data object into
12436      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12437      * process that block using the passed callback.
12438      * @param {Object} params This parameter is not used by the MemoryProxy class.
12439      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12440      * object into a block of Roo.data.Records.
12441      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12442      * The function must be passed <ul>
12443      * <li>The Record block object</li>
12444      * <li>The "arg" argument from the load function</li>
12445      * <li>A boolean success indicator</li>
12446      * </ul>
12447      * @param {Object} scope The scope in which to call the callback
12448      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12449      */
12450     load : function(params, reader, callback, scope, arg){
12451         params = params || {};
12452         var result;
12453         try {
12454             result = reader.readRecords(params.data ? params.data :this.data);
12455         }catch(e){
12456             this.fireEvent("loadexception", this, arg, null, e);
12457             callback.call(scope, null, arg, false);
12458             return;
12459         }
12460         callback.call(scope, result, arg, true);
12461     },
12462     
12463     // private
12464     update : function(params, records){
12465         
12466     }
12467 });/*
12468  * Based on:
12469  * Ext JS Library 1.1.1
12470  * Copyright(c) 2006-2007, Ext JS, LLC.
12471  *
12472  * Originally Released Under LGPL - original licence link has changed is not relivant.
12473  *
12474  * Fork - LGPL
12475  * <script type="text/javascript">
12476  */
12477 /**
12478  * @class Roo.data.HttpProxy
12479  * @extends Roo.data.DataProxy
12480  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12481  * configured to reference a certain URL.<br><br>
12482  * <p>
12483  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12484  * from which the running page was served.<br><br>
12485  * <p>
12486  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12487  * <p>
12488  * Be aware that to enable the browser to parse an XML document, the server must set
12489  * the Content-Type header in the HTTP response to "text/xml".
12490  * @constructor
12491  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12492  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12493  * will be used to make the request.
12494  */
12495 Roo.data.HttpProxy = function(conn){
12496     Roo.data.HttpProxy.superclass.constructor.call(this);
12497     // is conn a conn config or a real conn?
12498     this.conn = conn;
12499     this.useAjax = !conn || !conn.events;
12500   
12501 };
12502
12503 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12504     // thse are take from connection...
12505     
12506     /**
12507      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12508      */
12509     /**
12510      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12511      * extra parameters to each request made by this object. (defaults to undefined)
12512      */
12513     /**
12514      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12515      *  to each request made by this object. (defaults to undefined)
12516      */
12517     /**
12518      * @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)
12519      */
12520     /**
12521      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12522      */
12523      /**
12524      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12525      * @type Boolean
12526      */
12527   
12528
12529     /**
12530      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12531      * @type Boolean
12532      */
12533     /**
12534      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12535      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12536      * a finer-grained basis than the DataProxy events.
12537      */
12538     getConnection : function(){
12539         return this.useAjax ? Roo.Ajax : this.conn;
12540     },
12541
12542     /**
12543      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12544      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12545      * process that block using the passed callback.
12546      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12547      * for the request to the remote server.
12548      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12549      * object into a block of Roo.data.Records.
12550      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12551      * The function must be passed <ul>
12552      * <li>The Record block object</li>
12553      * <li>The "arg" argument from the load function</li>
12554      * <li>A boolean success indicator</li>
12555      * </ul>
12556      * @param {Object} scope The scope in which to call the callback
12557      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12558      */
12559     load : function(params, reader, callback, scope, arg){
12560         if(this.fireEvent("beforeload", this, params) !== false){
12561             var  o = {
12562                 params : params || {},
12563                 request: {
12564                     callback : callback,
12565                     scope : scope,
12566                     arg : arg
12567                 },
12568                 reader: reader,
12569                 callback : this.loadResponse,
12570                 scope: this
12571             };
12572             if(this.useAjax){
12573                 Roo.applyIf(o, this.conn);
12574                 if(this.activeRequest){
12575                     Roo.Ajax.abort(this.activeRequest);
12576                 }
12577                 this.activeRequest = Roo.Ajax.request(o);
12578             }else{
12579                 this.conn.request(o);
12580             }
12581         }else{
12582             callback.call(scope||this, null, arg, false);
12583         }
12584     },
12585
12586     // private
12587     loadResponse : function(o, success, response){
12588         delete this.activeRequest;
12589         if(!success){
12590             this.fireEvent("loadexception", this, o, response);
12591             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12592             return;
12593         }
12594         var result;
12595         try {
12596             result = o.reader.read(response);
12597         }catch(e){
12598             this.fireEvent("loadexception", this, o, response, e);
12599             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12600             return;
12601         }
12602         
12603         this.fireEvent("load", this, o, o.request.arg);
12604         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12605     },
12606
12607     // private
12608     update : function(dataSet){
12609
12610     },
12611
12612     // private
12613     updateResponse : function(dataSet){
12614
12615     }
12616 });/*
12617  * Based on:
12618  * Ext JS Library 1.1.1
12619  * Copyright(c) 2006-2007, Ext JS, LLC.
12620  *
12621  * Originally Released Under LGPL - original licence link has changed is not relivant.
12622  *
12623  * Fork - LGPL
12624  * <script type="text/javascript">
12625  */
12626
12627 /**
12628  * @class Roo.data.ScriptTagProxy
12629  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12630  * other than the originating domain of the running page.<br><br>
12631  * <p>
12632  * <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
12633  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12634  * <p>
12635  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12636  * source code that is used as the source inside a &lt;script> tag.<br><br>
12637  * <p>
12638  * In order for the browser to process the returned data, the server must wrap the data object
12639  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12640  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12641  * depending on whether the callback name was passed:
12642  * <p>
12643  * <pre><code>
12644 boolean scriptTag = false;
12645 String cb = request.getParameter("callback");
12646 if (cb != null) {
12647     scriptTag = true;
12648     response.setContentType("text/javascript");
12649 } else {
12650     response.setContentType("application/x-json");
12651 }
12652 Writer out = response.getWriter();
12653 if (scriptTag) {
12654     out.write(cb + "(");
12655 }
12656 out.print(dataBlock.toJsonString());
12657 if (scriptTag) {
12658     out.write(");");
12659 }
12660 </pre></code>
12661  *
12662  * @constructor
12663  * @param {Object} config A configuration object.
12664  */
12665 Roo.data.ScriptTagProxy = function(config){
12666     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12667     Roo.apply(this, config);
12668     this.head = document.getElementsByTagName("head")[0];
12669 };
12670
12671 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12672
12673 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12674     /**
12675      * @cfg {String} url The URL from which to request the data object.
12676      */
12677     /**
12678      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12679      */
12680     timeout : 30000,
12681     /**
12682      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12683      * the server the name of the callback function set up by the load call to process the returned data object.
12684      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12685      * javascript output which calls this named function passing the data object as its only parameter.
12686      */
12687     callbackParam : "callback",
12688     /**
12689      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12690      * name to the request.
12691      */
12692     nocache : true,
12693
12694     /**
12695      * Load data from the configured URL, read the data object into
12696      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12697      * process that block using the passed callback.
12698      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12699      * for the request to the remote server.
12700      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12701      * object into a block of Roo.data.Records.
12702      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12703      * The function must be passed <ul>
12704      * <li>The Record block object</li>
12705      * <li>The "arg" argument from the load function</li>
12706      * <li>A boolean success indicator</li>
12707      * </ul>
12708      * @param {Object} scope The scope in which to call the callback
12709      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12710      */
12711     load : function(params, reader, callback, scope, arg){
12712         if(this.fireEvent("beforeload", this, params) !== false){
12713
12714             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12715
12716             var url = this.url;
12717             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12718             if(this.nocache){
12719                 url += "&_dc=" + (new Date().getTime());
12720             }
12721             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12722             var trans = {
12723                 id : transId,
12724                 cb : "stcCallback"+transId,
12725                 scriptId : "stcScript"+transId,
12726                 params : params,
12727                 arg : arg,
12728                 url : url,
12729                 callback : callback,
12730                 scope : scope,
12731                 reader : reader
12732             };
12733             var conn = this;
12734
12735             window[trans.cb] = function(o){
12736                 conn.handleResponse(o, trans);
12737             };
12738
12739             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12740
12741             if(this.autoAbort !== false){
12742                 this.abort();
12743             }
12744
12745             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12746
12747             var script = document.createElement("script");
12748             script.setAttribute("src", url);
12749             script.setAttribute("type", "text/javascript");
12750             script.setAttribute("id", trans.scriptId);
12751             this.head.appendChild(script);
12752
12753             this.trans = trans;
12754         }else{
12755             callback.call(scope||this, null, arg, false);
12756         }
12757     },
12758
12759     // private
12760     isLoading : function(){
12761         return this.trans ? true : false;
12762     },
12763
12764     /**
12765      * Abort the current server request.
12766      */
12767     abort : function(){
12768         if(this.isLoading()){
12769             this.destroyTrans(this.trans);
12770         }
12771     },
12772
12773     // private
12774     destroyTrans : function(trans, isLoaded){
12775         this.head.removeChild(document.getElementById(trans.scriptId));
12776         clearTimeout(trans.timeoutId);
12777         if(isLoaded){
12778             window[trans.cb] = undefined;
12779             try{
12780                 delete window[trans.cb];
12781             }catch(e){}
12782         }else{
12783             // if hasn't been loaded, wait for load to remove it to prevent script error
12784             window[trans.cb] = function(){
12785                 window[trans.cb] = undefined;
12786                 try{
12787                     delete window[trans.cb];
12788                 }catch(e){}
12789             };
12790         }
12791     },
12792
12793     // private
12794     handleResponse : function(o, trans){
12795         this.trans = false;
12796         this.destroyTrans(trans, true);
12797         var result;
12798         try {
12799             result = trans.reader.readRecords(o);
12800         }catch(e){
12801             this.fireEvent("loadexception", this, o, trans.arg, e);
12802             trans.callback.call(trans.scope||window, null, trans.arg, false);
12803             return;
12804         }
12805         this.fireEvent("load", this, o, trans.arg);
12806         trans.callback.call(trans.scope||window, result, trans.arg, true);
12807     },
12808
12809     // private
12810     handleFailure : function(trans){
12811         this.trans = false;
12812         this.destroyTrans(trans, false);
12813         this.fireEvent("loadexception", this, null, trans.arg);
12814         trans.callback.call(trans.scope||window, null, trans.arg, false);
12815     }
12816 });/*
12817  * Based on:
12818  * Ext JS Library 1.1.1
12819  * Copyright(c) 2006-2007, Ext JS, LLC.
12820  *
12821  * Originally Released Under LGPL - original licence link has changed is not relivant.
12822  *
12823  * Fork - LGPL
12824  * <script type="text/javascript">
12825  */
12826
12827 /**
12828  * @class Roo.data.JsonReader
12829  * @extends Roo.data.DataReader
12830  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12831  * based on mappings in a provided Roo.data.Record constructor.
12832  * 
12833  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12834  * in the reply previously. 
12835  * 
12836  * <p>
12837  * Example code:
12838  * <pre><code>
12839 var RecordDef = Roo.data.Record.create([
12840     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12841     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12842 ]);
12843 var myReader = new Roo.data.JsonReader({
12844     totalProperty: "results",    // The property which contains the total dataset size (optional)
12845     root: "rows",                // The property which contains an Array of row objects
12846     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12847 }, RecordDef);
12848 </code></pre>
12849  * <p>
12850  * This would consume a JSON file like this:
12851  * <pre><code>
12852 { 'results': 2, 'rows': [
12853     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12854     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12855 }
12856 </code></pre>
12857  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12858  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12859  * paged from the remote server.
12860  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12861  * @cfg {String} root name of the property which contains the Array of row objects.
12862  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12863  * @cfg {Array} fields Array of field definition objects
12864  * @constructor
12865  * Create a new JsonReader
12866  * @param {Object} meta Metadata configuration options
12867  * @param {Object} recordType Either an Array of field definition objects,
12868  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12869  */
12870 Roo.data.JsonReader = function(meta, recordType){
12871     
12872     meta = meta || {};
12873     // set some defaults:
12874     Roo.applyIf(meta, {
12875         totalProperty: 'total',
12876         successProperty : 'success',
12877         root : 'data',
12878         id : 'id'
12879     });
12880     
12881     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12882 };
12883 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12884     
12885     /**
12886      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12887      * Used by Store query builder to append _requestMeta to params.
12888      * 
12889      */
12890     metaFromRemote : false,
12891     /**
12892      * This method is only used by a DataProxy which has retrieved data from a remote server.
12893      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12894      * @return {Object} data A data block which is used by an Roo.data.Store object as
12895      * a cache of Roo.data.Records.
12896      */
12897     read : function(response){
12898         var json = response.responseText;
12899        
12900         var o = /* eval:var:o */ eval("("+json+")");
12901         if(!o) {
12902             throw {message: "JsonReader.read: Json object not found"};
12903         }
12904         
12905         if(o.metaData){
12906             
12907             delete this.ef;
12908             this.metaFromRemote = true;
12909             this.meta = o.metaData;
12910             this.recordType = Roo.data.Record.create(o.metaData.fields);
12911             this.onMetaChange(this.meta, this.recordType, o);
12912         }
12913         return this.readRecords(o);
12914     },
12915
12916     // private function a store will implement
12917     onMetaChange : function(meta, recordType, o){
12918
12919     },
12920
12921     /**
12922          * @ignore
12923          */
12924     simpleAccess: function(obj, subsc) {
12925         return obj[subsc];
12926     },
12927
12928         /**
12929          * @ignore
12930          */
12931     getJsonAccessor: function(){
12932         var re = /[\[\.]/;
12933         return function(expr) {
12934             try {
12935                 return(re.test(expr))
12936                     ? new Function("obj", "return obj." + expr)
12937                     : function(obj){
12938                         return obj[expr];
12939                     };
12940             } catch(e){}
12941             return Roo.emptyFn;
12942         };
12943     }(),
12944
12945     /**
12946      * Create a data block containing Roo.data.Records from an XML document.
12947      * @param {Object} o An object which contains an Array of row objects in the property specified
12948      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12949      * which contains the total size of the dataset.
12950      * @return {Object} data A data block which is used by an Roo.data.Store object as
12951      * a cache of Roo.data.Records.
12952      */
12953     readRecords : function(o){
12954         /**
12955          * After any data loads, the raw JSON data is available for further custom processing.
12956          * @type Object
12957          */
12958         this.o = o;
12959         var s = this.meta, Record = this.recordType,
12960             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12961
12962 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12963         if (!this.ef) {
12964             if(s.totalProperty) {
12965                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12966                 }
12967                 if(s.successProperty) {
12968                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12969                 }
12970                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12971                 if (s.id) {
12972                         var g = this.getJsonAccessor(s.id);
12973                         this.getId = function(rec) {
12974                                 var r = g(rec);  
12975                                 return (r === undefined || r === "") ? null : r;
12976                         };
12977                 } else {
12978                         this.getId = function(){return null;};
12979                 }
12980             this.ef = [];
12981             for(var jj = 0; jj < fl; jj++){
12982                 f = fi[jj];
12983                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12984                 this.ef[jj] = this.getJsonAccessor(map);
12985             }
12986         }
12987
12988         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12989         if(s.totalProperty){
12990             var vt = parseInt(this.getTotal(o), 10);
12991             if(!isNaN(vt)){
12992                 totalRecords = vt;
12993             }
12994         }
12995         if(s.successProperty){
12996             var vs = this.getSuccess(o);
12997             if(vs === false || vs === 'false'){
12998                 success = false;
12999             }
13000         }
13001         var records = [];
13002         for(var i = 0; i < c; i++){
13003                 var n = root[i];
13004             var values = {};
13005             var id = this.getId(n);
13006             for(var j = 0; j < fl; j++){
13007                 f = fi[j];
13008             var v = this.ef[j](n);
13009             if (!f.convert) {
13010                 Roo.log('missing convert for ' + f.name);
13011                 Roo.log(f);
13012                 continue;
13013             }
13014             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13015             }
13016             var record = new Record(values, id);
13017             record.json = n;
13018             records[i] = record;
13019         }
13020         return {
13021             raw : o,
13022             success : success,
13023             records : records,
13024             totalRecords : totalRecords
13025         };
13026     }
13027 });/*
13028  * Based on:
13029  * Ext JS Library 1.1.1
13030  * Copyright(c) 2006-2007, Ext JS, LLC.
13031  *
13032  * Originally Released Under LGPL - original licence link has changed is not relivant.
13033  *
13034  * Fork - LGPL
13035  * <script type="text/javascript">
13036  */
13037
13038 /**
13039  * @class Roo.data.ArrayReader
13040  * @extends Roo.data.DataReader
13041  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13042  * Each element of that Array represents a row of data fields. The
13043  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13044  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13045  * <p>
13046  * Example code:.
13047  * <pre><code>
13048 var RecordDef = Roo.data.Record.create([
13049     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13050     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13051 ]);
13052 var myReader = new Roo.data.ArrayReader({
13053     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13054 }, RecordDef);
13055 </code></pre>
13056  * <p>
13057  * This would consume an Array like this:
13058  * <pre><code>
13059 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13060   </code></pre>
13061  
13062  * @constructor
13063  * Create a new JsonReader
13064  * @param {Object} meta Metadata configuration options.
13065  * @param {Object|Array} recordType Either an Array of field definition objects
13066  * 
13067  * @cfg {Array} fields Array of field definition objects
13068  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13069  * as specified to {@link Roo.data.Record#create},
13070  * or an {@link Roo.data.Record} object
13071  *
13072  * 
13073  * created using {@link Roo.data.Record#create}.
13074  */
13075 Roo.data.ArrayReader = function(meta, recordType){
13076     
13077      
13078     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13079 };
13080
13081 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13082     /**
13083      * Create a data block containing Roo.data.Records from an XML document.
13084      * @param {Object} o An Array of row objects which represents the dataset.
13085      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13086      * a cache of Roo.data.Records.
13087      */
13088     readRecords : function(o){
13089         var sid = this.meta ? this.meta.id : null;
13090         var recordType = this.recordType, fields = recordType.prototype.fields;
13091         var records = [];
13092         var root = o;
13093             for(var i = 0; i < root.length; i++){
13094                     var n = root[i];
13095                 var values = {};
13096                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13097                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13098                 var f = fields.items[j];
13099                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13100                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13101                 v = f.convert(v);
13102                 values[f.name] = v;
13103             }
13104                 var record = new recordType(values, id);
13105                 record.json = n;
13106                 records[records.length] = record;
13107             }
13108             return {
13109                 records : records,
13110                 totalRecords : records.length
13111             };
13112     }
13113 });/*
13114  * - LGPL
13115  * * 
13116  */
13117
13118 /**
13119  * @class Roo.bootstrap.ComboBox
13120  * @extends Roo.bootstrap.TriggerField
13121  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13122  * @cfg {Boolean} append (true|false) default false
13123  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13124  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13125  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13126  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13127  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13128  * @cfg {Boolean} animate default true
13129  * @cfg {Boolean} emptyResultText only for touch device
13130  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13131  * @cfg {String} emptyTitle default ''
13132  * @constructor
13133  * Create a new ComboBox.
13134  * @param {Object} config Configuration options
13135  */
13136 Roo.bootstrap.ComboBox = function(config){
13137     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13138     this.addEvents({
13139         /**
13140          * @event expand
13141          * Fires when the dropdown list is expanded
13142         * @param {Roo.bootstrap.ComboBox} combo This combo box
13143         */
13144         'expand' : true,
13145         /**
13146          * @event collapse
13147          * Fires when the dropdown list is collapsed
13148         * @param {Roo.bootstrap.ComboBox} combo This combo box
13149         */
13150         'collapse' : true,
13151         /**
13152          * @event beforeselect
13153          * Fires before a list item is selected. Return false to cancel the selection.
13154         * @param {Roo.bootstrap.ComboBox} combo This combo box
13155         * @param {Roo.data.Record} record The data record returned from the underlying store
13156         * @param {Number} index The index of the selected item in the dropdown list
13157         */
13158         'beforeselect' : true,
13159         /**
13160          * @event select
13161          * Fires when a list item is selected
13162         * @param {Roo.bootstrap.ComboBox} combo This combo box
13163         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13164         * @param {Number} index The index of the selected item in the dropdown list
13165         */
13166         'select' : true,
13167         /**
13168          * @event beforequery
13169          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13170          * The event object passed has these properties:
13171         * @param {Roo.bootstrap.ComboBox} combo This combo box
13172         * @param {String} query The query
13173         * @param {Boolean} forceAll true to force "all" query
13174         * @param {Boolean} cancel true to cancel the query
13175         * @param {Object} e The query event object
13176         */
13177         'beforequery': true,
13178          /**
13179          * @event add
13180          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13181         * @param {Roo.bootstrap.ComboBox} combo This combo box
13182         */
13183         'add' : true,
13184         /**
13185          * @event edit
13186          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13187         * @param {Roo.bootstrap.ComboBox} combo This combo box
13188         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13189         */
13190         'edit' : true,
13191         /**
13192          * @event remove
13193          * Fires when the remove value from the combobox array
13194         * @param {Roo.bootstrap.ComboBox} combo This combo box
13195         */
13196         'remove' : true,
13197         /**
13198          * @event afterremove
13199          * Fires when the remove value from the combobox array
13200         * @param {Roo.bootstrap.ComboBox} combo This combo box
13201         */
13202         'afterremove' : true,
13203         /**
13204          * @event specialfilter
13205          * Fires when specialfilter
13206             * @param {Roo.bootstrap.ComboBox} combo This combo box
13207             */
13208         'specialfilter' : true,
13209         /**
13210          * @event tick
13211          * Fires when tick the element
13212             * @param {Roo.bootstrap.ComboBox} combo This combo box
13213             */
13214         'tick' : true,
13215         /**
13216          * @event touchviewdisplay
13217          * Fires when touch view require special display (default is using displayField)
13218             * @param {Roo.bootstrap.ComboBox} combo This combo box
13219             * @param {Object} cfg set html .
13220             */
13221         'touchviewdisplay' : true
13222         
13223     });
13224     
13225     this.item = [];
13226     this.tickItems = [];
13227     
13228     this.selectedIndex = -1;
13229     if(this.mode == 'local'){
13230         if(config.queryDelay === undefined){
13231             this.queryDelay = 10;
13232         }
13233         if(config.minChars === undefined){
13234             this.minChars = 0;
13235         }
13236     }
13237 };
13238
13239 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13240      
13241     /**
13242      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13243      * rendering into an Roo.Editor, defaults to false)
13244      */
13245     /**
13246      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13247      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13248      */
13249     /**
13250      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13251      */
13252     /**
13253      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13254      * the dropdown list (defaults to undefined, with no header element)
13255      */
13256
13257      /**
13258      * @cfg {String/Roo.Template} tpl The template to use to render the output
13259      */
13260      
13261      /**
13262      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13263      */
13264     listWidth: undefined,
13265     /**
13266      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13267      * mode = 'remote' or 'text' if mode = 'local')
13268      */
13269     displayField: undefined,
13270     
13271     /**
13272      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13273      * mode = 'remote' or 'value' if mode = 'local'). 
13274      * Note: use of a valueField requires the user make a selection
13275      * in order for a value to be mapped.
13276      */
13277     valueField: undefined,
13278     /**
13279      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13280      */
13281     modalTitle : '',
13282     
13283     /**
13284      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13285      * field's data value (defaults to the underlying DOM element's name)
13286      */
13287     hiddenName: undefined,
13288     /**
13289      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13290      */
13291     listClass: '',
13292     /**
13293      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13294      */
13295     selectedClass: 'active',
13296     
13297     /**
13298      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13299      */
13300     shadow:'sides',
13301     /**
13302      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13303      * anchor positions (defaults to 'tl-bl')
13304      */
13305     listAlign: 'tl-bl?',
13306     /**
13307      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13308      */
13309     maxHeight: 300,
13310     /**
13311      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13312      * query specified by the allQuery config option (defaults to 'query')
13313      */
13314     triggerAction: 'query',
13315     /**
13316      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13317      * (defaults to 4, does not apply if editable = false)
13318      */
13319     minChars : 4,
13320     /**
13321      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13322      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13323      */
13324     typeAhead: false,
13325     /**
13326      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13327      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13328      */
13329     queryDelay: 500,
13330     /**
13331      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13332      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13333      */
13334     pageSize: 0,
13335     /**
13336      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13337      * when editable = true (defaults to false)
13338      */
13339     selectOnFocus:false,
13340     /**
13341      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13342      */
13343     queryParam: 'query',
13344     /**
13345      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13346      * when mode = 'remote' (defaults to 'Loading...')
13347      */
13348     loadingText: 'Loading...',
13349     /**
13350      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13351      */
13352     resizable: false,
13353     /**
13354      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13355      */
13356     handleHeight : 8,
13357     /**
13358      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13359      * traditional select (defaults to true)
13360      */
13361     editable: true,
13362     /**
13363      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13364      */
13365     allQuery: '',
13366     /**
13367      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13368      */
13369     mode: 'remote',
13370     /**
13371      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13372      * listWidth has a higher value)
13373      */
13374     minListWidth : 70,
13375     /**
13376      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13377      * allow the user to set arbitrary text into the field (defaults to false)
13378      */
13379     forceSelection:false,
13380     /**
13381      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13382      * if typeAhead = true (defaults to 250)
13383      */
13384     typeAheadDelay : 250,
13385     /**
13386      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13387      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13388      */
13389     valueNotFoundText : undefined,
13390     /**
13391      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13392      */
13393     blockFocus : false,
13394     
13395     /**
13396      * @cfg {Boolean} disableClear Disable showing of clear button.
13397      */
13398     disableClear : false,
13399     /**
13400      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13401      */
13402     alwaysQuery : false,
13403     
13404     /**
13405      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13406      */
13407     multiple : false,
13408     
13409     /**
13410      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13411      */
13412     invalidClass : "has-warning",
13413     
13414     /**
13415      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13416      */
13417     validClass : "has-success",
13418     
13419     /**
13420      * @cfg {Boolean} specialFilter (true|false) special filter default false
13421      */
13422     specialFilter : false,
13423     
13424     /**
13425      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13426      */
13427     mobileTouchView : true,
13428     
13429     /**
13430      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13431      */
13432     useNativeIOS : false,
13433     
13434     /**
13435      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13436      */
13437     mobile_restrict_height : false,
13438     
13439     ios_options : false,
13440     
13441     //private
13442     addicon : false,
13443     editicon: false,
13444     
13445     page: 0,
13446     hasQuery: false,
13447     append: false,
13448     loadNext: false,
13449     autoFocus : true,
13450     tickable : false,
13451     btnPosition : 'right',
13452     triggerList : true,
13453     showToggleBtn : true,
13454     animate : true,
13455     emptyResultText: 'Empty',
13456     triggerText : 'Select',
13457     emptyTitle : '',
13458     
13459     // element that contains real text value.. (when hidden is used..)
13460     
13461     getAutoCreate : function()
13462     {   
13463         var cfg = false;
13464         //render
13465         /*
13466          * Render classic select for iso
13467          */
13468         
13469         if(Roo.isIOS && this.useNativeIOS){
13470             cfg = this.getAutoCreateNativeIOS();
13471             return cfg;
13472         }
13473         
13474         /*
13475          * Touch Devices
13476          */
13477         
13478         if(Roo.isTouch && this.mobileTouchView){
13479             cfg = this.getAutoCreateTouchView();
13480             return cfg;;
13481         }
13482         
13483         /*
13484          *  Normal ComboBox
13485          */
13486         if(!this.tickable){
13487             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13488             return cfg;
13489         }
13490         
13491         /*
13492          *  ComboBox with tickable selections
13493          */
13494              
13495         var align = this.labelAlign || this.parentLabelAlign();
13496         
13497         cfg = {
13498             cls : 'form-group roo-combobox-tickable' //input-group
13499         };
13500         
13501         var btn_text_select = '';
13502         var btn_text_done = '';
13503         var btn_text_cancel = '';
13504         
13505         if (this.btn_text_show) {
13506             btn_text_select = 'Select';
13507             btn_text_done = 'Done';
13508             btn_text_cancel = 'Cancel'; 
13509         }
13510         
13511         var buttons = {
13512             tag : 'div',
13513             cls : 'tickable-buttons',
13514             cn : [
13515                 {
13516                     tag : 'button',
13517                     type : 'button',
13518                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13519                     //html : this.triggerText
13520                     html: btn_text_select
13521                 },
13522                 {
13523                     tag : 'button',
13524                     type : 'button',
13525                     name : 'ok',
13526                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13527                     //html : 'Done'
13528                     html: btn_text_done
13529                 },
13530                 {
13531                     tag : 'button',
13532                     type : 'button',
13533                     name : 'cancel',
13534                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13535                     //html : 'Cancel'
13536                     html: btn_text_cancel
13537                 }
13538             ]
13539         };
13540         
13541         if(this.editable){
13542             buttons.cn.unshift({
13543                 tag: 'input',
13544                 cls: 'roo-select2-search-field-input'
13545             });
13546         }
13547         
13548         var _this = this;
13549         
13550         Roo.each(buttons.cn, function(c){
13551             if (_this.size) {
13552                 c.cls += ' btn-' + _this.size;
13553             }
13554
13555             if (_this.disabled) {
13556                 c.disabled = true;
13557             }
13558         });
13559         
13560         var box = {
13561             tag: 'div',
13562             style : 'display: contents',
13563             cn: [
13564                 {
13565                     tag: 'input',
13566                     type : 'hidden',
13567                     cls: 'form-hidden-field'
13568                 },
13569                 {
13570                     tag: 'ul',
13571                     cls: 'roo-select2-choices',
13572                     cn:[
13573                         {
13574                             tag: 'li',
13575                             cls: 'roo-select2-search-field',
13576                             cn: [
13577                                 buttons
13578                             ]
13579                         }
13580                     ]
13581                 }
13582             ]
13583         };
13584         
13585         var combobox = {
13586             cls: 'roo-select2-container input-group roo-select2-container-multi',
13587             cn: [
13588                 
13589                 box
13590 //                {
13591 //                    tag: 'ul',
13592 //                    cls: 'typeahead typeahead-long dropdown-menu',
13593 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13594 //                }
13595             ]
13596         };
13597         
13598         if(this.hasFeedback && !this.allowBlank){
13599             
13600             var feedback = {
13601                 tag: 'span',
13602                 cls: 'glyphicon form-control-feedback'
13603             };
13604
13605             combobox.cn.push(feedback);
13606         }
13607         
13608         var indicator = {
13609             tag : 'i',
13610             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13611             tooltip : 'This field is required'
13612         };
13613         if (Roo.bootstrap.version == 4) {
13614             indicator = {
13615                 tag : 'i',
13616                 style : 'display:none'
13617             };
13618         }
13619         if (align ==='left' && this.fieldLabel.length) {
13620             
13621             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13622             
13623             cfg.cn = [
13624                 indicator,
13625                 {
13626                     tag: 'label',
13627                     'for' :  id,
13628                     cls : 'control-label col-form-label',
13629                     html : this.fieldLabel
13630
13631                 },
13632                 {
13633                     cls : "", 
13634                     cn: [
13635                         combobox
13636                     ]
13637                 }
13638
13639             ];
13640             
13641             var labelCfg = cfg.cn[1];
13642             var contentCfg = cfg.cn[2];
13643             
13644
13645             if(this.indicatorpos == 'right'){
13646                 
13647                 cfg.cn = [
13648                     {
13649                         tag: 'label',
13650                         'for' :  id,
13651                         cls : 'control-label col-form-label',
13652                         cn : [
13653                             {
13654                                 tag : 'span',
13655                                 html : this.fieldLabel
13656                             },
13657                             indicator
13658                         ]
13659                     },
13660                     {
13661                         cls : "",
13662                         cn: [
13663                             combobox
13664                         ]
13665                     }
13666
13667                 ];
13668                 
13669                 
13670                 
13671                 labelCfg = cfg.cn[0];
13672                 contentCfg = cfg.cn[1];
13673             
13674             }
13675             
13676             if(this.labelWidth > 12){
13677                 labelCfg.style = "width: " + this.labelWidth + 'px';
13678             }
13679             
13680             if(this.labelWidth < 13 && this.labelmd == 0){
13681                 this.labelmd = this.labelWidth;
13682             }
13683             
13684             if(this.labellg > 0){
13685                 labelCfg.cls += ' col-lg-' + this.labellg;
13686                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13687             }
13688             
13689             if(this.labelmd > 0){
13690                 labelCfg.cls += ' col-md-' + this.labelmd;
13691                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13692             }
13693             
13694             if(this.labelsm > 0){
13695                 labelCfg.cls += ' col-sm-' + this.labelsm;
13696                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13697             }
13698             
13699             if(this.labelxs > 0){
13700                 labelCfg.cls += ' col-xs-' + this.labelxs;
13701                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13702             }
13703                 
13704                 
13705         } else if ( this.fieldLabel.length) {
13706 //                Roo.log(" label");
13707                  cfg.cn = [
13708                    indicator,
13709                     {
13710                         tag: 'label',
13711                         //cls : 'input-group-addon',
13712                         html : this.fieldLabel
13713                     },
13714                     combobox
13715                 ];
13716                 
13717                 if(this.indicatorpos == 'right'){
13718                     cfg.cn = [
13719                         {
13720                             tag: 'label',
13721                             //cls : 'input-group-addon',
13722                             html : this.fieldLabel
13723                         },
13724                         indicator,
13725                         combobox
13726                     ];
13727                     
13728                 }
13729
13730         } else {
13731             
13732 //                Roo.log(" no label && no align");
13733                 cfg = combobox
13734                      
13735                 
13736         }
13737          
13738         var settings=this;
13739         ['xs','sm','md','lg'].map(function(size){
13740             if (settings[size]) {
13741                 cfg.cls += ' col-' + size + '-' + settings[size];
13742             }
13743         });
13744         
13745         return cfg;
13746         
13747     },
13748     
13749     _initEventsCalled : false,
13750     
13751     // private
13752     initEvents: function()
13753     {   
13754         if (this._initEventsCalled) { // as we call render... prevent looping...
13755             return;
13756         }
13757         this._initEventsCalled = true;
13758         
13759         if (!this.store) {
13760             throw "can not find store for combo";
13761         }
13762         
13763         this.indicator = this.indicatorEl();
13764         
13765         this.store = Roo.factory(this.store, Roo.data);
13766         this.store.parent = this;
13767         
13768         // if we are building from html. then this element is so complex, that we can not really
13769         // use the rendered HTML.
13770         // so we have to trash and replace the previous code.
13771         if (Roo.XComponent.build_from_html) {
13772             // remove this element....
13773             var e = this.el.dom, k=0;
13774             while (e ) { e = e.previousSibling;  ++k;}
13775
13776             this.el.remove();
13777             
13778             this.el=false;
13779             this.rendered = false;
13780             
13781             this.render(this.parent().getChildContainer(true), k);
13782         }
13783         
13784         if(Roo.isIOS && this.useNativeIOS){
13785             this.initIOSView();
13786             return;
13787         }
13788         
13789         /*
13790          * Touch Devices
13791          */
13792         
13793         if(Roo.isTouch && this.mobileTouchView){
13794             this.initTouchView();
13795             return;
13796         }
13797         
13798         if(this.tickable){
13799             this.initTickableEvents();
13800             return;
13801         }
13802         
13803         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13804         
13805         if(this.hiddenName){
13806             
13807             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13808             
13809             this.hiddenField.dom.value =
13810                 this.hiddenValue !== undefined ? this.hiddenValue :
13811                 this.value !== undefined ? this.value : '';
13812
13813             // prevent input submission
13814             this.el.dom.removeAttribute('name');
13815             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13816              
13817              
13818         }
13819         //if(Roo.isGecko){
13820         //    this.el.dom.setAttribute('autocomplete', 'off');
13821         //}
13822         
13823         var cls = 'x-combo-list';
13824         
13825         //this.list = new Roo.Layer({
13826         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13827         //});
13828         
13829         var _this = this;
13830         
13831         (function(){
13832             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13833             _this.list.setWidth(lw);
13834         }).defer(100);
13835         
13836         this.list.on('mouseover', this.onViewOver, this);
13837         this.list.on('mousemove', this.onViewMove, this);
13838         this.list.on('scroll', this.onViewScroll, this);
13839         
13840         /*
13841         this.list.swallowEvent('mousewheel');
13842         this.assetHeight = 0;
13843
13844         if(this.title){
13845             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13846             this.assetHeight += this.header.getHeight();
13847         }
13848
13849         this.innerList = this.list.createChild({cls:cls+'-inner'});
13850         this.innerList.on('mouseover', this.onViewOver, this);
13851         this.innerList.on('mousemove', this.onViewMove, this);
13852         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13853         
13854         if(this.allowBlank && !this.pageSize && !this.disableClear){
13855             this.footer = this.list.createChild({cls:cls+'-ft'});
13856             this.pageTb = new Roo.Toolbar(this.footer);
13857            
13858         }
13859         if(this.pageSize){
13860             this.footer = this.list.createChild({cls:cls+'-ft'});
13861             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13862                     {pageSize: this.pageSize});
13863             
13864         }
13865         
13866         if (this.pageTb && this.allowBlank && !this.disableClear) {
13867             var _this = this;
13868             this.pageTb.add(new Roo.Toolbar.Fill(), {
13869                 cls: 'x-btn-icon x-btn-clear',
13870                 text: '&#160;',
13871                 handler: function()
13872                 {
13873                     _this.collapse();
13874                     _this.clearValue();
13875                     _this.onSelect(false, -1);
13876                 }
13877             });
13878         }
13879         if (this.footer) {
13880             this.assetHeight += this.footer.getHeight();
13881         }
13882         */
13883             
13884         if(!this.tpl){
13885             this.tpl = Roo.bootstrap.version == 4 ?
13886                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13887                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13888         }
13889
13890         this.view = new Roo.View(this.list, this.tpl, {
13891             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13892         });
13893         //this.view.wrapEl.setDisplayed(false);
13894         this.view.on('click', this.onViewClick, this);
13895         
13896         
13897         this.store.on('beforeload', this.onBeforeLoad, this);
13898         this.store.on('load', this.onLoad, this);
13899         this.store.on('loadexception', this.onLoadException, this);
13900         /*
13901         if(this.resizable){
13902             this.resizer = new Roo.Resizable(this.list,  {
13903                pinned:true, handles:'se'
13904             });
13905             this.resizer.on('resize', function(r, w, h){
13906                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13907                 this.listWidth = w;
13908                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13909                 this.restrictHeight();
13910             }, this);
13911             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13912         }
13913         */
13914         if(!this.editable){
13915             this.editable = true;
13916             this.setEditable(false);
13917         }
13918         
13919         /*
13920         
13921         if (typeof(this.events.add.listeners) != 'undefined') {
13922             
13923             this.addicon = this.wrap.createChild(
13924                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13925        
13926             this.addicon.on('click', function(e) {
13927                 this.fireEvent('add', this);
13928             }, this);
13929         }
13930         if (typeof(this.events.edit.listeners) != 'undefined') {
13931             
13932             this.editicon = this.wrap.createChild(
13933                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13934             if (this.addicon) {
13935                 this.editicon.setStyle('margin-left', '40px');
13936             }
13937             this.editicon.on('click', function(e) {
13938                 
13939                 // we fire even  if inothing is selected..
13940                 this.fireEvent('edit', this, this.lastData );
13941                 
13942             }, this);
13943         }
13944         */
13945         
13946         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13947             "up" : function(e){
13948                 this.inKeyMode = true;
13949                 this.selectPrev();
13950             },
13951
13952             "down" : function(e){
13953                 if(!this.isExpanded()){
13954                     this.onTriggerClick();
13955                 }else{
13956                     this.inKeyMode = true;
13957                     this.selectNext();
13958                 }
13959             },
13960
13961             "enter" : function(e){
13962 //                this.onViewClick();
13963                 //return true;
13964                 this.collapse();
13965                 
13966                 if(this.fireEvent("specialkey", this, e)){
13967                     this.onViewClick(false);
13968                 }
13969                 
13970                 return true;
13971             },
13972
13973             "esc" : function(e){
13974                 this.collapse();
13975             },
13976
13977             "tab" : function(e){
13978                 this.collapse();
13979                 
13980                 if(this.fireEvent("specialkey", this, e)){
13981                     this.onViewClick(false);
13982                 }
13983                 
13984                 return true;
13985             },
13986
13987             scope : this,
13988
13989             doRelay : function(foo, bar, hname){
13990                 if(hname == 'down' || this.scope.isExpanded()){
13991                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13992                 }
13993                 return true;
13994             },
13995
13996             forceKeyDown: true
13997         });
13998         
13999         
14000         this.queryDelay = Math.max(this.queryDelay || 10,
14001                 this.mode == 'local' ? 10 : 250);
14002         
14003         
14004         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14005         
14006         if(this.typeAhead){
14007             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14008         }
14009         if(this.editable !== false){
14010             this.inputEl().on("keyup", this.onKeyUp, this);
14011         }
14012         if(this.forceSelection){
14013             this.inputEl().on('blur', this.doForce, this);
14014         }
14015         
14016         if(this.multiple){
14017             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14018             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14019         }
14020     },
14021     
14022     initTickableEvents: function()
14023     {   
14024         this.createList();
14025         
14026         if(this.hiddenName){
14027             
14028             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14029             
14030             this.hiddenField.dom.value =
14031                 this.hiddenValue !== undefined ? this.hiddenValue :
14032                 this.value !== undefined ? this.value : '';
14033
14034             // prevent input submission
14035             this.el.dom.removeAttribute('name');
14036             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14037              
14038              
14039         }
14040         
14041 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14042         
14043         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14044         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14045         if(this.triggerList){
14046             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14047         }
14048          
14049         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14050         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14051         
14052         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14053         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14054         
14055         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14056         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14057         
14058         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14059         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14060         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14061         
14062         this.okBtn.hide();
14063         this.cancelBtn.hide();
14064         
14065         var _this = this;
14066         
14067         (function(){
14068             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14069             _this.list.setWidth(lw);
14070         }).defer(100);
14071         
14072         this.list.on('mouseover', this.onViewOver, this);
14073         this.list.on('mousemove', this.onViewMove, this);
14074         
14075         this.list.on('scroll', this.onViewScroll, this);
14076         
14077         if(!this.tpl){
14078             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14079                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14080         }
14081
14082         this.view = new Roo.View(this.list, this.tpl, {
14083             singleSelect:true,
14084             tickable:true,
14085             parent:this,
14086             store: this.store,
14087             selectedClass: this.selectedClass
14088         });
14089         
14090         //this.view.wrapEl.setDisplayed(false);
14091         this.view.on('click', this.onViewClick, this);
14092         
14093         
14094         
14095         this.store.on('beforeload', this.onBeforeLoad, this);
14096         this.store.on('load', this.onLoad, this);
14097         this.store.on('loadexception', this.onLoadException, this);
14098         
14099         if(this.editable){
14100             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14101                 "up" : function(e){
14102                     this.inKeyMode = true;
14103                     this.selectPrev();
14104                 },
14105
14106                 "down" : function(e){
14107                     this.inKeyMode = true;
14108                     this.selectNext();
14109                 },
14110
14111                 "enter" : function(e){
14112                     if(this.fireEvent("specialkey", this, e)){
14113                         this.onViewClick(false);
14114                     }
14115                     
14116                     return true;
14117                 },
14118
14119                 "esc" : function(e){
14120                     this.onTickableFooterButtonClick(e, false, false);
14121                 },
14122
14123                 "tab" : function(e){
14124                     this.fireEvent("specialkey", this, e);
14125                     
14126                     this.onTickableFooterButtonClick(e, false, false);
14127                     
14128                     return true;
14129                 },
14130
14131                 scope : this,
14132
14133                 doRelay : function(e, fn, key){
14134                     if(this.scope.isExpanded()){
14135                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14136                     }
14137                     return true;
14138                 },
14139
14140                 forceKeyDown: true
14141             });
14142         }
14143         
14144         this.queryDelay = Math.max(this.queryDelay || 10,
14145                 this.mode == 'local' ? 10 : 250);
14146         
14147         
14148         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14149         
14150         if(this.typeAhead){
14151             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14152         }
14153         
14154         if(this.editable !== false){
14155             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14156         }
14157         
14158         this.indicator = this.indicatorEl();
14159         
14160         if(this.indicator){
14161             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14162             this.indicator.hide();
14163         }
14164         
14165     },
14166
14167     onDestroy : function(){
14168         if(this.view){
14169             this.view.setStore(null);
14170             this.view.el.removeAllListeners();
14171             this.view.el.remove();
14172             this.view.purgeListeners();
14173         }
14174         if(this.list){
14175             this.list.dom.innerHTML  = '';
14176         }
14177         
14178         if(this.store){
14179             this.store.un('beforeload', this.onBeforeLoad, this);
14180             this.store.un('load', this.onLoad, this);
14181             this.store.un('loadexception', this.onLoadException, this);
14182         }
14183         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14184     },
14185
14186     // private
14187     fireKey : function(e){
14188         if(e.isNavKeyPress() && !this.list.isVisible()){
14189             this.fireEvent("specialkey", this, e);
14190         }
14191     },
14192
14193     // private
14194     onResize: function(w, h){
14195 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14196 //        
14197 //        if(typeof w != 'number'){
14198 //            // we do not handle it!?!?
14199 //            return;
14200 //        }
14201 //        var tw = this.trigger.getWidth();
14202 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14203 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14204 //        var x = w - tw;
14205 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14206 //            
14207 //        //this.trigger.setStyle('left', x+'px');
14208 //        
14209 //        if(this.list && this.listWidth === undefined){
14210 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14211 //            this.list.setWidth(lw);
14212 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14213 //        }
14214         
14215     
14216         
14217     },
14218
14219     /**
14220      * Allow or prevent the user from directly editing the field text.  If false is passed,
14221      * the user will only be able to select from the items defined in the dropdown list.  This method
14222      * is the runtime equivalent of setting the 'editable' config option at config time.
14223      * @param {Boolean} value True to allow the user to directly edit the field text
14224      */
14225     setEditable : function(value){
14226         if(value == this.editable){
14227             return;
14228         }
14229         this.editable = value;
14230         if(!value){
14231             this.inputEl().dom.setAttribute('readOnly', true);
14232             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14233             this.inputEl().addClass('x-combo-noedit');
14234         }else{
14235             this.inputEl().dom.setAttribute('readOnly', false);
14236             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14237             this.inputEl().removeClass('x-combo-noedit');
14238         }
14239     },
14240
14241     // private
14242     
14243     onBeforeLoad : function(combo,opts){
14244         if(!this.hasFocus){
14245             return;
14246         }
14247          if (!opts.add) {
14248             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14249          }
14250         this.restrictHeight();
14251         this.selectedIndex = -1;
14252     },
14253
14254     // private
14255     onLoad : function(){
14256         
14257         this.hasQuery = false;
14258         
14259         if(!this.hasFocus){
14260             return;
14261         }
14262         
14263         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14264             this.loading.hide();
14265         }
14266         
14267         if(this.store.getCount() > 0){
14268             
14269             this.expand();
14270             this.restrictHeight();
14271             if(this.lastQuery == this.allQuery){
14272                 if(this.editable && !this.tickable){
14273                     this.inputEl().dom.select();
14274                 }
14275                 
14276                 if(
14277                     !this.selectByValue(this.value, true) &&
14278                     this.autoFocus && 
14279                     (
14280                         !this.store.lastOptions ||
14281                         typeof(this.store.lastOptions.add) == 'undefined' || 
14282                         this.store.lastOptions.add != true
14283                     )
14284                 ){
14285                     this.select(0, true);
14286                 }
14287             }else{
14288                 if(this.autoFocus){
14289                     this.selectNext();
14290                 }
14291                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14292                     this.taTask.delay(this.typeAheadDelay);
14293                 }
14294             }
14295         }else{
14296             this.onEmptyResults();
14297         }
14298         
14299         //this.el.focus();
14300     },
14301     // private
14302     onLoadException : function()
14303     {
14304         this.hasQuery = false;
14305         
14306         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14307             this.loading.hide();
14308         }
14309         
14310         if(this.tickable && this.editable){
14311             return;
14312         }
14313         
14314         this.collapse();
14315         // only causes errors at present
14316         //Roo.log(this.store.reader.jsonData);
14317         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14318             // fixme
14319             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14320         //}
14321         
14322         
14323     },
14324     // private
14325     onTypeAhead : function(){
14326         if(this.store.getCount() > 0){
14327             var r = this.store.getAt(0);
14328             var newValue = r.data[this.displayField];
14329             var len = newValue.length;
14330             var selStart = this.getRawValue().length;
14331             
14332             if(selStart != len){
14333                 this.setRawValue(newValue);
14334                 this.selectText(selStart, newValue.length);
14335             }
14336         }
14337     },
14338
14339     // private
14340     onSelect : function(record, index){
14341         
14342         if(this.fireEvent('beforeselect', this, record, index) !== false){
14343         
14344             this.setFromData(index > -1 ? record.data : false);
14345             
14346             this.collapse();
14347             this.fireEvent('select', this, record, index);
14348         }
14349     },
14350
14351     /**
14352      * Returns the currently selected field value or empty string if no value is set.
14353      * @return {String} value The selected value
14354      */
14355     getValue : function()
14356     {
14357         if(Roo.isIOS && this.useNativeIOS){
14358             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14359         }
14360         
14361         if(this.multiple){
14362             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14363         }
14364         
14365         if(this.valueField){
14366             return typeof this.value != 'undefined' ? this.value : '';
14367         }else{
14368             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14369         }
14370     },
14371     
14372     getRawValue : function()
14373     {
14374         if(Roo.isIOS && this.useNativeIOS){
14375             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14376         }
14377         
14378         var v = this.inputEl().getValue();
14379         
14380         return v;
14381     },
14382
14383     /**
14384      * Clears any text/value currently set in the field
14385      */
14386     clearValue : function(){
14387         
14388         if(this.hiddenField){
14389             this.hiddenField.dom.value = '';
14390         }
14391         this.value = '';
14392         this.setRawValue('');
14393         this.lastSelectionText = '';
14394         this.lastData = false;
14395         
14396         var close = this.closeTriggerEl();
14397         
14398         if(close){
14399             close.hide();
14400         }
14401         
14402         this.validate();
14403         
14404     },
14405
14406     /**
14407      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14408      * will be displayed in the field.  If the value does not match the data value of an existing item,
14409      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14410      * Otherwise the field will be blank (although the value will still be set).
14411      * @param {String} value The value to match
14412      */
14413     setValue : function(v)
14414     {
14415         if(Roo.isIOS && this.useNativeIOS){
14416             this.setIOSValue(v);
14417             return;
14418         }
14419         
14420         if(this.multiple){
14421             this.syncValue();
14422             return;
14423         }
14424         
14425         var text = v;
14426         if(this.valueField){
14427             var r = this.findRecord(this.valueField, v);
14428             if(r){
14429                 text = r.data[this.displayField];
14430             }else if(this.valueNotFoundText !== undefined){
14431                 text = this.valueNotFoundText;
14432             }
14433         }
14434         this.lastSelectionText = text;
14435         if(this.hiddenField){
14436             this.hiddenField.dom.value = v;
14437         }
14438         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14439         this.value = v;
14440         
14441         var close = this.closeTriggerEl();
14442         
14443         if(close){
14444             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14445         }
14446         
14447         this.validate();
14448     },
14449     /**
14450      * @property {Object} the last set data for the element
14451      */
14452     
14453     lastData : false,
14454     /**
14455      * Sets the value of the field based on a object which is related to the record format for the store.
14456      * @param {Object} value the value to set as. or false on reset?
14457      */
14458     setFromData : function(o){
14459         
14460         if(this.multiple){
14461             this.addItem(o);
14462             return;
14463         }
14464             
14465         var dv = ''; // display value
14466         var vv = ''; // value value..
14467         this.lastData = o;
14468         if (this.displayField) {
14469             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14470         } else {
14471             // this is an error condition!!!
14472             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14473         }
14474         
14475         if(this.valueField){
14476             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14477         }
14478         
14479         var close = this.closeTriggerEl();
14480         
14481         if(close){
14482             if(dv.length || vv * 1 > 0){
14483                 close.show() ;
14484                 this.blockFocus=true;
14485             } else {
14486                 close.hide();
14487             }             
14488         }
14489         
14490         if(this.hiddenField){
14491             this.hiddenField.dom.value = vv;
14492             
14493             this.lastSelectionText = dv;
14494             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14495             this.value = vv;
14496             return;
14497         }
14498         // no hidden field.. - we store the value in 'value', but still display
14499         // display field!!!!
14500         this.lastSelectionText = dv;
14501         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14502         this.value = vv;
14503         
14504         
14505         
14506     },
14507     // private
14508     reset : function(){
14509         // overridden so that last data is reset..
14510         
14511         if(this.multiple){
14512             this.clearItem();
14513             return;
14514         }
14515         
14516         this.setValue(this.originalValue);
14517         //this.clearInvalid();
14518         this.lastData = false;
14519         if (this.view) {
14520             this.view.clearSelections();
14521         }
14522         
14523         this.validate();
14524     },
14525     // private
14526     findRecord : function(prop, value){
14527         var record;
14528         if(this.store.getCount() > 0){
14529             this.store.each(function(r){
14530                 if(r.data[prop] == value){
14531                     record = r;
14532                     return false;
14533                 }
14534                 return true;
14535             });
14536         }
14537         return record;
14538     },
14539     
14540     getName: function()
14541     {
14542         // returns hidden if it's set..
14543         if (!this.rendered) {return ''};
14544         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14545         
14546     },
14547     // private
14548     onViewMove : function(e, t){
14549         this.inKeyMode = false;
14550     },
14551
14552     // private
14553     onViewOver : function(e, t){
14554         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14555             return;
14556         }
14557         var item = this.view.findItemFromChild(t);
14558         
14559         if(item){
14560             var index = this.view.indexOf(item);
14561             this.select(index, false);
14562         }
14563     },
14564
14565     // private
14566     onViewClick : function(view, doFocus, el, e)
14567     {
14568         var index = this.view.getSelectedIndexes()[0];
14569         
14570         var r = this.store.getAt(index);
14571         
14572         if(this.tickable){
14573             
14574             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14575                 return;
14576             }
14577             
14578             var rm = false;
14579             var _this = this;
14580             
14581             Roo.each(this.tickItems, function(v,k){
14582                 
14583                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14584                     Roo.log(v);
14585                     _this.tickItems.splice(k, 1);
14586                     
14587                     if(typeof(e) == 'undefined' && view == false){
14588                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14589                     }
14590                     
14591                     rm = true;
14592                     return;
14593                 }
14594             });
14595             
14596             if(rm){
14597                 return;
14598             }
14599             
14600             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14601                 this.tickItems.push(r.data);
14602             }
14603             
14604             if(typeof(e) == 'undefined' && view == false){
14605                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14606             }
14607                     
14608             return;
14609         }
14610         
14611         if(r){
14612             this.onSelect(r, index);
14613         }
14614         if(doFocus !== false && !this.blockFocus){
14615             this.inputEl().focus();
14616         }
14617     },
14618
14619     // private
14620     restrictHeight : function(){
14621         //this.innerList.dom.style.height = '';
14622         //var inner = this.innerList.dom;
14623         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14624         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14625         //this.list.beginUpdate();
14626         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14627         this.list.alignTo(this.inputEl(), this.listAlign);
14628         this.list.alignTo(this.inputEl(), this.listAlign);
14629         //this.list.endUpdate();
14630     },
14631
14632     // private
14633     onEmptyResults : function(){
14634         
14635         if(this.tickable && this.editable){
14636             this.hasFocus = false;
14637             this.restrictHeight();
14638             return;
14639         }
14640         
14641         this.collapse();
14642     },
14643
14644     /**
14645      * Returns true if the dropdown list is expanded, else false.
14646      */
14647     isExpanded : function(){
14648         return this.list.isVisible();
14649     },
14650
14651     /**
14652      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14653      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14654      * @param {String} value The data value of the item to select
14655      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14656      * selected item if it is not currently in view (defaults to true)
14657      * @return {Boolean} True if the value matched an item in the list, else false
14658      */
14659     selectByValue : function(v, scrollIntoView){
14660         if(v !== undefined && v !== null){
14661             var r = this.findRecord(this.valueField || this.displayField, v);
14662             if(r){
14663                 this.select(this.store.indexOf(r), scrollIntoView);
14664                 return true;
14665             }
14666         }
14667         return false;
14668     },
14669
14670     /**
14671      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14672      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14673      * @param {Number} index The zero-based index of the list item to select
14674      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14675      * selected item if it is not currently in view (defaults to true)
14676      */
14677     select : function(index, scrollIntoView){
14678         this.selectedIndex = index;
14679         this.view.select(index);
14680         if(scrollIntoView !== false){
14681             var el = this.view.getNode(index);
14682             /*
14683              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14684              */
14685             if(el){
14686                 this.list.scrollChildIntoView(el, false);
14687             }
14688         }
14689     },
14690
14691     // private
14692     selectNext : function(){
14693         var ct = this.store.getCount();
14694         if(ct > 0){
14695             if(this.selectedIndex == -1){
14696                 this.select(0);
14697             }else if(this.selectedIndex < ct-1){
14698                 this.select(this.selectedIndex+1);
14699             }
14700         }
14701     },
14702
14703     // private
14704     selectPrev : function(){
14705         var ct = this.store.getCount();
14706         if(ct > 0){
14707             if(this.selectedIndex == -1){
14708                 this.select(0);
14709             }else if(this.selectedIndex != 0){
14710                 this.select(this.selectedIndex-1);
14711             }
14712         }
14713     },
14714
14715     // private
14716     onKeyUp : function(e){
14717         if(this.editable !== false && !e.isSpecialKey()){
14718             this.lastKey = e.getKey();
14719             this.dqTask.delay(this.queryDelay);
14720         }
14721     },
14722
14723     // private
14724     validateBlur : function(){
14725         return !this.list || !this.list.isVisible();   
14726     },
14727
14728     // private
14729     initQuery : function(){
14730         
14731         var v = this.getRawValue();
14732         
14733         if(this.tickable && this.editable){
14734             v = this.tickableInputEl().getValue();
14735         }
14736         
14737         this.doQuery(v);
14738     },
14739
14740     // private
14741     doForce : function(){
14742         if(this.inputEl().dom.value.length > 0){
14743             this.inputEl().dom.value =
14744                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14745              
14746         }
14747     },
14748
14749     /**
14750      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14751      * query allowing the query action to be canceled if needed.
14752      * @param {String} query The SQL query to execute
14753      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14754      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14755      * saved in the current store (defaults to false)
14756      */
14757     doQuery : function(q, forceAll){
14758         
14759         if(q === undefined || q === null){
14760             q = '';
14761         }
14762         var qe = {
14763             query: q,
14764             forceAll: forceAll,
14765             combo: this,
14766             cancel:false
14767         };
14768         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14769             return false;
14770         }
14771         q = qe.query;
14772         
14773         forceAll = qe.forceAll;
14774         if(forceAll === true || (q.length >= this.minChars)){
14775             
14776             this.hasQuery = true;
14777             
14778             if(this.lastQuery != q || this.alwaysQuery){
14779                 this.lastQuery = q;
14780                 if(this.mode == 'local'){
14781                     this.selectedIndex = -1;
14782                     if(forceAll){
14783                         this.store.clearFilter();
14784                     }else{
14785                         
14786                         if(this.specialFilter){
14787                             this.fireEvent('specialfilter', this);
14788                             this.onLoad();
14789                             return;
14790                         }
14791                         
14792                         this.store.filter(this.displayField, q);
14793                     }
14794                     
14795                     this.store.fireEvent("datachanged", this.store);
14796                     
14797                     this.onLoad();
14798                     
14799                     
14800                 }else{
14801                     
14802                     this.store.baseParams[this.queryParam] = q;
14803                     
14804                     var options = {params : this.getParams(q)};
14805                     
14806                     if(this.loadNext){
14807                         options.add = true;
14808                         options.params.start = this.page * this.pageSize;
14809                     }
14810                     
14811                     this.store.load(options);
14812                     
14813                     /*
14814                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14815                      *  we should expand the list on onLoad
14816                      *  so command out it
14817                      */
14818 //                    this.expand();
14819                 }
14820             }else{
14821                 this.selectedIndex = -1;
14822                 this.onLoad();   
14823             }
14824         }
14825         
14826         this.loadNext = false;
14827     },
14828     
14829     // private
14830     getParams : function(q){
14831         var p = {};
14832         //p[this.queryParam] = q;
14833         
14834         if(this.pageSize){
14835             p.start = 0;
14836             p.limit = this.pageSize;
14837         }
14838         return p;
14839     },
14840
14841     /**
14842      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14843      */
14844     collapse : function(){
14845         if(!this.isExpanded()){
14846             return;
14847         }
14848         
14849         this.list.hide();
14850         
14851         this.hasFocus = false;
14852         
14853         if(this.tickable){
14854             this.okBtn.hide();
14855             this.cancelBtn.hide();
14856             this.trigger.show();
14857             
14858             if(this.editable){
14859                 this.tickableInputEl().dom.value = '';
14860                 this.tickableInputEl().blur();
14861             }
14862             
14863         }
14864         
14865         Roo.get(document).un('mousedown', this.collapseIf, this);
14866         Roo.get(document).un('mousewheel', this.collapseIf, this);
14867         if (!this.editable) {
14868             Roo.get(document).un('keydown', this.listKeyPress, this);
14869         }
14870         this.fireEvent('collapse', this);
14871         
14872         this.validate();
14873     },
14874
14875     // private
14876     collapseIf : function(e){
14877         var in_combo  = e.within(this.el);
14878         var in_list =  e.within(this.list);
14879         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14880         
14881         if (in_combo || in_list || is_list) {
14882             //e.stopPropagation();
14883             return;
14884         }
14885         
14886         if(this.tickable){
14887             this.onTickableFooterButtonClick(e, false, false);
14888         }
14889
14890         this.collapse();
14891         
14892     },
14893
14894     /**
14895      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14896      */
14897     expand : function(){
14898        
14899         if(this.isExpanded() || !this.hasFocus){
14900             return;
14901         }
14902         
14903         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14904         this.list.setWidth(lw);
14905         
14906         Roo.log('expand');
14907         
14908         this.list.show();
14909         
14910         this.restrictHeight();
14911         
14912         if(this.tickable){
14913             
14914             this.tickItems = Roo.apply([], this.item);
14915             
14916             this.okBtn.show();
14917             this.cancelBtn.show();
14918             this.trigger.hide();
14919             
14920             if(this.editable){
14921                 this.tickableInputEl().focus();
14922             }
14923             
14924         }
14925         
14926         Roo.get(document).on('mousedown', this.collapseIf, this);
14927         Roo.get(document).on('mousewheel', this.collapseIf, this);
14928         if (!this.editable) {
14929             Roo.get(document).on('keydown', this.listKeyPress, this);
14930         }
14931         
14932         this.fireEvent('expand', this);
14933     },
14934
14935     // private
14936     // Implements the default empty TriggerField.onTriggerClick function
14937     onTriggerClick : function(e)
14938     {
14939         Roo.log('trigger click');
14940         
14941         if(this.disabled || !this.triggerList){
14942             return;
14943         }
14944         
14945         this.page = 0;
14946         this.loadNext = false;
14947         
14948         if(this.isExpanded()){
14949             this.collapse();
14950             if (!this.blockFocus) {
14951                 this.inputEl().focus();
14952             }
14953             
14954         }else {
14955             this.hasFocus = true;
14956             if(this.triggerAction == 'all') {
14957                 this.doQuery(this.allQuery, true);
14958             } else {
14959                 this.doQuery(this.getRawValue());
14960             }
14961             if (!this.blockFocus) {
14962                 this.inputEl().focus();
14963             }
14964         }
14965     },
14966     
14967     onTickableTriggerClick : function(e)
14968     {
14969         if(this.disabled){
14970             return;
14971         }
14972         
14973         this.page = 0;
14974         this.loadNext = false;
14975         this.hasFocus = true;
14976         
14977         if(this.triggerAction == 'all') {
14978             this.doQuery(this.allQuery, true);
14979         } else {
14980             this.doQuery(this.getRawValue());
14981         }
14982     },
14983     
14984     onSearchFieldClick : function(e)
14985     {
14986         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14987             this.onTickableFooterButtonClick(e, false, false);
14988             return;
14989         }
14990         
14991         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14992             return;
14993         }
14994         
14995         this.page = 0;
14996         this.loadNext = false;
14997         this.hasFocus = true;
14998         
14999         if(this.triggerAction == 'all') {
15000             this.doQuery(this.allQuery, true);
15001         } else {
15002             this.doQuery(this.getRawValue());
15003         }
15004     },
15005     
15006     listKeyPress : function(e)
15007     {
15008         //Roo.log('listkeypress');
15009         // scroll to first matching element based on key pres..
15010         if (e.isSpecialKey()) {
15011             return false;
15012         }
15013         var k = String.fromCharCode(e.getKey()).toUpperCase();
15014         //Roo.log(k);
15015         var match  = false;
15016         var csel = this.view.getSelectedNodes();
15017         var cselitem = false;
15018         if (csel.length) {
15019             var ix = this.view.indexOf(csel[0]);
15020             cselitem  = this.store.getAt(ix);
15021             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15022                 cselitem = false;
15023             }
15024             
15025         }
15026         
15027         this.store.each(function(v) { 
15028             if (cselitem) {
15029                 // start at existing selection.
15030                 if (cselitem.id == v.id) {
15031                     cselitem = false;
15032                 }
15033                 return true;
15034             }
15035                 
15036             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15037                 match = this.store.indexOf(v);
15038                 return false;
15039             }
15040             return true;
15041         }, this);
15042         
15043         if (match === false) {
15044             return true; // no more action?
15045         }
15046         // scroll to?
15047         this.view.select(match);
15048         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15049         sn.scrollIntoView(sn.dom.parentNode, false);
15050     },
15051     
15052     onViewScroll : function(e, t){
15053         
15054         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){
15055             return;
15056         }
15057         
15058         this.hasQuery = true;
15059         
15060         this.loading = this.list.select('.loading', true).first();
15061         
15062         if(this.loading === null){
15063             this.list.createChild({
15064                 tag: 'div',
15065                 cls: 'loading roo-select2-more-results roo-select2-active',
15066                 html: 'Loading more results...'
15067             });
15068             
15069             this.loading = this.list.select('.loading', true).first();
15070             
15071             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15072             
15073             this.loading.hide();
15074         }
15075         
15076         this.loading.show();
15077         
15078         var _combo = this;
15079         
15080         this.page++;
15081         this.loadNext = true;
15082         
15083         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15084         
15085         return;
15086     },
15087     
15088     addItem : function(o)
15089     {   
15090         var dv = ''; // display value
15091         
15092         if (this.displayField) {
15093             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15094         } else {
15095             // this is an error condition!!!
15096             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15097         }
15098         
15099         if(!dv.length){
15100             return;
15101         }
15102         
15103         var choice = this.choices.createChild({
15104             tag: 'li',
15105             cls: 'roo-select2-search-choice',
15106             cn: [
15107                 {
15108                     tag: 'div',
15109                     html: dv
15110                 },
15111                 {
15112                     tag: 'a',
15113                     href: '#',
15114                     cls: 'roo-select2-search-choice-close fa fa-times',
15115                     tabindex: '-1'
15116                 }
15117             ]
15118             
15119         }, this.searchField);
15120         
15121         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15122         
15123         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15124         
15125         this.item.push(o);
15126         
15127         this.lastData = o;
15128         
15129         this.syncValue();
15130         
15131         this.inputEl().dom.value = '';
15132         
15133         this.validate();
15134     },
15135     
15136     onRemoveItem : function(e, _self, o)
15137     {
15138         e.preventDefault();
15139         
15140         this.lastItem = Roo.apply([], this.item);
15141         
15142         var index = this.item.indexOf(o.data) * 1;
15143         
15144         if( index < 0){
15145             Roo.log('not this item?!');
15146             return;
15147         }
15148         
15149         this.item.splice(index, 1);
15150         o.item.remove();
15151         
15152         this.syncValue();
15153         
15154         this.fireEvent('remove', this, e);
15155         
15156         this.validate();
15157         
15158     },
15159     
15160     syncValue : function()
15161     {
15162         if(!this.item.length){
15163             this.clearValue();
15164             return;
15165         }
15166             
15167         var value = [];
15168         var _this = this;
15169         Roo.each(this.item, function(i){
15170             if(_this.valueField){
15171                 value.push(i[_this.valueField]);
15172                 return;
15173             }
15174
15175             value.push(i);
15176         });
15177
15178         this.value = value.join(',');
15179
15180         if(this.hiddenField){
15181             this.hiddenField.dom.value = this.value;
15182         }
15183         
15184         this.store.fireEvent("datachanged", this.store);
15185         
15186         this.validate();
15187     },
15188     
15189     clearItem : function()
15190     {
15191         if(!this.multiple){
15192             return;
15193         }
15194         
15195         this.item = [];
15196         
15197         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15198            c.remove();
15199         });
15200         
15201         this.syncValue();
15202         
15203         this.validate();
15204         
15205         if(this.tickable && !Roo.isTouch){
15206             this.view.refresh();
15207         }
15208     },
15209     
15210     inputEl: function ()
15211     {
15212         if(Roo.isIOS && this.useNativeIOS){
15213             return this.el.select('select.roo-ios-select', true).first();
15214         }
15215         
15216         if(Roo.isTouch && this.mobileTouchView){
15217             return this.el.select('input.form-control',true).first();
15218         }
15219         
15220         if(this.tickable){
15221             return this.searchField;
15222         }
15223         
15224         return this.el.select('input.form-control',true).first();
15225     },
15226     
15227     onTickableFooterButtonClick : function(e, btn, el)
15228     {
15229         e.preventDefault();
15230         
15231         this.lastItem = Roo.apply([], this.item);
15232         
15233         if(btn && btn.name == 'cancel'){
15234             this.tickItems = Roo.apply([], this.item);
15235             this.collapse();
15236             return;
15237         }
15238         
15239         this.clearItem();
15240         
15241         var _this = this;
15242         
15243         Roo.each(this.tickItems, function(o){
15244             _this.addItem(o);
15245         });
15246         
15247         this.collapse();
15248         
15249     },
15250     
15251     validate : function()
15252     {
15253         if(this.getVisibilityEl().hasClass('hidden')){
15254             return true;
15255         }
15256         
15257         var v = this.getRawValue();
15258         
15259         if(this.multiple){
15260             v = this.getValue();
15261         }
15262         
15263         if(this.disabled || this.allowBlank || v.length){
15264             this.markValid();
15265             return true;
15266         }
15267         
15268         this.markInvalid();
15269         return false;
15270     },
15271     
15272     tickableInputEl : function()
15273     {
15274         if(!this.tickable || !this.editable){
15275             return this.inputEl();
15276         }
15277         
15278         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15279     },
15280     
15281     
15282     getAutoCreateTouchView : function()
15283     {
15284         var id = Roo.id();
15285         
15286         var cfg = {
15287             cls: 'form-group' //input-group
15288         };
15289         
15290         var input =  {
15291             tag: 'input',
15292             id : id,
15293             type : this.inputType,
15294             cls : 'form-control x-combo-noedit',
15295             autocomplete: 'new-password',
15296             placeholder : this.placeholder || '',
15297             readonly : true
15298         };
15299         
15300         if (this.name) {
15301             input.name = this.name;
15302         }
15303         
15304         if (this.size) {
15305             input.cls += ' input-' + this.size;
15306         }
15307         
15308         if (this.disabled) {
15309             input.disabled = true;
15310         }
15311         
15312         var inputblock = {
15313             cls : '',
15314             cn : [
15315                 input
15316             ]
15317         };
15318         
15319         if(this.before){
15320             inputblock.cls += ' input-group';
15321             
15322             inputblock.cn.unshift({
15323                 tag :'span',
15324                 cls : 'input-group-addon input-group-prepend input-group-text',
15325                 html : this.before
15326             });
15327         }
15328         
15329         if(this.removable && !this.multiple){
15330             inputblock.cls += ' roo-removable';
15331             
15332             inputblock.cn.push({
15333                 tag: 'button',
15334                 html : 'x',
15335                 cls : 'roo-combo-removable-btn close'
15336             });
15337         }
15338
15339         if(this.hasFeedback && !this.allowBlank){
15340             
15341             inputblock.cls += ' has-feedback';
15342             
15343             inputblock.cn.push({
15344                 tag: 'span',
15345                 cls: 'glyphicon form-control-feedback'
15346             });
15347             
15348         }
15349         
15350         if (this.after) {
15351             
15352             inputblock.cls += (this.before) ? '' : ' input-group';
15353             
15354             inputblock.cn.push({
15355                 tag :'span',
15356                 cls : 'input-group-addon input-group-append input-group-text',
15357                 html : this.after
15358             });
15359         }
15360
15361         
15362         var ibwrap = inputblock;
15363         
15364         if(this.multiple){
15365             ibwrap = {
15366                 tag: 'ul',
15367                 cls: 'roo-select2-choices',
15368                 cn:[
15369                     {
15370                         tag: 'li',
15371                         cls: 'roo-select2-search-field',
15372                         cn: [
15373
15374                             inputblock
15375                         ]
15376                     }
15377                 ]
15378             };
15379         
15380             
15381         }
15382         
15383         var combobox = {
15384             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15385             cn: [
15386                 {
15387                     tag: 'input',
15388                     type : 'hidden',
15389                     cls: 'form-hidden-field'
15390                 },
15391                 ibwrap
15392             ]
15393         };
15394         
15395         if(!this.multiple && this.showToggleBtn){
15396             
15397             var caret = {
15398                 cls: 'caret'
15399             };
15400             
15401             if (this.caret != false) {
15402                 caret = {
15403                      tag: 'i',
15404                      cls: 'fa fa-' + this.caret
15405                 };
15406                 
15407             }
15408             
15409             combobox.cn.push({
15410                 tag :'span',
15411                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15412                 cn : [
15413                     Roo.bootstrap.version == 3 ? caret : '',
15414                     {
15415                         tag: 'span',
15416                         cls: 'combobox-clear',
15417                         cn  : [
15418                             {
15419                                 tag : 'i',
15420                                 cls: 'icon-remove'
15421                             }
15422                         ]
15423                     }
15424                 ]
15425
15426             })
15427         }
15428         
15429         if(this.multiple){
15430             combobox.cls += ' roo-select2-container-multi';
15431         }
15432         
15433         var align = this.labelAlign || this.parentLabelAlign();
15434         
15435         if (align ==='left' && this.fieldLabel.length) {
15436
15437             cfg.cn = [
15438                 {
15439                    tag : 'i',
15440                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15441                    tooltip : 'This field is required'
15442                 },
15443                 {
15444                     tag: 'label',
15445                     cls : 'control-label col-form-label',
15446                     html : this.fieldLabel
15447
15448                 },
15449                 {
15450                     cls : '', 
15451                     cn: [
15452                         combobox
15453                     ]
15454                 }
15455             ];
15456             
15457             var labelCfg = cfg.cn[1];
15458             var contentCfg = cfg.cn[2];
15459             
15460
15461             if(this.indicatorpos == 'right'){
15462                 cfg.cn = [
15463                     {
15464                         tag: 'label',
15465                         'for' :  id,
15466                         cls : 'control-label col-form-label',
15467                         cn : [
15468                             {
15469                                 tag : 'span',
15470                                 html : this.fieldLabel
15471                             },
15472                             {
15473                                 tag : 'i',
15474                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15475                                 tooltip : 'This field is required'
15476                             }
15477                         ]
15478                     },
15479                     {
15480                         cls : "",
15481                         cn: [
15482                             combobox
15483                         ]
15484                     }
15485
15486                 ];
15487                 
15488                 labelCfg = cfg.cn[0];
15489                 contentCfg = cfg.cn[1];
15490             }
15491             
15492            
15493             
15494             if(this.labelWidth > 12){
15495                 labelCfg.style = "width: " + this.labelWidth + 'px';
15496             }
15497             
15498             if(this.labelWidth < 13 && this.labelmd == 0){
15499                 this.labelmd = this.labelWidth;
15500             }
15501             
15502             if(this.labellg > 0){
15503                 labelCfg.cls += ' col-lg-' + this.labellg;
15504                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15505             }
15506             
15507             if(this.labelmd > 0){
15508                 labelCfg.cls += ' col-md-' + this.labelmd;
15509                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15510             }
15511             
15512             if(this.labelsm > 0){
15513                 labelCfg.cls += ' col-sm-' + this.labelsm;
15514                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15515             }
15516             
15517             if(this.labelxs > 0){
15518                 labelCfg.cls += ' col-xs-' + this.labelxs;
15519                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15520             }
15521                 
15522                 
15523         } else if ( this.fieldLabel.length) {
15524             cfg.cn = [
15525                 {
15526                    tag : 'i',
15527                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15528                    tooltip : 'This field is required'
15529                 },
15530                 {
15531                     tag: 'label',
15532                     cls : 'control-label',
15533                     html : this.fieldLabel
15534
15535                 },
15536                 {
15537                     cls : '', 
15538                     cn: [
15539                         combobox
15540                     ]
15541                 }
15542             ];
15543             
15544             if(this.indicatorpos == 'right'){
15545                 cfg.cn = [
15546                     {
15547                         tag: 'label',
15548                         cls : 'control-label',
15549                         html : this.fieldLabel,
15550                         cn : [
15551                             {
15552                                tag : 'i',
15553                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15554                                tooltip : 'This field is required'
15555                             }
15556                         ]
15557                     },
15558                     {
15559                         cls : '', 
15560                         cn: [
15561                             combobox
15562                         ]
15563                     }
15564                 ];
15565             }
15566         } else {
15567             cfg.cn = combobox;    
15568         }
15569         
15570         
15571         var settings = this;
15572         
15573         ['xs','sm','md','lg'].map(function(size){
15574             if (settings[size]) {
15575                 cfg.cls += ' col-' + size + '-' + settings[size];
15576             }
15577         });
15578         
15579         return cfg;
15580     },
15581     
15582     initTouchView : function()
15583     {
15584         this.renderTouchView();
15585         
15586         this.touchViewEl.on('scroll', function(){
15587             this.el.dom.scrollTop = 0;
15588         }, this);
15589         
15590         this.originalValue = this.getValue();
15591         
15592         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15593         
15594         this.inputEl().on("click", this.showTouchView, this);
15595         if (this.triggerEl) {
15596             this.triggerEl.on("click", this.showTouchView, this);
15597         }
15598         
15599         
15600         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15601         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15602         
15603         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15604         
15605         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15606         this.store.on('load', this.onTouchViewLoad, this);
15607         this.store.on('loadexception', this.onTouchViewLoadException, this);
15608         
15609         if(this.hiddenName){
15610             
15611             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15612             
15613             this.hiddenField.dom.value =
15614                 this.hiddenValue !== undefined ? this.hiddenValue :
15615                 this.value !== undefined ? this.value : '';
15616         
15617             this.el.dom.removeAttribute('name');
15618             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15619         }
15620         
15621         if(this.multiple){
15622             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15623             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15624         }
15625         
15626         if(this.removable && !this.multiple){
15627             var close = this.closeTriggerEl();
15628             if(close){
15629                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15630                 close.on('click', this.removeBtnClick, this, close);
15631             }
15632         }
15633         /*
15634          * fix the bug in Safari iOS8
15635          */
15636         this.inputEl().on("focus", function(e){
15637             document.activeElement.blur();
15638         }, this);
15639         
15640         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15641         
15642         return;
15643         
15644         
15645     },
15646     
15647     renderTouchView : function()
15648     {
15649         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15650         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15651         
15652         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15653         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15654         
15655         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15656         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15657         this.touchViewBodyEl.setStyle('overflow', 'auto');
15658         
15659         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15660         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15661         
15662         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15663         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15664         
15665     },
15666     
15667     showTouchView : function()
15668     {
15669         if(this.disabled){
15670             return;
15671         }
15672         
15673         this.touchViewHeaderEl.hide();
15674
15675         if(this.modalTitle.length){
15676             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15677             this.touchViewHeaderEl.show();
15678         }
15679
15680         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15681         this.touchViewEl.show();
15682
15683         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15684         
15685         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15686         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15687
15688         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15689
15690         if(this.modalTitle.length){
15691             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15692         }
15693         
15694         this.touchViewBodyEl.setHeight(bodyHeight);
15695
15696         if(this.animate){
15697             var _this = this;
15698             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15699         }else{
15700             this.touchViewEl.addClass('in');
15701         }
15702         
15703         if(this._touchViewMask){
15704             Roo.get(document.body).addClass("x-body-masked");
15705             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15706             this._touchViewMask.setStyle('z-index', 10000);
15707             this._touchViewMask.addClass('show');
15708         }
15709         
15710         this.doTouchViewQuery();
15711         
15712     },
15713     
15714     hideTouchView : function()
15715     {
15716         this.touchViewEl.removeClass('in');
15717
15718         if(this.animate){
15719             var _this = this;
15720             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15721         }else{
15722             this.touchViewEl.setStyle('display', 'none');
15723         }
15724         
15725         if(this._touchViewMask){
15726             this._touchViewMask.removeClass('show');
15727             Roo.get(document.body).removeClass("x-body-masked");
15728         }
15729     },
15730     
15731     setTouchViewValue : function()
15732     {
15733         if(this.multiple){
15734             this.clearItem();
15735         
15736             var _this = this;
15737
15738             Roo.each(this.tickItems, function(o){
15739                 this.addItem(o);
15740             }, this);
15741         }
15742         
15743         this.hideTouchView();
15744     },
15745     
15746     doTouchViewQuery : function()
15747     {
15748         var qe = {
15749             query: '',
15750             forceAll: true,
15751             combo: this,
15752             cancel:false
15753         };
15754         
15755         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15756             return false;
15757         }
15758         
15759         if(!this.alwaysQuery || this.mode == 'local'){
15760             this.onTouchViewLoad();
15761             return;
15762         }
15763         
15764         this.store.load();
15765     },
15766     
15767     onTouchViewBeforeLoad : function(combo,opts)
15768     {
15769         return;
15770     },
15771
15772     // private
15773     onTouchViewLoad : function()
15774     {
15775         if(this.store.getCount() < 1){
15776             this.onTouchViewEmptyResults();
15777             return;
15778         }
15779         
15780         this.clearTouchView();
15781         
15782         var rawValue = this.getRawValue();
15783         
15784         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15785         
15786         this.tickItems = [];
15787         
15788         this.store.data.each(function(d, rowIndex){
15789             var row = this.touchViewListGroup.createChild(template);
15790             
15791             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15792                 row.addClass(d.data.cls);
15793             }
15794             
15795             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15796                 var cfg = {
15797                     data : d.data,
15798                     html : d.data[this.displayField]
15799                 };
15800                 
15801                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15802                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15803                 }
15804             }
15805             row.removeClass('selected');
15806             if(!this.multiple && this.valueField &&
15807                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15808             {
15809                 // radio buttons..
15810                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15811                 row.addClass('selected');
15812             }
15813             
15814             if(this.multiple && this.valueField &&
15815                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15816             {
15817                 
15818                 // checkboxes...
15819                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15820                 this.tickItems.push(d.data);
15821             }
15822             
15823             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15824             
15825         }, this);
15826         
15827         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15828         
15829         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15830
15831         if(this.modalTitle.length){
15832             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15833         }
15834
15835         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15836         
15837         if(this.mobile_restrict_height && listHeight < bodyHeight){
15838             this.touchViewBodyEl.setHeight(listHeight);
15839         }
15840         
15841         var _this = this;
15842         
15843         if(firstChecked && listHeight > bodyHeight){
15844             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15845         }
15846         
15847     },
15848     
15849     onTouchViewLoadException : function()
15850     {
15851         this.hideTouchView();
15852     },
15853     
15854     onTouchViewEmptyResults : function()
15855     {
15856         this.clearTouchView();
15857         
15858         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15859         
15860         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15861         
15862     },
15863     
15864     clearTouchView : function()
15865     {
15866         this.touchViewListGroup.dom.innerHTML = '';
15867     },
15868     
15869     onTouchViewClick : function(e, el, o)
15870     {
15871         e.preventDefault();
15872         
15873         var row = o.row;
15874         var rowIndex = o.rowIndex;
15875         
15876         var r = this.store.getAt(rowIndex);
15877         
15878         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15879             
15880             if(!this.multiple){
15881                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15882                     c.dom.removeAttribute('checked');
15883                 }, this);
15884
15885                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15886
15887                 this.setFromData(r.data);
15888
15889                 var close = this.closeTriggerEl();
15890
15891                 if(close){
15892                     close.show();
15893                 }
15894
15895                 this.hideTouchView();
15896
15897                 this.fireEvent('select', this, r, rowIndex);
15898
15899                 return;
15900             }
15901
15902             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15903                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15904                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15905                 return;
15906             }
15907
15908             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15909             this.addItem(r.data);
15910             this.tickItems.push(r.data);
15911         }
15912     },
15913     
15914     getAutoCreateNativeIOS : function()
15915     {
15916         var cfg = {
15917             cls: 'form-group' //input-group,
15918         };
15919         
15920         var combobox =  {
15921             tag: 'select',
15922             cls : 'roo-ios-select'
15923         };
15924         
15925         if (this.name) {
15926             combobox.name = this.name;
15927         }
15928         
15929         if (this.disabled) {
15930             combobox.disabled = true;
15931         }
15932         
15933         var settings = this;
15934         
15935         ['xs','sm','md','lg'].map(function(size){
15936             if (settings[size]) {
15937                 cfg.cls += ' col-' + size + '-' + settings[size];
15938             }
15939         });
15940         
15941         cfg.cn = combobox;
15942         
15943         return cfg;
15944         
15945     },
15946     
15947     initIOSView : function()
15948     {
15949         this.store.on('load', this.onIOSViewLoad, this);
15950         
15951         return;
15952     },
15953     
15954     onIOSViewLoad : function()
15955     {
15956         if(this.store.getCount() < 1){
15957             return;
15958         }
15959         
15960         this.clearIOSView();
15961         
15962         if(this.allowBlank) {
15963             
15964             var default_text = '-- SELECT --';
15965             
15966             if(this.placeholder.length){
15967                 default_text = this.placeholder;
15968             }
15969             
15970             if(this.emptyTitle.length){
15971                 default_text += ' - ' + this.emptyTitle + ' -';
15972             }
15973             
15974             var opt = this.inputEl().createChild({
15975                 tag: 'option',
15976                 value : 0,
15977                 html : default_text
15978             });
15979             
15980             var o = {};
15981             o[this.valueField] = 0;
15982             o[this.displayField] = default_text;
15983             
15984             this.ios_options.push({
15985                 data : o,
15986                 el : opt
15987             });
15988             
15989         }
15990         
15991         this.store.data.each(function(d, rowIndex){
15992             
15993             var html = '';
15994             
15995             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15996                 html = d.data[this.displayField];
15997             }
15998             
15999             var value = '';
16000             
16001             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16002                 value = d.data[this.valueField];
16003             }
16004             
16005             var option = {
16006                 tag: 'option',
16007                 value : value,
16008                 html : html
16009             };
16010             
16011             if(this.value == d.data[this.valueField]){
16012                 option['selected'] = true;
16013             }
16014             
16015             var opt = this.inputEl().createChild(option);
16016             
16017             this.ios_options.push({
16018                 data : d.data,
16019                 el : opt
16020             });
16021             
16022         }, this);
16023         
16024         this.inputEl().on('change', function(){
16025            this.fireEvent('select', this);
16026         }, this);
16027         
16028     },
16029     
16030     clearIOSView: function()
16031     {
16032         this.inputEl().dom.innerHTML = '';
16033         
16034         this.ios_options = [];
16035     },
16036     
16037     setIOSValue: function(v)
16038     {
16039         this.value = v;
16040         
16041         if(!this.ios_options){
16042             return;
16043         }
16044         
16045         Roo.each(this.ios_options, function(opts){
16046            
16047            opts.el.dom.removeAttribute('selected');
16048            
16049            if(opts.data[this.valueField] != v){
16050                return;
16051            }
16052            
16053            opts.el.dom.setAttribute('selected', true);
16054            
16055         }, this);
16056     }
16057
16058     /** 
16059     * @cfg {Boolean} grow 
16060     * @hide 
16061     */
16062     /** 
16063     * @cfg {Number} growMin 
16064     * @hide 
16065     */
16066     /** 
16067     * @cfg {Number} growMax 
16068     * @hide 
16069     */
16070     /**
16071      * @hide
16072      * @method autoSize
16073      */
16074 });
16075
16076 Roo.apply(Roo.bootstrap.ComboBox,  {
16077     
16078     header : {
16079         tag: 'div',
16080         cls: 'modal-header',
16081         cn: [
16082             {
16083                 tag: 'h4',
16084                 cls: 'modal-title'
16085             }
16086         ]
16087     },
16088     
16089     body : {
16090         tag: 'div',
16091         cls: 'modal-body',
16092         cn: [
16093             {
16094                 tag: 'ul',
16095                 cls: 'list-group'
16096             }
16097         ]
16098     },
16099     
16100     listItemRadio : {
16101         tag: 'li',
16102         cls: 'list-group-item',
16103         cn: [
16104             {
16105                 tag: 'span',
16106                 cls: 'roo-combobox-list-group-item-value'
16107             },
16108             {
16109                 tag: 'div',
16110                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16111                 cn: [
16112                     {
16113                         tag: 'input',
16114                         type: 'radio'
16115                     },
16116                     {
16117                         tag: 'label'
16118                     }
16119                 ]
16120             }
16121         ]
16122     },
16123     
16124     listItemCheckbox : {
16125         tag: 'li',
16126         cls: 'list-group-item',
16127         cn: [
16128             {
16129                 tag: 'span',
16130                 cls: 'roo-combobox-list-group-item-value'
16131             },
16132             {
16133                 tag: 'div',
16134                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16135                 cn: [
16136                     {
16137                         tag: 'input',
16138                         type: 'checkbox'
16139                     },
16140                     {
16141                         tag: 'label'
16142                     }
16143                 ]
16144             }
16145         ]
16146     },
16147     
16148     emptyResult : {
16149         tag: 'div',
16150         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16151     },
16152     
16153     footer : {
16154         tag: 'div',
16155         cls: 'modal-footer',
16156         cn: [
16157             {
16158                 tag: 'div',
16159                 cls: 'row',
16160                 cn: [
16161                     {
16162                         tag: 'div',
16163                         cls: 'col-xs-6 text-left',
16164                         cn: {
16165                             tag: 'button',
16166                             cls: 'btn btn-danger roo-touch-view-cancel',
16167                             html: 'Cancel'
16168                         }
16169                     },
16170                     {
16171                         tag: 'div',
16172                         cls: 'col-xs-6 text-right',
16173                         cn: {
16174                             tag: 'button',
16175                             cls: 'btn btn-success roo-touch-view-ok',
16176                             html: 'OK'
16177                         }
16178                     }
16179                 ]
16180             }
16181         ]
16182         
16183     }
16184 });
16185
16186 Roo.apply(Roo.bootstrap.ComboBox,  {
16187     
16188     touchViewTemplate : {
16189         tag: 'div',
16190         cls: 'modal fade roo-combobox-touch-view',
16191         cn: [
16192             {
16193                 tag: 'div',
16194                 cls: 'modal-dialog',
16195                 style : 'position:fixed', // we have to fix position....
16196                 cn: [
16197                     {
16198                         tag: 'div',
16199                         cls: 'modal-content',
16200                         cn: [
16201                             Roo.bootstrap.ComboBox.header,
16202                             Roo.bootstrap.ComboBox.body,
16203                             Roo.bootstrap.ComboBox.footer
16204                         ]
16205                     }
16206                 ]
16207             }
16208         ]
16209     }
16210 });/*
16211  * Based on:
16212  * Ext JS Library 1.1.1
16213  * Copyright(c) 2006-2007, Ext JS, LLC.
16214  *
16215  * Originally Released Under LGPL - original licence link has changed is not relivant.
16216  *
16217  * Fork - LGPL
16218  * <script type="text/javascript">
16219  */
16220
16221 /**
16222  * @class Roo.View
16223  * @extends Roo.util.Observable
16224  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16225  * This class also supports single and multi selection modes. <br>
16226  * Create a data model bound view:
16227  <pre><code>
16228  var store = new Roo.data.Store(...);
16229
16230  var view = new Roo.View({
16231     el : "my-element",
16232     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16233  
16234     singleSelect: true,
16235     selectedClass: "ydataview-selected",
16236     store: store
16237  });
16238
16239  // listen for node click?
16240  view.on("click", function(vw, index, node, e){
16241  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16242  });
16243
16244  // load XML data
16245  dataModel.load("foobar.xml");
16246  </code></pre>
16247  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16248  * <br><br>
16249  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16250  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16251  * 
16252  * Note: old style constructor is still suported (container, template, config)
16253  * 
16254  * @constructor
16255  * Create a new View
16256  * @param {Object} config The config object
16257  * 
16258  */
16259 Roo.View = function(config, depreciated_tpl, depreciated_config){
16260     
16261     this.parent = false;
16262     
16263     if (typeof(depreciated_tpl) == 'undefined') {
16264         // new way.. - universal constructor.
16265         Roo.apply(this, config);
16266         this.el  = Roo.get(this.el);
16267     } else {
16268         // old format..
16269         this.el  = Roo.get(config);
16270         this.tpl = depreciated_tpl;
16271         Roo.apply(this, depreciated_config);
16272     }
16273     this.wrapEl  = this.el.wrap().wrap();
16274     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16275     
16276     
16277     if(typeof(this.tpl) == "string"){
16278         this.tpl = new Roo.Template(this.tpl);
16279     } else {
16280         // support xtype ctors..
16281         this.tpl = new Roo.factory(this.tpl, Roo);
16282     }
16283     
16284     
16285     this.tpl.compile();
16286     
16287     /** @private */
16288     this.addEvents({
16289         /**
16290          * @event beforeclick
16291          * Fires before a click is processed. Returns false to cancel the default action.
16292          * @param {Roo.View} this
16293          * @param {Number} index The index of the target node
16294          * @param {HTMLElement} node The target node
16295          * @param {Roo.EventObject} e The raw event object
16296          */
16297             "beforeclick" : true,
16298         /**
16299          * @event click
16300          * Fires when a template node is clicked.
16301          * @param {Roo.View} this
16302          * @param {Number} index The index of the target node
16303          * @param {HTMLElement} node The target node
16304          * @param {Roo.EventObject} e The raw event object
16305          */
16306             "click" : true,
16307         /**
16308          * @event dblclick
16309          * Fires when a template node is double clicked.
16310          * @param {Roo.View} this
16311          * @param {Number} index The index of the target node
16312          * @param {HTMLElement} node The target node
16313          * @param {Roo.EventObject} e The raw event object
16314          */
16315             "dblclick" : true,
16316         /**
16317          * @event contextmenu
16318          * Fires when a template node is right clicked.
16319          * @param {Roo.View} this
16320          * @param {Number} index The index of the target node
16321          * @param {HTMLElement} node The target node
16322          * @param {Roo.EventObject} e The raw event object
16323          */
16324             "contextmenu" : true,
16325         /**
16326          * @event selectionchange
16327          * Fires when the selected nodes change.
16328          * @param {Roo.View} this
16329          * @param {Array} selections Array of the selected nodes
16330          */
16331             "selectionchange" : true,
16332     
16333         /**
16334          * @event beforeselect
16335          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16336          * @param {Roo.View} this
16337          * @param {HTMLElement} node The node to be selected
16338          * @param {Array} selections Array of currently selected nodes
16339          */
16340             "beforeselect" : true,
16341         /**
16342          * @event preparedata
16343          * Fires on every row to render, to allow you to change the data.
16344          * @param {Roo.View} this
16345          * @param {Object} data to be rendered (change this)
16346          */
16347           "preparedata" : true
16348           
16349           
16350         });
16351
16352
16353
16354     this.el.on({
16355         "click": this.onClick,
16356         "dblclick": this.onDblClick,
16357         "contextmenu": this.onContextMenu,
16358         scope:this
16359     });
16360
16361     this.selections = [];
16362     this.nodes = [];
16363     this.cmp = new Roo.CompositeElementLite([]);
16364     if(this.store){
16365         this.store = Roo.factory(this.store, Roo.data);
16366         this.setStore(this.store, true);
16367     }
16368     
16369     if ( this.footer && this.footer.xtype) {
16370            
16371          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16372         
16373         this.footer.dataSource = this.store;
16374         this.footer.container = fctr;
16375         this.footer = Roo.factory(this.footer, Roo);
16376         fctr.insertFirst(this.el);
16377         
16378         // this is a bit insane - as the paging toolbar seems to detach the el..
16379 //        dom.parentNode.parentNode.parentNode
16380          // they get detached?
16381     }
16382     
16383     
16384     Roo.View.superclass.constructor.call(this);
16385     
16386     
16387 };
16388
16389 Roo.extend(Roo.View, Roo.util.Observable, {
16390     
16391      /**
16392      * @cfg {Roo.data.Store} store Data store to load data from.
16393      */
16394     store : false,
16395     
16396     /**
16397      * @cfg {String|Roo.Element} el The container element.
16398      */
16399     el : '',
16400     
16401     /**
16402      * @cfg {String|Roo.Template} tpl The template used by this View 
16403      */
16404     tpl : false,
16405     /**
16406      * @cfg {String} dataName the named area of the template to use as the data area
16407      *                          Works with domtemplates roo-name="name"
16408      */
16409     dataName: false,
16410     /**
16411      * @cfg {String} selectedClass The css class to add to selected nodes
16412      */
16413     selectedClass : "x-view-selected",
16414      /**
16415      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16416      */
16417     emptyText : "",
16418     
16419     /**
16420      * @cfg {String} text to display on mask (default Loading)
16421      */
16422     mask : false,
16423     /**
16424      * @cfg {Boolean} multiSelect Allow multiple selection
16425      */
16426     multiSelect : false,
16427     /**
16428      * @cfg {Boolean} singleSelect Allow single selection
16429      */
16430     singleSelect:  false,
16431     
16432     /**
16433      * @cfg {Boolean} toggleSelect - selecting 
16434      */
16435     toggleSelect : false,
16436     
16437     /**
16438      * @cfg {Boolean} tickable - selecting 
16439      */
16440     tickable : false,
16441     
16442     /**
16443      * Returns the element this view is bound to.
16444      * @return {Roo.Element}
16445      */
16446     getEl : function(){
16447         return this.wrapEl;
16448     },
16449     
16450     
16451
16452     /**
16453      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16454      */
16455     refresh : function(){
16456         //Roo.log('refresh');
16457         var t = this.tpl;
16458         
16459         // if we are using something like 'domtemplate', then
16460         // the what gets used is:
16461         // t.applySubtemplate(NAME, data, wrapping data..)
16462         // the outer template then get' applied with
16463         //     the store 'extra data'
16464         // and the body get's added to the
16465         //      roo-name="data" node?
16466         //      <span class='roo-tpl-{name}'></span> ?????
16467         
16468         
16469         
16470         this.clearSelections();
16471         this.el.update("");
16472         var html = [];
16473         var records = this.store.getRange();
16474         if(records.length < 1) {
16475             
16476             // is this valid??  = should it render a template??
16477             
16478             this.el.update(this.emptyText);
16479             return;
16480         }
16481         var el = this.el;
16482         if (this.dataName) {
16483             this.el.update(t.apply(this.store.meta)); //????
16484             el = this.el.child('.roo-tpl-' + this.dataName);
16485         }
16486         
16487         for(var i = 0, len = records.length; i < len; i++){
16488             var data = this.prepareData(records[i].data, i, records[i]);
16489             this.fireEvent("preparedata", this, data, i, records[i]);
16490             
16491             var d = Roo.apply({}, data);
16492             
16493             if(this.tickable){
16494                 Roo.apply(d, {'roo-id' : Roo.id()});
16495                 
16496                 var _this = this;
16497             
16498                 Roo.each(this.parent.item, function(item){
16499                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16500                         return;
16501                     }
16502                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16503                 });
16504             }
16505             
16506             html[html.length] = Roo.util.Format.trim(
16507                 this.dataName ?
16508                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16509                     t.apply(d)
16510             );
16511         }
16512         
16513         
16514         
16515         el.update(html.join(""));
16516         this.nodes = el.dom.childNodes;
16517         this.updateIndexes(0);
16518     },
16519     
16520
16521     /**
16522      * Function to override to reformat the data that is sent to
16523      * the template for each node.
16524      * DEPRICATED - use the preparedata event handler.
16525      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16526      * a JSON object for an UpdateManager bound view).
16527      */
16528     prepareData : function(data, index, record)
16529     {
16530         this.fireEvent("preparedata", this, data, index, record);
16531         return data;
16532     },
16533
16534     onUpdate : function(ds, record){
16535         // Roo.log('on update');   
16536         this.clearSelections();
16537         var index = this.store.indexOf(record);
16538         var n = this.nodes[index];
16539         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16540         n.parentNode.removeChild(n);
16541         this.updateIndexes(index, index);
16542     },
16543
16544     
16545     
16546 // --------- FIXME     
16547     onAdd : function(ds, records, index)
16548     {
16549         //Roo.log(['on Add', ds, records, index] );        
16550         this.clearSelections();
16551         if(this.nodes.length == 0){
16552             this.refresh();
16553             return;
16554         }
16555         var n = this.nodes[index];
16556         for(var i = 0, len = records.length; i < len; i++){
16557             var d = this.prepareData(records[i].data, i, records[i]);
16558             if(n){
16559                 this.tpl.insertBefore(n, d);
16560             }else{
16561                 
16562                 this.tpl.append(this.el, d);
16563             }
16564         }
16565         this.updateIndexes(index);
16566     },
16567
16568     onRemove : function(ds, record, index){
16569        // Roo.log('onRemove');
16570         this.clearSelections();
16571         var el = this.dataName  ?
16572             this.el.child('.roo-tpl-' + this.dataName) :
16573             this.el; 
16574         
16575         el.dom.removeChild(this.nodes[index]);
16576         this.updateIndexes(index);
16577     },
16578
16579     /**
16580      * Refresh an individual node.
16581      * @param {Number} index
16582      */
16583     refreshNode : function(index){
16584         this.onUpdate(this.store, this.store.getAt(index));
16585     },
16586
16587     updateIndexes : function(startIndex, endIndex){
16588         var ns = this.nodes;
16589         startIndex = startIndex || 0;
16590         endIndex = endIndex || ns.length - 1;
16591         for(var i = startIndex; i <= endIndex; i++){
16592             ns[i].nodeIndex = i;
16593         }
16594     },
16595
16596     /**
16597      * Changes the data store this view uses and refresh the view.
16598      * @param {Store} store
16599      */
16600     setStore : function(store, initial){
16601         if(!initial && this.store){
16602             this.store.un("datachanged", this.refresh);
16603             this.store.un("add", this.onAdd);
16604             this.store.un("remove", this.onRemove);
16605             this.store.un("update", this.onUpdate);
16606             this.store.un("clear", this.refresh);
16607             this.store.un("beforeload", this.onBeforeLoad);
16608             this.store.un("load", this.onLoad);
16609             this.store.un("loadexception", this.onLoad);
16610         }
16611         if(store){
16612           
16613             store.on("datachanged", this.refresh, this);
16614             store.on("add", this.onAdd, this);
16615             store.on("remove", this.onRemove, this);
16616             store.on("update", this.onUpdate, this);
16617             store.on("clear", this.refresh, this);
16618             store.on("beforeload", this.onBeforeLoad, this);
16619             store.on("load", this.onLoad, this);
16620             store.on("loadexception", this.onLoad, this);
16621         }
16622         
16623         if(store){
16624             this.refresh();
16625         }
16626     },
16627     /**
16628      * onbeforeLoad - masks the loading area.
16629      *
16630      */
16631     onBeforeLoad : function(store,opts)
16632     {
16633          //Roo.log('onBeforeLoad');   
16634         if (!opts.add) {
16635             this.el.update("");
16636         }
16637         this.el.mask(this.mask ? this.mask : "Loading" ); 
16638     },
16639     onLoad : function ()
16640     {
16641         this.el.unmask();
16642     },
16643     
16644
16645     /**
16646      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16647      * @param {HTMLElement} node
16648      * @return {HTMLElement} The template node
16649      */
16650     findItemFromChild : function(node){
16651         var el = this.dataName  ?
16652             this.el.child('.roo-tpl-' + this.dataName,true) :
16653             this.el.dom; 
16654         
16655         if(!node || node.parentNode == el){
16656                     return node;
16657             }
16658             var p = node.parentNode;
16659             while(p && p != el){
16660             if(p.parentNode == el){
16661                 return p;
16662             }
16663             p = p.parentNode;
16664         }
16665             return null;
16666     },
16667
16668     /** @ignore */
16669     onClick : function(e){
16670         var item = this.findItemFromChild(e.getTarget());
16671         if(item){
16672             var index = this.indexOf(item);
16673             if(this.onItemClick(item, index, e) !== false){
16674                 this.fireEvent("click", this, index, item, e);
16675             }
16676         }else{
16677             this.clearSelections();
16678         }
16679     },
16680
16681     /** @ignore */
16682     onContextMenu : function(e){
16683         var item = this.findItemFromChild(e.getTarget());
16684         if(item){
16685             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16686         }
16687     },
16688
16689     /** @ignore */
16690     onDblClick : function(e){
16691         var item = this.findItemFromChild(e.getTarget());
16692         if(item){
16693             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16694         }
16695     },
16696
16697     onItemClick : function(item, index, e)
16698     {
16699         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16700             return false;
16701         }
16702         if (this.toggleSelect) {
16703             var m = this.isSelected(item) ? 'unselect' : 'select';
16704             //Roo.log(m);
16705             var _t = this;
16706             _t[m](item, true, false);
16707             return true;
16708         }
16709         if(this.multiSelect || this.singleSelect){
16710             if(this.multiSelect && e.shiftKey && this.lastSelection){
16711                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16712             }else{
16713                 this.select(item, this.multiSelect && e.ctrlKey);
16714                 this.lastSelection = item;
16715             }
16716             
16717             if(!this.tickable){
16718                 e.preventDefault();
16719             }
16720             
16721         }
16722         return true;
16723     },
16724
16725     /**
16726      * Get the number of selected nodes.
16727      * @return {Number}
16728      */
16729     getSelectionCount : function(){
16730         return this.selections.length;
16731     },
16732
16733     /**
16734      * Get the currently selected nodes.
16735      * @return {Array} An array of HTMLElements
16736      */
16737     getSelectedNodes : function(){
16738         return this.selections;
16739     },
16740
16741     /**
16742      * Get the indexes of the selected nodes.
16743      * @return {Array}
16744      */
16745     getSelectedIndexes : function(){
16746         var indexes = [], s = this.selections;
16747         for(var i = 0, len = s.length; i < len; i++){
16748             indexes.push(s[i].nodeIndex);
16749         }
16750         return indexes;
16751     },
16752
16753     /**
16754      * Clear all selections
16755      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16756      */
16757     clearSelections : function(suppressEvent){
16758         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16759             this.cmp.elements = this.selections;
16760             this.cmp.removeClass(this.selectedClass);
16761             this.selections = [];
16762             if(!suppressEvent){
16763                 this.fireEvent("selectionchange", this, this.selections);
16764             }
16765         }
16766     },
16767
16768     /**
16769      * Returns true if the passed node is selected
16770      * @param {HTMLElement/Number} node The node or node index
16771      * @return {Boolean}
16772      */
16773     isSelected : function(node){
16774         var s = this.selections;
16775         if(s.length < 1){
16776             return false;
16777         }
16778         node = this.getNode(node);
16779         return s.indexOf(node) !== -1;
16780     },
16781
16782     /**
16783      * Selects nodes.
16784      * @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
16785      * @param {Boolean} keepExisting (optional) true to keep existing selections
16786      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16787      */
16788     select : function(nodeInfo, keepExisting, suppressEvent){
16789         if(nodeInfo instanceof Array){
16790             if(!keepExisting){
16791                 this.clearSelections(true);
16792             }
16793             for(var i = 0, len = nodeInfo.length; i < len; i++){
16794                 this.select(nodeInfo[i], true, true);
16795             }
16796             return;
16797         } 
16798         var node = this.getNode(nodeInfo);
16799         if(!node || this.isSelected(node)){
16800             return; // already selected.
16801         }
16802         if(!keepExisting){
16803             this.clearSelections(true);
16804         }
16805         
16806         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16807             Roo.fly(node).addClass(this.selectedClass);
16808             this.selections.push(node);
16809             if(!suppressEvent){
16810                 this.fireEvent("selectionchange", this, this.selections);
16811             }
16812         }
16813         
16814         
16815     },
16816       /**
16817      * Unselects nodes.
16818      * @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
16819      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16820      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16821      */
16822     unselect : function(nodeInfo, keepExisting, suppressEvent)
16823     {
16824         if(nodeInfo instanceof Array){
16825             Roo.each(this.selections, function(s) {
16826                 this.unselect(s, nodeInfo);
16827             }, this);
16828             return;
16829         }
16830         var node = this.getNode(nodeInfo);
16831         if(!node || !this.isSelected(node)){
16832             //Roo.log("not selected");
16833             return; // not selected.
16834         }
16835         // fireevent???
16836         var ns = [];
16837         Roo.each(this.selections, function(s) {
16838             if (s == node ) {
16839                 Roo.fly(node).removeClass(this.selectedClass);
16840
16841                 return;
16842             }
16843             ns.push(s);
16844         },this);
16845         
16846         this.selections= ns;
16847         this.fireEvent("selectionchange", this, this.selections);
16848     },
16849
16850     /**
16851      * Gets a template node.
16852      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16853      * @return {HTMLElement} The node or null if it wasn't found
16854      */
16855     getNode : function(nodeInfo){
16856         if(typeof nodeInfo == "string"){
16857             return document.getElementById(nodeInfo);
16858         }else if(typeof nodeInfo == "number"){
16859             return this.nodes[nodeInfo];
16860         }
16861         return nodeInfo;
16862     },
16863
16864     /**
16865      * Gets a range template nodes.
16866      * @param {Number} startIndex
16867      * @param {Number} endIndex
16868      * @return {Array} An array of nodes
16869      */
16870     getNodes : function(start, end){
16871         var ns = this.nodes;
16872         start = start || 0;
16873         end = typeof end == "undefined" ? ns.length - 1 : end;
16874         var nodes = [];
16875         if(start <= end){
16876             for(var i = start; i <= end; i++){
16877                 nodes.push(ns[i]);
16878             }
16879         } else{
16880             for(var i = start; i >= end; i--){
16881                 nodes.push(ns[i]);
16882             }
16883         }
16884         return nodes;
16885     },
16886
16887     /**
16888      * Finds the index of the passed node
16889      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16890      * @return {Number} The index of the node or -1
16891      */
16892     indexOf : function(node){
16893         node = this.getNode(node);
16894         if(typeof node.nodeIndex == "number"){
16895             return node.nodeIndex;
16896         }
16897         var ns = this.nodes;
16898         for(var i = 0, len = ns.length; i < len; i++){
16899             if(ns[i] == node){
16900                 return i;
16901             }
16902         }
16903         return -1;
16904     }
16905 });
16906 /*
16907  * - LGPL
16908  *
16909  * based on jquery fullcalendar
16910  * 
16911  */
16912
16913 Roo.bootstrap = Roo.bootstrap || {};
16914 /**
16915  * @class Roo.bootstrap.Calendar
16916  * @extends Roo.bootstrap.Component
16917  * Bootstrap Calendar class
16918  * @cfg {Boolean} loadMask (true|false) default false
16919  * @cfg {Object} header generate the user specific header of the calendar, default false
16920
16921  * @constructor
16922  * Create a new Container
16923  * @param {Object} config The config object
16924  */
16925
16926
16927
16928 Roo.bootstrap.Calendar = function(config){
16929     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16930      this.addEvents({
16931         /**
16932              * @event select
16933              * Fires when a date is selected
16934              * @param {DatePicker} this
16935              * @param {Date} date The selected date
16936              */
16937         'select': true,
16938         /**
16939              * @event monthchange
16940              * Fires when the displayed month changes 
16941              * @param {DatePicker} this
16942              * @param {Date} date The selected month
16943              */
16944         'monthchange': true,
16945         /**
16946              * @event evententer
16947              * Fires when mouse over an event
16948              * @param {Calendar} this
16949              * @param {event} Event
16950              */
16951         'evententer': true,
16952         /**
16953              * @event eventleave
16954              * Fires when the mouse leaves an
16955              * @param {Calendar} this
16956              * @param {event}
16957              */
16958         'eventleave': true,
16959         /**
16960              * @event eventclick
16961              * Fires when the mouse click an
16962              * @param {Calendar} this
16963              * @param {event}
16964              */
16965         'eventclick': true
16966         
16967     });
16968
16969 };
16970
16971 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16972     
16973      /**
16974      * @cfg {Number} startDay
16975      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16976      */
16977     startDay : 0,
16978     
16979     loadMask : false,
16980     
16981     header : false,
16982       
16983     getAutoCreate : function(){
16984         
16985         
16986         var fc_button = function(name, corner, style, content ) {
16987             return Roo.apply({},{
16988                 tag : 'span',
16989                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16990                          (corner.length ?
16991                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16992                             ''
16993                         ),
16994                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16995                 unselectable: 'on'
16996             });
16997         };
16998         
16999         var header = {};
17000         
17001         if(!this.header){
17002             header = {
17003                 tag : 'table',
17004                 cls : 'fc-header',
17005                 style : 'width:100%',
17006                 cn : [
17007                     {
17008                         tag: 'tr',
17009                         cn : [
17010                             {
17011                                 tag : 'td',
17012                                 cls : 'fc-header-left',
17013                                 cn : [
17014                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17015                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17016                                     { tag: 'span', cls: 'fc-header-space' },
17017                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17018
17019
17020                                 ]
17021                             },
17022
17023                             {
17024                                 tag : 'td',
17025                                 cls : 'fc-header-center',
17026                                 cn : [
17027                                     {
17028                                         tag: 'span',
17029                                         cls: 'fc-header-title',
17030                                         cn : {
17031                                             tag: 'H2',
17032                                             html : 'month / year'
17033                                         }
17034                                     }
17035
17036                                 ]
17037                             },
17038                             {
17039                                 tag : 'td',
17040                                 cls : 'fc-header-right',
17041                                 cn : [
17042                               /*      fc_button('month', 'left', '', 'month' ),
17043                                     fc_button('week', '', '', 'week' ),
17044                                     fc_button('day', 'right', '', 'day' )
17045                                 */    
17046
17047                                 ]
17048                             }
17049
17050                         ]
17051                     }
17052                 ]
17053             };
17054         }
17055         
17056         header = this.header;
17057         
17058        
17059         var cal_heads = function() {
17060             var ret = [];
17061             // fixme - handle this.
17062             
17063             for (var i =0; i < Date.dayNames.length; i++) {
17064                 var d = Date.dayNames[i];
17065                 ret.push({
17066                     tag: 'th',
17067                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17068                     html : d.substring(0,3)
17069                 });
17070                 
17071             }
17072             ret[0].cls += ' fc-first';
17073             ret[6].cls += ' fc-last';
17074             return ret;
17075         };
17076         var cal_cell = function(n) {
17077             return  {
17078                 tag: 'td',
17079                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17080                 cn : [
17081                     {
17082                         cn : [
17083                             {
17084                                 cls: 'fc-day-number',
17085                                 html: 'D'
17086                             },
17087                             {
17088                                 cls: 'fc-day-content',
17089                              
17090                                 cn : [
17091                                      {
17092                                         style: 'position: relative;' // height: 17px;
17093                                     }
17094                                 ]
17095                             }
17096                             
17097                             
17098                         ]
17099                     }
17100                 ]
17101                 
17102             }
17103         };
17104         var cal_rows = function() {
17105             
17106             var ret = [];
17107             for (var r = 0; r < 6; r++) {
17108                 var row= {
17109                     tag : 'tr',
17110                     cls : 'fc-week',
17111                     cn : []
17112                 };
17113                 
17114                 for (var i =0; i < Date.dayNames.length; i++) {
17115                     var d = Date.dayNames[i];
17116                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17117
17118                 }
17119                 row.cn[0].cls+=' fc-first';
17120                 row.cn[0].cn[0].style = 'min-height:90px';
17121                 row.cn[6].cls+=' fc-last';
17122                 ret.push(row);
17123                 
17124             }
17125             ret[0].cls += ' fc-first';
17126             ret[4].cls += ' fc-prev-last';
17127             ret[5].cls += ' fc-last';
17128             return ret;
17129             
17130         };
17131         
17132         var cal_table = {
17133             tag: 'table',
17134             cls: 'fc-border-separate',
17135             style : 'width:100%',
17136             cellspacing  : 0,
17137             cn : [
17138                 { 
17139                     tag: 'thead',
17140                     cn : [
17141                         { 
17142                             tag: 'tr',
17143                             cls : 'fc-first fc-last',
17144                             cn : cal_heads()
17145                         }
17146                     ]
17147                 },
17148                 { 
17149                     tag: 'tbody',
17150                     cn : cal_rows()
17151                 }
17152                   
17153             ]
17154         };
17155          
17156          var cfg = {
17157             cls : 'fc fc-ltr',
17158             cn : [
17159                 header,
17160                 {
17161                     cls : 'fc-content',
17162                     style : "position: relative;",
17163                     cn : [
17164                         {
17165                             cls : 'fc-view fc-view-month fc-grid',
17166                             style : 'position: relative',
17167                             unselectable : 'on',
17168                             cn : [
17169                                 {
17170                                     cls : 'fc-event-container',
17171                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17172                                 },
17173                                 cal_table
17174                             ]
17175                         }
17176                     ]
17177     
17178                 }
17179            ] 
17180             
17181         };
17182         
17183          
17184         
17185         return cfg;
17186     },
17187     
17188     
17189     initEvents : function()
17190     {
17191         if(!this.store){
17192             throw "can not find store for calendar";
17193         }
17194         
17195         var mark = {
17196             tag: "div",
17197             cls:"x-dlg-mask",
17198             style: "text-align:center",
17199             cn: [
17200                 {
17201                     tag: "div",
17202                     style: "background-color:white;width:50%;margin:250 auto",
17203                     cn: [
17204                         {
17205                             tag: "img",
17206                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17207                         },
17208                         {
17209                             tag: "span",
17210                             html: "Loading"
17211                         }
17212                         
17213                     ]
17214                 }
17215             ]
17216         };
17217         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17218         
17219         var size = this.el.select('.fc-content', true).first().getSize();
17220         this.maskEl.setSize(size.width, size.height);
17221         this.maskEl.enableDisplayMode("block");
17222         if(!this.loadMask){
17223             this.maskEl.hide();
17224         }
17225         
17226         this.store = Roo.factory(this.store, Roo.data);
17227         this.store.on('load', this.onLoad, this);
17228         this.store.on('beforeload', this.onBeforeLoad, this);
17229         
17230         this.resize();
17231         
17232         this.cells = this.el.select('.fc-day',true);
17233         //Roo.log(this.cells);
17234         this.textNodes = this.el.query('.fc-day-number');
17235         this.cells.addClassOnOver('fc-state-hover');
17236         
17237         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17238         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17239         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17240         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17241         
17242         this.on('monthchange', this.onMonthChange, this);
17243         
17244         this.update(new Date().clearTime());
17245     },
17246     
17247     resize : function() {
17248         var sz  = this.el.getSize();
17249         
17250         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17251         this.el.select('.fc-day-content div',true).setHeight(34);
17252     },
17253     
17254     
17255     // private
17256     showPrevMonth : function(e){
17257         this.update(this.activeDate.add("mo", -1));
17258     },
17259     showToday : function(e){
17260         this.update(new Date().clearTime());
17261     },
17262     // private
17263     showNextMonth : function(e){
17264         this.update(this.activeDate.add("mo", 1));
17265     },
17266
17267     // private
17268     showPrevYear : function(){
17269         this.update(this.activeDate.add("y", -1));
17270     },
17271
17272     // private
17273     showNextYear : function(){
17274         this.update(this.activeDate.add("y", 1));
17275     },
17276
17277     
17278    // private
17279     update : function(date)
17280     {
17281         var vd = this.activeDate;
17282         this.activeDate = date;
17283 //        if(vd && this.el){
17284 //            var t = date.getTime();
17285 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17286 //                Roo.log('using add remove');
17287 //                
17288 //                this.fireEvent('monthchange', this, date);
17289 //                
17290 //                this.cells.removeClass("fc-state-highlight");
17291 //                this.cells.each(function(c){
17292 //                   if(c.dateValue == t){
17293 //                       c.addClass("fc-state-highlight");
17294 //                       setTimeout(function(){
17295 //                            try{c.dom.firstChild.focus();}catch(e){}
17296 //                       }, 50);
17297 //                       return false;
17298 //                   }
17299 //                   return true;
17300 //                });
17301 //                return;
17302 //            }
17303 //        }
17304         
17305         var days = date.getDaysInMonth();
17306         
17307         var firstOfMonth = date.getFirstDateOfMonth();
17308         var startingPos = firstOfMonth.getDay()-this.startDay;
17309         
17310         if(startingPos < this.startDay){
17311             startingPos += 7;
17312         }
17313         
17314         var pm = date.add(Date.MONTH, -1);
17315         var prevStart = pm.getDaysInMonth()-startingPos;
17316 //        
17317         this.cells = this.el.select('.fc-day',true);
17318         this.textNodes = this.el.query('.fc-day-number');
17319         this.cells.addClassOnOver('fc-state-hover');
17320         
17321         var cells = this.cells.elements;
17322         var textEls = this.textNodes;
17323         
17324         Roo.each(cells, function(cell){
17325             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17326         });
17327         
17328         days += startingPos;
17329
17330         // convert everything to numbers so it's fast
17331         var day = 86400000;
17332         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17333         //Roo.log(d);
17334         //Roo.log(pm);
17335         //Roo.log(prevStart);
17336         
17337         var today = new Date().clearTime().getTime();
17338         var sel = date.clearTime().getTime();
17339         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17340         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17341         var ddMatch = this.disabledDatesRE;
17342         var ddText = this.disabledDatesText;
17343         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17344         var ddaysText = this.disabledDaysText;
17345         var format = this.format;
17346         
17347         var setCellClass = function(cal, cell){
17348             cell.row = 0;
17349             cell.events = [];
17350             cell.more = [];
17351             //Roo.log('set Cell Class');
17352             cell.title = "";
17353             var t = d.getTime();
17354             
17355             //Roo.log(d);
17356             
17357             cell.dateValue = t;
17358             if(t == today){
17359                 cell.className += " fc-today";
17360                 cell.className += " fc-state-highlight";
17361                 cell.title = cal.todayText;
17362             }
17363             if(t == sel){
17364                 // disable highlight in other month..
17365                 //cell.className += " fc-state-highlight";
17366                 
17367             }
17368             // disabling
17369             if(t < min) {
17370                 cell.className = " fc-state-disabled";
17371                 cell.title = cal.minText;
17372                 return;
17373             }
17374             if(t > max) {
17375                 cell.className = " fc-state-disabled";
17376                 cell.title = cal.maxText;
17377                 return;
17378             }
17379             if(ddays){
17380                 if(ddays.indexOf(d.getDay()) != -1){
17381                     cell.title = ddaysText;
17382                     cell.className = " fc-state-disabled";
17383                 }
17384             }
17385             if(ddMatch && format){
17386                 var fvalue = d.dateFormat(format);
17387                 if(ddMatch.test(fvalue)){
17388                     cell.title = ddText.replace("%0", fvalue);
17389                     cell.className = " fc-state-disabled";
17390                 }
17391             }
17392             
17393             if (!cell.initialClassName) {
17394                 cell.initialClassName = cell.dom.className;
17395             }
17396             
17397             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17398         };
17399
17400         var i = 0;
17401         
17402         for(; i < startingPos; i++) {
17403             textEls[i].innerHTML = (++prevStart);
17404             d.setDate(d.getDate()+1);
17405             
17406             cells[i].className = "fc-past fc-other-month";
17407             setCellClass(this, cells[i]);
17408         }
17409         
17410         var intDay = 0;
17411         
17412         for(; i < days; i++){
17413             intDay = i - startingPos + 1;
17414             textEls[i].innerHTML = (intDay);
17415             d.setDate(d.getDate()+1);
17416             
17417             cells[i].className = ''; // "x-date-active";
17418             setCellClass(this, cells[i]);
17419         }
17420         var extraDays = 0;
17421         
17422         for(; i < 42; i++) {
17423             textEls[i].innerHTML = (++extraDays);
17424             d.setDate(d.getDate()+1);
17425             
17426             cells[i].className = "fc-future fc-other-month";
17427             setCellClass(this, cells[i]);
17428         }
17429         
17430         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17431         
17432         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17433         
17434         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17435         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17436         
17437         if(totalRows != 6){
17438             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17439             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17440         }
17441         
17442         this.fireEvent('monthchange', this, date);
17443         
17444         
17445         /*
17446         if(!this.internalRender){
17447             var main = this.el.dom.firstChild;
17448             var w = main.offsetWidth;
17449             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17450             Roo.fly(main).setWidth(w);
17451             this.internalRender = true;
17452             // opera does not respect the auto grow header center column
17453             // then, after it gets a width opera refuses to recalculate
17454             // without a second pass
17455             if(Roo.isOpera && !this.secondPass){
17456                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17457                 this.secondPass = true;
17458                 this.update.defer(10, this, [date]);
17459             }
17460         }
17461         */
17462         
17463     },
17464     
17465     findCell : function(dt) {
17466         dt = dt.clearTime().getTime();
17467         var ret = false;
17468         this.cells.each(function(c){
17469             //Roo.log("check " +c.dateValue + '?=' + dt);
17470             if(c.dateValue == dt){
17471                 ret = c;
17472                 return false;
17473             }
17474             return true;
17475         });
17476         
17477         return ret;
17478     },
17479     
17480     findCells : function(ev) {
17481         var s = ev.start.clone().clearTime().getTime();
17482        // Roo.log(s);
17483         var e= ev.end.clone().clearTime().getTime();
17484        // Roo.log(e);
17485         var ret = [];
17486         this.cells.each(function(c){
17487              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17488             
17489             if(c.dateValue > e){
17490                 return ;
17491             }
17492             if(c.dateValue < s){
17493                 return ;
17494             }
17495             ret.push(c);
17496         });
17497         
17498         return ret;    
17499     },
17500     
17501 //    findBestRow: function(cells)
17502 //    {
17503 //        var ret = 0;
17504 //        
17505 //        for (var i =0 ; i < cells.length;i++) {
17506 //            ret  = Math.max(cells[i].rows || 0,ret);
17507 //        }
17508 //        return ret;
17509 //        
17510 //    },
17511     
17512     
17513     addItem : function(ev)
17514     {
17515         // look for vertical location slot in
17516         var cells = this.findCells(ev);
17517         
17518 //        ev.row = this.findBestRow(cells);
17519         
17520         // work out the location.
17521         
17522         var crow = false;
17523         var rows = [];
17524         for(var i =0; i < cells.length; i++) {
17525             
17526             cells[i].row = cells[0].row;
17527             
17528             if(i == 0){
17529                 cells[i].row = cells[i].row + 1;
17530             }
17531             
17532             if (!crow) {
17533                 crow = {
17534                     start : cells[i],
17535                     end :  cells[i]
17536                 };
17537                 continue;
17538             }
17539             if (crow.start.getY() == cells[i].getY()) {
17540                 // on same row.
17541                 crow.end = cells[i];
17542                 continue;
17543             }
17544             // different row.
17545             rows.push(crow);
17546             crow = {
17547                 start: cells[i],
17548                 end : cells[i]
17549             };
17550             
17551         }
17552         
17553         rows.push(crow);
17554         ev.els = [];
17555         ev.rows = rows;
17556         ev.cells = cells;
17557         
17558         cells[0].events.push(ev);
17559         
17560         this.calevents.push(ev);
17561     },
17562     
17563     clearEvents: function() {
17564         
17565         if(!this.calevents){
17566             return;
17567         }
17568         
17569         Roo.each(this.cells.elements, function(c){
17570             c.row = 0;
17571             c.events = [];
17572             c.more = [];
17573         });
17574         
17575         Roo.each(this.calevents, function(e) {
17576             Roo.each(e.els, function(el) {
17577                 el.un('mouseenter' ,this.onEventEnter, this);
17578                 el.un('mouseleave' ,this.onEventLeave, this);
17579                 el.remove();
17580             },this);
17581         },this);
17582         
17583         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17584             e.remove();
17585         });
17586         
17587     },
17588     
17589     renderEvents: function()
17590     {   
17591         var _this = this;
17592         
17593         this.cells.each(function(c) {
17594             
17595             if(c.row < 5){
17596                 return;
17597             }
17598             
17599             var ev = c.events;
17600             
17601             var r = 4;
17602             if(c.row != c.events.length){
17603                 r = 4 - (4 - (c.row - c.events.length));
17604             }
17605             
17606             c.events = ev.slice(0, r);
17607             c.more = ev.slice(r);
17608             
17609             if(c.more.length && c.more.length == 1){
17610                 c.events.push(c.more.pop());
17611             }
17612             
17613             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17614             
17615         });
17616             
17617         this.cells.each(function(c) {
17618             
17619             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17620             
17621             
17622             for (var e = 0; e < c.events.length; e++){
17623                 var ev = c.events[e];
17624                 var rows = ev.rows;
17625                 
17626                 for(var i = 0; i < rows.length; i++) {
17627                 
17628                     // how many rows should it span..
17629
17630                     var  cfg = {
17631                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17632                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17633
17634                         unselectable : "on",
17635                         cn : [
17636                             {
17637                                 cls: 'fc-event-inner',
17638                                 cn : [
17639     //                                {
17640     //                                  tag:'span',
17641     //                                  cls: 'fc-event-time',
17642     //                                  html : cells.length > 1 ? '' : ev.time
17643     //                                },
17644                                     {
17645                                       tag:'span',
17646                                       cls: 'fc-event-title',
17647                                       html : String.format('{0}', ev.title)
17648                                     }
17649
17650
17651                                 ]
17652                             },
17653                             {
17654                                 cls: 'ui-resizable-handle ui-resizable-e',
17655                                 html : '&nbsp;&nbsp;&nbsp'
17656                             }
17657
17658                         ]
17659                     };
17660
17661                     if (i == 0) {
17662                         cfg.cls += ' fc-event-start';
17663                     }
17664                     if ((i+1) == rows.length) {
17665                         cfg.cls += ' fc-event-end';
17666                     }
17667
17668                     var ctr = _this.el.select('.fc-event-container',true).first();
17669                     var cg = ctr.createChild(cfg);
17670
17671                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17672                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17673
17674                     var r = (c.more.length) ? 1 : 0;
17675                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17676                     cg.setWidth(ebox.right - sbox.x -2);
17677
17678                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17679                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17680                     cg.on('click', _this.onEventClick, _this, ev);
17681
17682                     ev.els.push(cg);
17683                     
17684                 }
17685                 
17686             }
17687             
17688             
17689             if(c.more.length){
17690                 var  cfg = {
17691                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17692                     style : 'position: absolute',
17693                     unselectable : "on",
17694                     cn : [
17695                         {
17696                             cls: 'fc-event-inner',
17697                             cn : [
17698                                 {
17699                                   tag:'span',
17700                                   cls: 'fc-event-title',
17701                                   html : 'More'
17702                                 }
17703
17704
17705                             ]
17706                         },
17707                         {
17708                             cls: 'ui-resizable-handle ui-resizable-e',
17709                             html : '&nbsp;&nbsp;&nbsp'
17710                         }
17711
17712                     ]
17713                 };
17714
17715                 var ctr = _this.el.select('.fc-event-container',true).first();
17716                 var cg = ctr.createChild(cfg);
17717
17718                 var sbox = c.select('.fc-day-content',true).first().getBox();
17719                 var ebox = c.select('.fc-day-content',true).first().getBox();
17720                 //Roo.log(cg);
17721                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17722                 cg.setWidth(ebox.right - sbox.x -2);
17723
17724                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17725                 
17726             }
17727             
17728         });
17729         
17730         
17731         
17732     },
17733     
17734     onEventEnter: function (e, el,event,d) {
17735         this.fireEvent('evententer', this, el, event);
17736     },
17737     
17738     onEventLeave: function (e, el,event,d) {
17739         this.fireEvent('eventleave', this, el, event);
17740     },
17741     
17742     onEventClick: function (e, el,event,d) {
17743         this.fireEvent('eventclick', this, el, event);
17744     },
17745     
17746     onMonthChange: function () {
17747         this.store.load();
17748     },
17749     
17750     onMoreEventClick: function(e, el, more)
17751     {
17752         var _this = this;
17753         
17754         this.calpopover.placement = 'right';
17755         this.calpopover.setTitle('More');
17756         
17757         this.calpopover.setContent('');
17758         
17759         var ctr = this.calpopover.el.select('.popover-content', true).first();
17760         
17761         Roo.each(more, function(m){
17762             var cfg = {
17763                 cls : 'fc-event-hori fc-event-draggable',
17764                 html : m.title
17765             };
17766             var cg = ctr.createChild(cfg);
17767             
17768             cg.on('click', _this.onEventClick, _this, m);
17769         });
17770         
17771         this.calpopover.show(el);
17772         
17773         
17774     },
17775     
17776     onLoad: function () 
17777     {   
17778         this.calevents = [];
17779         var cal = this;
17780         
17781         if(this.store.getCount() > 0){
17782             this.store.data.each(function(d){
17783                cal.addItem({
17784                     id : d.data.id,
17785                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17786                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17787                     time : d.data.start_time,
17788                     title : d.data.title,
17789                     description : d.data.description,
17790                     venue : d.data.venue
17791                 });
17792             });
17793         }
17794         
17795         this.renderEvents();
17796         
17797         if(this.calevents.length && this.loadMask){
17798             this.maskEl.hide();
17799         }
17800     },
17801     
17802     onBeforeLoad: function()
17803     {
17804         this.clearEvents();
17805         if(this.loadMask){
17806             this.maskEl.show();
17807         }
17808     }
17809 });
17810
17811  
17812  /*
17813  * - LGPL
17814  *
17815  * element
17816  * 
17817  */
17818
17819 /**
17820  * @class Roo.bootstrap.Popover
17821  * @extends Roo.bootstrap.Component
17822  * Bootstrap Popover class
17823  * @cfg {String} html contents of the popover   (or false to use children..)
17824  * @cfg {String} title of popover (or false to hide)
17825  * @cfg {String} placement how it is placed
17826  * @cfg {String} trigger click || hover (or false to trigger manually)
17827  * @cfg {String} over what (parent or false to trigger manually.)
17828  * @cfg {Number} delay - delay before showing
17829  
17830  * @constructor
17831  * Create a new Popover
17832  * @param {Object} config The config object
17833  */
17834
17835 Roo.bootstrap.Popover = function(config){
17836     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17837     
17838     this.addEvents({
17839         // raw events
17840          /**
17841          * @event show
17842          * After the popover show
17843          * 
17844          * @param {Roo.bootstrap.Popover} this
17845          */
17846         "show" : true,
17847         /**
17848          * @event hide
17849          * After the popover hide
17850          * 
17851          * @param {Roo.bootstrap.Popover} this
17852          */
17853         "hide" : true
17854     });
17855 };
17856
17857 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17858     
17859     title: 'Fill in a title',
17860     html: false,
17861     
17862     placement : 'right',
17863     trigger : 'hover', // hover
17864     
17865     delay : 0,
17866     
17867     over: 'parent',
17868     
17869     can_build_overlaid : false,
17870     
17871     getChildContainer : function()
17872     {
17873         return this.el.select('.popover-content',true).first();
17874     },
17875     
17876     getAutoCreate : function(){
17877          
17878         var cfg = {
17879            cls : 'popover roo-dynamic',
17880            style: 'display:block',
17881            cn : [
17882                 {
17883                     cls : 'arrow'
17884                 },
17885                 {
17886                     cls : 'popover-inner',
17887                     cn : [
17888                         {
17889                             tag: 'h3',
17890                             cls: 'popover-title popover-header',
17891                             html : this.title
17892                         },
17893                         {
17894                             cls : 'popover-content popover-body',
17895                             html : this.html
17896                         }
17897                     ]
17898                     
17899                 }
17900            ]
17901         };
17902         
17903         return cfg;
17904     },
17905     setTitle: function(str)
17906     {
17907         this.title = str;
17908         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17909     },
17910     setContent: function(str)
17911     {
17912         this.html = str;
17913         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17914     },
17915     // as it get's added to the bottom of the page.
17916     onRender : function(ct, position)
17917     {
17918         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17919         if(!this.el){
17920             var cfg = Roo.apply({},  this.getAutoCreate());
17921             cfg.id = Roo.id();
17922             
17923             if (this.cls) {
17924                 cfg.cls += ' ' + this.cls;
17925             }
17926             if (this.style) {
17927                 cfg.style = this.style;
17928             }
17929             //Roo.log("adding to ");
17930             this.el = Roo.get(document.body).createChild(cfg, position);
17931 //            Roo.log(this.el);
17932         }
17933         this.initEvents();
17934     },
17935     
17936     initEvents : function()
17937     {
17938         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17939         this.el.enableDisplayMode('block');
17940         this.el.hide();
17941         if (this.over === false) {
17942             return; 
17943         }
17944         if (this.triggers === false) {
17945             return;
17946         }
17947         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17948         var triggers = this.trigger ? this.trigger.split(' ') : [];
17949         Roo.each(triggers, function(trigger) {
17950         
17951             if (trigger == 'click') {
17952                 on_el.on('click', this.toggle, this);
17953             } else if (trigger != 'manual') {
17954                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17955                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17956       
17957                 on_el.on(eventIn  ,this.enter, this);
17958                 on_el.on(eventOut, this.leave, this);
17959             }
17960         }, this);
17961         
17962     },
17963     
17964     
17965     // private
17966     timeout : null,
17967     hoverState : null,
17968     
17969     toggle : function () {
17970         this.hoverState == 'in' ? this.leave() : this.enter();
17971     },
17972     
17973     enter : function () {
17974         
17975         clearTimeout(this.timeout);
17976     
17977         this.hoverState = 'in';
17978     
17979         if (!this.delay || !this.delay.show) {
17980             this.show();
17981             return;
17982         }
17983         var _t = this;
17984         this.timeout = setTimeout(function () {
17985             if (_t.hoverState == 'in') {
17986                 _t.show();
17987             }
17988         }, this.delay.show)
17989     },
17990     
17991     leave : function() {
17992         clearTimeout(this.timeout);
17993     
17994         this.hoverState = 'out';
17995     
17996         if (!this.delay || !this.delay.hide) {
17997             this.hide();
17998             return;
17999         }
18000         var _t = this;
18001         this.timeout = setTimeout(function () {
18002             if (_t.hoverState == 'out') {
18003                 _t.hide();
18004             }
18005         }, this.delay.hide)
18006     },
18007     
18008     show : function (on_el)
18009     {
18010         if (!on_el) {
18011             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18012         }
18013         
18014         // set content.
18015         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18016         if (this.html !== false) {
18017             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18018         }
18019         this.el.removeClass([
18020             'fade','top','bottom', 'left', 'right','in',
18021             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18022         ]);
18023         if (!this.title.length) {
18024             this.el.select('.popover-title',true).hide();
18025         }
18026         
18027         var placement = typeof this.placement == 'function' ?
18028             this.placement.call(this, this.el, on_el) :
18029             this.placement;
18030             
18031         var autoToken = /\s?auto?\s?/i;
18032         var autoPlace = autoToken.test(placement);
18033         if (autoPlace) {
18034             placement = placement.replace(autoToken, '') || 'top';
18035         }
18036         
18037         //this.el.detach()
18038         //this.el.setXY([0,0]);
18039         this.el.show();
18040         this.el.dom.style.display='block';
18041         this.el.addClass(placement);
18042         
18043         //this.el.appendTo(on_el);
18044         
18045         var p = this.getPosition();
18046         var box = this.el.getBox();
18047         
18048         if (autoPlace) {
18049             // fixme..
18050         }
18051         var align = Roo.bootstrap.Popover.alignment[placement];
18052         
18053 //        Roo.log(align);
18054         this.el.alignTo(on_el, align[0],align[1]);
18055         //var arrow = this.el.select('.arrow',true).first();
18056         //arrow.set(align[2], 
18057         
18058         this.el.addClass('in');
18059         
18060         
18061         if (this.el.hasClass('fade')) {
18062             // fade it?
18063         }
18064         
18065         this.hoverState = 'in';
18066         
18067         this.fireEvent('show', this);
18068         
18069     },
18070     hide : function()
18071     {
18072         this.el.setXY([0,0]);
18073         this.el.removeClass('in');
18074         this.el.hide();
18075         this.hoverState = null;
18076         
18077         this.fireEvent('hide', this);
18078     }
18079     
18080 });
18081
18082 Roo.bootstrap.Popover.alignment = {
18083     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18084     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18085     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18086     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18087 };
18088
18089  /*
18090  * - LGPL
18091  *
18092  * Progress
18093  * 
18094  */
18095
18096 /**
18097  * @class Roo.bootstrap.Progress
18098  * @extends Roo.bootstrap.Component
18099  * Bootstrap Progress class
18100  * @cfg {Boolean} striped striped of the progress bar
18101  * @cfg {Boolean} active animated of the progress bar
18102  * 
18103  * 
18104  * @constructor
18105  * Create a new Progress
18106  * @param {Object} config The config object
18107  */
18108
18109 Roo.bootstrap.Progress = function(config){
18110     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18111 };
18112
18113 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18114     
18115     striped : false,
18116     active: false,
18117     
18118     getAutoCreate : function(){
18119         var cfg = {
18120             tag: 'div',
18121             cls: 'progress'
18122         };
18123         
18124         
18125         if(this.striped){
18126             cfg.cls += ' progress-striped';
18127         }
18128       
18129         if(this.active){
18130             cfg.cls += ' active';
18131         }
18132         
18133         
18134         return cfg;
18135     }
18136    
18137 });
18138
18139  
18140
18141  /*
18142  * - LGPL
18143  *
18144  * ProgressBar
18145  * 
18146  */
18147
18148 /**
18149  * @class Roo.bootstrap.ProgressBar
18150  * @extends Roo.bootstrap.Component
18151  * Bootstrap ProgressBar class
18152  * @cfg {Number} aria_valuenow aria-value now
18153  * @cfg {Number} aria_valuemin aria-value min
18154  * @cfg {Number} aria_valuemax aria-value max
18155  * @cfg {String} label label for the progress bar
18156  * @cfg {String} panel (success | info | warning | danger )
18157  * @cfg {String} role role of the progress bar
18158  * @cfg {String} sr_only text
18159  * 
18160  * 
18161  * @constructor
18162  * Create a new ProgressBar
18163  * @param {Object} config The config object
18164  */
18165
18166 Roo.bootstrap.ProgressBar = function(config){
18167     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18168 };
18169
18170 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18171     
18172     aria_valuenow : 0,
18173     aria_valuemin : 0,
18174     aria_valuemax : 100,
18175     label : false,
18176     panel : false,
18177     role : false,
18178     sr_only: false,
18179     
18180     getAutoCreate : function()
18181     {
18182         
18183         var cfg = {
18184             tag: 'div',
18185             cls: 'progress-bar',
18186             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18187         };
18188         
18189         if(this.sr_only){
18190             cfg.cn = {
18191                 tag: 'span',
18192                 cls: 'sr-only',
18193                 html: this.sr_only
18194             }
18195         }
18196         
18197         if(this.role){
18198             cfg.role = this.role;
18199         }
18200         
18201         if(this.aria_valuenow){
18202             cfg['aria-valuenow'] = this.aria_valuenow;
18203         }
18204         
18205         if(this.aria_valuemin){
18206             cfg['aria-valuemin'] = this.aria_valuemin;
18207         }
18208         
18209         if(this.aria_valuemax){
18210             cfg['aria-valuemax'] = this.aria_valuemax;
18211         }
18212         
18213         if(this.label && !this.sr_only){
18214             cfg.html = this.label;
18215         }
18216         
18217         if(this.panel){
18218             cfg.cls += ' progress-bar-' + this.panel;
18219         }
18220         
18221         return cfg;
18222     },
18223     
18224     update : function(aria_valuenow)
18225     {
18226         this.aria_valuenow = aria_valuenow;
18227         
18228         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18229     }
18230    
18231 });
18232
18233  
18234
18235  /*
18236  * - LGPL
18237  *
18238  * column
18239  * 
18240  */
18241
18242 /**
18243  * @class Roo.bootstrap.TabGroup
18244  * @extends Roo.bootstrap.Column
18245  * Bootstrap Column class
18246  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18247  * @cfg {Boolean} carousel true to make the group behave like a carousel
18248  * @cfg {Boolean} bullets show bullets for the panels
18249  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18250  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18251  * @cfg {Boolean} showarrow (true|false) show arrow default true
18252  * 
18253  * @constructor
18254  * Create a new TabGroup
18255  * @param {Object} config The config object
18256  */
18257
18258 Roo.bootstrap.TabGroup = function(config){
18259     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18260     if (!this.navId) {
18261         this.navId = Roo.id();
18262     }
18263     this.tabs = [];
18264     Roo.bootstrap.TabGroup.register(this);
18265     
18266 };
18267
18268 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18269     
18270     carousel : false,
18271     transition : false,
18272     bullets : 0,
18273     timer : 0,
18274     autoslide : false,
18275     slideFn : false,
18276     slideOnTouch : false,
18277     showarrow : true,
18278     
18279     getAutoCreate : function()
18280     {
18281         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18282         
18283         cfg.cls += ' tab-content';
18284         
18285         if (this.carousel) {
18286             cfg.cls += ' carousel slide';
18287             
18288             cfg.cn = [{
18289                cls : 'carousel-inner',
18290                cn : []
18291             }];
18292         
18293             if(this.bullets  && !Roo.isTouch){
18294                 
18295                 var bullets = {
18296                     cls : 'carousel-bullets',
18297                     cn : []
18298                 };
18299                
18300                 if(this.bullets_cls){
18301                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18302                 }
18303                 
18304                 bullets.cn.push({
18305                     cls : 'clear'
18306                 });
18307                 
18308                 cfg.cn[0].cn.push(bullets);
18309             }
18310             
18311             if(this.showarrow){
18312                 cfg.cn[0].cn.push({
18313                     tag : 'div',
18314                     class : 'carousel-arrow',
18315                     cn : [
18316                         {
18317                             tag : 'div',
18318                             class : 'carousel-prev',
18319                             cn : [
18320                                 {
18321                                     tag : 'i',
18322                                     class : 'fa fa-chevron-left'
18323                                 }
18324                             ]
18325                         },
18326                         {
18327                             tag : 'div',
18328                             class : 'carousel-next',
18329                             cn : [
18330                                 {
18331                                     tag : 'i',
18332                                     class : 'fa fa-chevron-right'
18333                                 }
18334                             ]
18335                         }
18336                     ]
18337                 });
18338             }
18339             
18340         }
18341         
18342         return cfg;
18343     },
18344     
18345     initEvents:  function()
18346     {
18347 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18348 //            this.el.on("touchstart", this.onTouchStart, this);
18349 //        }
18350         
18351         if(this.autoslide){
18352             var _this = this;
18353             
18354             this.slideFn = window.setInterval(function() {
18355                 _this.showPanelNext();
18356             }, this.timer);
18357         }
18358         
18359         if(this.showarrow){
18360             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18361             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18362         }
18363         
18364         
18365     },
18366     
18367 //    onTouchStart : function(e, el, o)
18368 //    {
18369 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18370 //            return;
18371 //        }
18372 //        
18373 //        this.showPanelNext();
18374 //    },
18375     
18376     
18377     getChildContainer : function()
18378     {
18379         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18380     },
18381     
18382     /**
18383     * register a Navigation item
18384     * @param {Roo.bootstrap.NavItem} the navitem to add
18385     */
18386     register : function(item)
18387     {
18388         this.tabs.push( item);
18389         item.navId = this.navId; // not really needed..
18390         this.addBullet();
18391     
18392     },
18393     
18394     getActivePanel : function()
18395     {
18396         var r = false;
18397         Roo.each(this.tabs, function(t) {
18398             if (t.active) {
18399                 r = t;
18400                 return false;
18401             }
18402             return null;
18403         });
18404         return r;
18405         
18406     },
18407     getPanelByName : function(n)
18408     {
18409         var r = false;
18410         Roo.each(this.tabs, function(t) {
18411             if (t.tabId == n) {
18412                 r = t;
18413                 return false;
18414             }
18415             return null;
18416         });
18417         return r;
18418     },
18419     indexOfPanel : function(p)
18420     {
18421         var r = false;
18422         Roo.each(this.tabs, function(t,i) {
18423             if (t.tabId == p.tabId) {
18424                 r = i;
18425                 return false;
18426             }
18427             return null;
18428         });
18429         return r;
18430     },
18431     /**
18432      * show a specific panel
18433      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18434      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18435      */
18436     showPanel : function (pan)
18437     {
18438         if(this.transition || typeof(pan) == 'undefined'){
18439             Roo.log("waiting for the transitionend");
18440             return false;
18441         }
18442         
18443         if (typeof(pan) == 'number') {
18444             pan = this.tabs[pan];
18445         }
18446         
18447         if (typeof(pan) == 'string') {
18448             pan = this.getPanelByName(pan);
18449         }
18450         
18451         var cur = this.getActivePanel();
18452         
18453         if(!pan || !cur){
18454             Roo.log('pan or acitve pan is undefined');
18455             return false;
18456         }
18457         
18458         if (pan.tabId == this.getActivePanel().tabId) {
18459             return true;
18460         }
18461         
18462         if (false === cur.fireEvent('beforedeactivate')) {
18463             return false;
18464         }
18465         
18466         if(this.bullets > 0 && !Roo.isTouch){
18467             this.setActiveBullet(this.indexOfPanel(pan));
18468         }
18469         
18470         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18471             
18472             //class="carousel-item carousel-item-next carousel-item-left"
18473             
18474             this.transition = true;
18475             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18476             var lr = dir == 'next' ? 'left' : 'right';
18477             pan.el.addClass(dir); // or prev
18478             pan.el.addClass('carousel-item-' + dir); // or prev
18479             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18480             cur.el.addClass(lr); // or right
18481             pan.el.addClass(lr);
18482             cur.el.addClass('carousel-item-' +lr); // or right
18483             pan.el.addClass('carousel-item-' +lr);
18484             
18485             
18486             var _this = this;
18487             cur.el.on('transitionend', function() {
18488                 Roo.log("trans end?");
18489                 
18490                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18491                 pan.setActive(true);
18492                 
18493                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18494                 cur.setActive(false);
18495                 
18496                 _this.transition = false;
18497                 
18498             }, this, { single:  true } );
18499             
18500             return true;
18501         }
18502         
18503         cur.setActive(false);
18504         pan.setActive(true);
18505         
18506         return true;
18507         
18508     },
18509     showPanelNext : function()
18510     {
18511         var i = this.indexOfPanel(this.getActivePanel());
18512         
18513         if (i >= this.tabs.length - 1 && !this.autoslide) {
18514             return;
18515         }
18516         
18517         if (i >= this.tabs.length - 1 && this.autoslide) {
18518             i = -1;
18519         }
18520         
18521         this.showPanel(this.tabs[i+1]);
18522     },
18523     
18524     showPanelPrev : function()
18525     {
18526         var i = this.indexOfPanel(this.getActivePanel());
18527         
18528         if (i  < 1 && !this.autoslide) {
18529             return;
18530         }
18531         
18532         if (i < 1 && this.autoslide) {
18533             i = this.tabs.length;
18534         }
18535         
18536         this.showPanel(this.tabs[i-1]);
18537     },
18538     
18539     
18540     addBullet: function()
18541     {
18542         if(!this.bullets || Roo.isTouch){
18543             return;
18544         }
18545         var ctr = this.el.select('.carousel-bullets',true).first();
18546         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18547         var bullet = ctr.createChild({
18548             cls : 'bullet bullet-' + i
18549         },ctr.dom.lastChild);
18550         
18551         
18552         var _this = this;
18553         
18554         bullet.on('click', (function(e, el, o, ii, t){
18555
18556             e.preventDefault();
18557
18558             this.showPanel(ii);
18559
18560             if(this.autoslide && this.slideFn){
18561                 clearInterval(this.slideFn);
18562                 this.slideFn = window.setInterval(function() {
18563                     _this.showPanelNext();
18564                 }, this.timer);
18565             }
18566
18567         }).createDelegate(this, [i, bullet], true));
18568                 
18569         
18570     },
18571      
18572     setActiveBullet : function(i)
18573     {
18574         if(Roo.isTouch){
18575             return;
18576         }
18577         
18578         Roo.each(this.el.select('.bullet', true).elements, function(el){
18579             el.removeClass('selected');
18580         });
18581
18582         var bullet = this.el.select('.bullet-' + i, true).first();
18583         
18584         if(!bullet){
18585             return;
18586         }
18587         
18588         bullet.addClass('selected');
18589     }
18590     
18591     
18592   
18593 });
18594
18595  
18596
18597  
18598  
18599 Roo.apply(Roo.bootstrap.TabGroup, {
18600     
18601     groups: {},
18602      /**
18603     * register a Navigation Group
18604     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18605     */
18606     register : function(navgrp)
18607     {
18608         this.groups[navgrp.navId] = navgrp;
18609         
18610     },
18611     /**
18612     * fetch a Navigation Group based on the navigation ID
18613     * if one does not exist , it will get created.
18614     * @param {string} the navgroup to add
18615     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18616     */
18617     get: function(navId) {
18618         if (typeof(this.groups[navId]) == 'undefined') {
18619             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18620         }
18621         return this.groups[navId] ;
18622     }
18623     
18624     
18625     
18626 });
18627
18628  /*
18629  * - LGPL
18630  *
18631  * TabPanel
18632  * 
18633  */
18634
18635 /**
18636  * @class Roo.bootstrap.TabPanel
18637  * @extends Roo.bootstrap.Component
18638  * Bootstrap TabPanel class
18639  * @cfg {Boolean} active panel active
18640  * @cfg {String} html panel content
18641  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18642  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18643  * @cfg {String} href click to link..
18644  * 
18645  * 
18646  * @constructor
18647  * Create a new TabPanel
18648  * @param {Object} config The config object
18649  */
18650
18651 Roo.bootstrap.TabPanel = function(config){
18652     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18653     this.addEvents({
18654         /**
18655              * @event changed
18656              * Fires when the active status changes
18657              * @param {Roo.bootstrap.TabPanel} this
18658              * @param {Boolean} state the new state
18659             
18660          */
18661         'changed': true,
18662         /**
18663              * @event beforedeactivate
18664              * Fires before a tab is de-activated - can be used to do validation on a form.
18665              * @param {Roo.bootstrap.TabPanel} this
18666              * @return {Boolean} false if there is an error
18667             
18668          */
18669         'beforedeactivate': true
18670      });
18671     
18672     this.tabId = this.tabId || Roo.id();
18673   
18674 };
18675
18676 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18677     
18678     active: false,
18679     html: false,
18680     tabId: false,
18681     navId : false,
18682     href : '',
18683     
18684     getAutoCreate : function(){
18685         
18686         
18687         var cfg = {
18688             tag: 'div',
18689             // item is needed for carousel - not sure if it has any effect otherwise
18690             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18691             html: this.html || ''
18692         };
18693         
18694         if(this.active){
18695             cfg.cls += ' active';
18696         }
18697         
18698         if(this.tabId){
18699             cfg.tabId = this.tabId;
18700         }
18701         
18702         
18703         
18704         return cfg;
18705     },
18706     
18707     initEvents:  function()
18708     {
18709         var p = this.parent();
18710         
18711         this.navId = this.navId || p.navId;
18712         
18713         if (typeof(this.navId) != 'undefined') {
18714             // not really needed.. but just in case.. parent should be a NavGroup.
18715             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18716             
18717             tg.register(this);
18718             
18719             var i = tg.tabs.length - 1;
18720             
18721             if(this.active && tg.bullets > 0 && i < tg.bullets){
18722                 tg.setActiveBullet(i);
18723             }
18724         }
18725         
18726         this.el.on('click', this.onClick, this);
18727         
18728         if(Roo.isTouch){
18729             this.el.on("touchstart", this.onTouchStart, this);
18730             this.el.on("touchmove", this.onTouchMove, this);
18731             this.el.on("touchend", this.onTouchEnd, this);
18732         }
18733         
18734     },
18735     
18736     onRender : function(ct, position)
18737     {
18738         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18739     },
18740     
18741     setActive : function(state)
18742     {
18743         Roo.log("panel - set active " + this.tabId + "=" + state);
18744         
18745         this.active = state;
18746         if (!state) {
18747             this.el.removeClass('active');
18748             
18749         } else  if (!this.el.hasClass('active')) {
18750             this.el.addClass('active');
18751         }
18752         
18753         this.fireEvent('changed', this, state);
18754     },
18755     
18756     onClick : function(e)
18757     {
18758         e.preventDefault();
18759         
18760         if(!this.href.length){
18761             return;
18762         }
18763         
18764         window.location.href = this.href;
18765     },
18766     
18767     startX : 0,
18768     startY : 0,
18769     endX : 0,
18770     endY : 0,
18771     swiping : false,
18772     
18773     onTouchStart : function(e)
18774     {
18775         this.swiping = false;
18776         
18777         this.startX = e.browserEvent.touches[0].clientX;
18778         this.startY = e.browserEvent.touches[0].clientY;
18779     },
18780     
18781     onTouchMove : function(e)
18782     {
18783         this.swiping = true;
18784         
18785         this.endX = e.browserEvent.touches[0].clientX;
18786         this.endY = e.browserEvent.touches[0].clientY;
18787     },
18788     
18789     onTouchEnd : function(e)
18790     {
18791         if(!this.swiping){
18792             this.onClick(e);
18793             return;
18794         }
18795         
18796         var tabGroup = this.parent();
18797         
18798         if(this.endX > this.startX){ // swiping right
18799             tabGroup.showPanelPrev();
18800             return;
18801         }
18802         
18803         if(this.startX > this.endX){ // swiping left
18804             tabGroup.showPanelNext();
18805             return;
18806         }
18807     }
18808     
18809     
18810 });
18811  
18812
18813  
18814
18815  /*
18816  * - LGPL
18817  *
18818  * DateField
18819  * 
18820  */
18821
18822 /**
18823  * @class Roo.bootstrap.DateField
18824  * @extends Roo.bootstrap.Input
18825  * Bootstrap DateField class
18826  * @cfg {Number} weekStart default 0
18827  * @cfg {String} viewMode default empty, (months|years)
18828  * @cfg {String} minViewMode default empty, (months|years)
18829  * @cfg {Number} startDate default -Infinity
18830  * @cfg {Number} endDate default Infinity
18831  * @cfg {Boolean} todayHighlight default false
18832  * @cfg {Boolean} todayBtn default false
18833  * @cfg {Boolean} calendarWeeks default false
18834  * @cfg {Object} daysOfWeekDisabled default empty
18835  * @cfg {Boolean} singleMode default false (true | false)
18836  * 
18837  * @cfg {Boolean} keyboardNavigation default true
18838  * @cfg {String} language default en
18839  * 
18840  * @constructor
18841  * Create a new DateField
18842  * @param {Object} config The config object
18843  */
18844
18845 Roo.bootstrap.DateField = function(config){
18846     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18847      this.addEvents({
18848             /**
18849              * @event show
18850              * Fires when this field show.
18851              * @param {Roo.bootstrap.DateField} this
18852              * @param {Mixed} date The date value
18853              */
18854             show : true,
18855             /**
18856              * @event show
18857              * Fires when this field hide.
18858              * @param {Roo.bootstrap.DateField} this
18859              * @param {Mixed} date The date value
18860              */
18861             hide : true,
18862             /**
18863              * @event select
18864              * Fires when select a date.
18865              * @param {Roo.bootstrap.DateField} this
18866              * @param {Mixed} date The date value
18867              */
18868             select : true,
18869             /**
18870              * @event beforeselect
18871              * Fires when before select a date.
18872              * @param {Roo.bootstrap.DateField} this
18873              * @param {Mixed} date The date value
18874              */
18875             beforeselect : true
18876         });
18877 };
18878
18879 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18880     
18881     /**
18882      * @cfg {String} format
18883      * The default date format string which can be overriden for localization support.  The format must be
18884      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18885      */
18886     format : "m/d/y",
18887     /**
18888      * @cfg {String} altFormats
18889      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18890      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18891      */
18892     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18893     
18894     weekStart : 0,
18895     
18896     viewMode : '',
18897     
18898     minViewMode : '',
18899     
18900     todayHighlight : false,
18901     
18902     todayBtn: false,
18903     
18904     language: 'en',
18905     
18906     keyboardNavigation: true,
18907     
18908     calendarWeeks: false,
18909     
18910     startDate: -Infinity,
18911     
18912     endDate: Infinity,
18913     
18914     daysOfWeekDisabled: [],
18915     
18916     _events: [],
18917     
18918     singleMode : false,
18919     
18920     UTCDate: function()
18921     {
18922         return new Date(Date.UTC.apply(Date, arguments));
18923     },
18924     
18925     UTCToday: function()
18926     {
18927         var today = new Date();
18928         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18929     },
18930     
18931     getDate: function() {
18932             var d = this.getUTCDate();
18933             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18934     },
18935     
18936     getUTCDate: function() {
18937             return this.date;
18938     },
18939     
18940     setDate: function(d) {
18941             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18942     },
18943     
18944     setUTCDate: function(d) {
18945             this.date = d;
18946             this.setValue(this.formatDate(this.date));
18947     },
18948         
18949     onRender: function(ct, position)
18950     {
18951         
18952         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18953         
18954         this.language = this.language || 'en';
18955         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18956         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18957         
18958         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18959         this.format = this.format || 'm/d/y';
18960         this.isInline = false;
18961         this.isInput = true;
18962         this.component = this.el.select('.add-on', true).first() || false;
18963         this.component = (this.component && this.component.length === 0) ? false : this.component;
18964         this.hasInput = this.component && this.inputEl().length;
18965         
18966         if (typeof(this.minViewMode === 'string')) {
18967             switch (this.minViewMode) {
18968                 case 'months':
18969                     this.minViewMode = 1;
18970                     break;
18971                 case 'years':
18972                     this.minViewMode = 2;
18973                     break;
18974                 default:
18975                     this.minViewMode = 0;
18976                     break;
18977             }
18978         }
18979         
18980         if (typeof(this.viewMode === 'string')) {
18981             switch (this.viewMode) {
18982                 case 'months':
18983                     this.viewMode = 1;
18984                     break;
18985                 case 'years':
18986                     this.viewMode = 2;
18987                     break;
18988                 default:
18989                     this.viewMode = 0;
18990                     break;
18991             }
18992         }
18993                 
18994         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18995         
18996 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18997         
18998         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18999         
19000         this.picker().on('mousedown', this.onMousedown, this);
19001         this.picker().on('click', this.onClick, this);
19002         
19003         this.picker().addClass('datepicker-dropdown');
19004         
19005         this.startViewMode = this.viewMode;
19006         
19007         if(this.singleMode){
19008             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19009                 v.setVisibilityMode(Roo.Element.DISPLAY);
19010                 v.hide();
19011             });
19012             
19013             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19014                 v.setStyle('width', '189px');
19015             });
19016         }
19017         
19018         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19019             if(!this.calendarWeeks){
19020                 v.remove();
19021                 return;
19022             }
19023             
19024             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19025             v.attr('colspan', function(i, val){
19026                 return parseInt(val) + 1;
19027             });
19028         });
19029                         
19030         
19031         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19032         
19033         this.setStartDate(this.startDate);
19034         this.setEndDate(this.endDate);
19035         
19036         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19037         
19038         this.fillDow();
19039         this.fillMonths();
19040         this.update();
19041         this.showMode();
19042         
19043         if(this.isInline) {
19044             this.showPopup();
19045         }
19046     },
19047     
19048     picker : function()
19049     {
19050         return this.pickerEl;
19051 //        return this.el.select('.datepicker', true).first();
19052     },
19053     
19054     fillDow: function()
19055     {
19056         var dowCnt = this.weekStart;
19057         
19058         var dow = {
19059             tag: 'tr',
19060             cn: [
19061                 
19062             ]
19063         };
19064         
19065         if(this.calendarWeeks){
19066             dow.cn.push({
19067                 tag: 'th',
19068                 cls: 'cw',
19069                 html: '&nbsp;'
19070             })
19071         }
19072         
19073         while (dowCnt < this.weekStart + 7) {
19074             dow.cn.push({
19075                 tag: 'th',
19076                 cls: 'dow',
19077                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19078             });
19079         }
19080         
19081         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19082     },
19083     
19084     fillMonths: function()
19085     {    
19086         var i = 0;
19087         var months = this.picker().select('>.datepicker-months td', true).first();
19088         
19089         months.dom.innerHTML = '';
19090         
19091         while (i < 12) {
19092             var month = {
19093                 tag: 'span',
19094                 cls: 'month',
19095                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19096             };
19097             
19098             months.createChild(month);
19099         }
19100         
19101     },
19102     
19103     update: function()
19104     {
19105         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;
19106         
19107         if (this.date < this.startDate) {
19108             this.viewDate = new Date(this.startDate);
19109         } else if (this.date > this.endDate) {
19110             this.viewDate = new Date(this.endDate);
19111         } else {
19112             this.viewDate = new Date(this.date);
19113         }
19114         
19115         this.fill();
19116     },
19117     
19118     fill: function() 
19119     {
19120         var d = new Date(this.viewDate),
19121                 year = d.getUTCFullYear(),
19122                 month = d.getUTCMonth(),
19123                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19124                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19125                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19126                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19127                 currentDate = this.date && this.date.valueOf(),
19128                 today = this.UTCToday();
19129         
19130         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19131         
19132 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19133         
19134 //        this.picker.select('>tfoot th.today').
19135 //                                              .text(dates[this.language].today)
19136 //                                              .toggle(this.todayBtn !== false);
19137     
19138         this.updateNavArrows();
19139         this.fillMonths();
19140                                                 
19141         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19142         
19143         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19144          
19145         prevMonth.setUTCDate(day);
19146         
19147         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19148         
19149         var nextMonth = new Date(prevMonth);
19150         
19151         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19152         
19153         nextMonth = nextMonth.valueOf();
19154         
19155         var fillMonths = false;
19156         
19157         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19158         
19159         while(prevMonth.valueOf() <= nextMonth) {
19160             var clsName = '';
19161             
19162             if (prevMonth.getUTCDay() === this.weekStart) {
19163                 if(fillMonths){
19164                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19165                 }
19166                     
19167                 fillMonths = {
19168                     tag: 'tr',
19169                     cn: []
19170                 };
19171                 
19172                 if(this.calendarWeeks){
19173                     // ISO 8601: First week contains first thursday.
19174                     // ISO also states week starts on Monday, but we can be more abstract here.
19175                     var
19176                     // Start of current week: based on weekstart/current date
19177                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19178                     // Thursday of this week
19179                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19180                     // First Thursday of year, year from thursday
19181                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19182                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19183                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19184                     
19185                     fillMonths.cn.push({
19186                         tag: 'td',
19187                         cls: 'cw',
19188                         html: calWeek
19189                     });
19190                 }
19191             }
19192             
19193             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19194                 clsName += ' old';
19195             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19196                 clsName += ' new';
19197             }
19198             if (this.todayHighlight &&
19199                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19200                 prevMonth.getUTCMonth() == today.getMonth() &&
19201                 prevMonth.getUTCDate() == today.getDate()) {
19202                 clsName += ' today';
19203             }
19204             
19205             if (currentDate && prevMonth.valueOf() === currentDate) {
19206                 clsName += ' active';
19207             }
19208             
19209             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19210                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19211                     clsName += ' disabled';
19212             }
19213             
19214             fillMonths.cn.push({
19215                 tag: 'td',
19216                 cls: 'day ' + clsName,
19217                 html: prevMonth.getDate()
19218             });
19219             
19220             prevMonth.setDate(prevMonth.getDate()+1);
19221         }
19222           
19223         var currentYear = this.date && this.date.getUTCFullYear();
19224         var currentMonth = this.date && this.date.getUTCMonth();
19225         
19226         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19227         
19228         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19229             v.removeClass('active');
19230             
19231             if(currentYear === year && k === currentMonth){
19232                 v.addClass('active');
19233             }
19234             
19235             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19236                 v.addClass('disabled');
19237             }
19238             
19239         });
19240         
19241         
19242         year = parseInt(year/10, 10) * 10;
19243         
19244         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19245         
19246         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19247         
19248         year -= 1;
19249         for (var i = -1; i < 11; i++) {
19250             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19251                 tag: 'span',
19252                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19253                 html: year
19254             });
19255             
19256             year += 1;
19257         }
19258     },
19259     
19260     showMode: function(dir) 
19261     {
19262         if (dir) {
19263             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19264         }
19265         
19266         Roo.each(this.picker().select('>div',true).elements, function(v){
19267             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19268             v.hide();
19269         });
19270         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19271     },
19272     
19273     place: function()
19274     {
19275         if(this.isInline) {
19276             return;
19277         }
19278         
19279         this.picker().removeClass(['bottom', 'top']);
19280         
19281         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19282             /*
19283              * place to the top of element!
19284              *
19285              */
19286             
19287             this.picker().addClass('top');
19288             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19289             
19290             return;
19291         }
19292         
19293         this.picker().addClass('bottom');
19294         
19295         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19296     },
19297     
19298     parseDate : function(value)
19299     {
19300         if(!value || value instanceof Date){
19301             return value;
19302         }
19303         var v = Date.parseDate(value, this.format);
19304         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19305             v = Date.parseDate(value, 'Y-m-d');
19306         }
19307         if(!v && this.altFormats){
19308             if(!this.altFormatsArray){
19309                 this.altFormatsArray = this.altFormats.split("|");
19310             }
19311             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19312                 v = Date.parseDate(value, this.altFormatsArray[i]);
19313             }
19314         }
19315         return v;
19316     },
19317     
19318     formatDate : function(date, fmt)
19319     {   
19320         return (!date || !(date instanceof Date)) ?
19321         date : date.dateFormat(fmt || this.format);
19322     },
19323     
19324     onFocus : function()
19325     {
19326         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19327         this.showPopup();
19328     },
19329     
19330     onBlur : function()
19331     {
19332         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19333         
19334         var d = this.inputEl().getValue();
19335         
19336         this.setValue(d);
19337                 
19338         this.hidePopup();
19339     },
19340     
19341     showPopup : function()
19342     {
19343         this.picker().show();
19344         this.update();
19345         this.place();
19346         
19347         this.fireEvent('showpopup', this, this.date);
19348     },
19349     
19350     hidePopup : function()
19351     {
19352         if(this.isInline) {
19353             return;
19354         }
19355         this.picker().hide();
19356         this.viewMode = this.startViewMode;
19357         this.showMode();
19358         
19359         this.fireEvent('hidepopup', this, this.date);
19360         
19361     },
19362     
19363     onMousedown: function(e)
19364     {
19365         e.stopPropagation();
19366         e.preventDefault();
19367     },
19368     
19369     keyup: function(e)
19370     {
19371         Roo.bootstrap.DateField.superclass.keyup.call(this);
19372         this.update();
19373     },
19374
19375     setValue: function(v)
19376     {
19377         if(this.fireEvent('beforeselect', this, v) !== false){
19378             var d = new Date(this.parseDate(v) ).clearTime();
19379         
19380             if(isNaN(d.getTime())){
19381                 this.date = this.viewDate = '';
19382                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19383                 return;
19384             }
19385
19386             v = this.formatDate(d);
19387
19388             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19389
19390             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19391
19392             this.update();
19393
19394             this.fireEvent('select', this, this.date);
19395         }
19396     },
19397     
19398     getValue: function()
19399     {
19400         return this.formatDate(this.date);
19401     },
19402     
19403     fireKey: function(e)
19404     {
19405         if (!this.picker().isVisible()){
19406             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19407                 this.showPopup();
19408             }
19409             return;
19410         }
19411         
19412         var dateChanged = false,
19413         dir, day, month,
19414         newDate, newViewDate;
19415         
19416         switch(e.keyCode){
19417             case 27: // escape
19418                 this.hidePopup();
19419                 e.preventDefault();
19420                 break;
19421             case 37: // left
19422             case 39: // right
19423                 if (!this.keyboardNavigation) {
19424                     break;
19425                 }
19426                 dir = e.keyCode == 37 ? -1 : 1;
19427                 
19428                 if (e.ctrlKey){
19429                     newDate = this.moveYear(this.date, dir);
19430                     newViewDate = this.moveYear(this.viewDate, dir);
19431                 } else if (e.shiftKey){
19432                     newDate = this.moveMonth(this.date, dir);
19433                     newViewDate = this.moveMonth(this.viewDate, dir);
19434                 } else {
19435                     newDate = new Date(this.date);
19436                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19437                     newViewDate = new Date(this.viewDate);
19438                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19439                 }
19440                 if (this.dateWithinRange(newDate)){
19441                     this.date = newDate;
19442                     this.viewDate = newViewDate;
19443                     this.setValue(this.formatDate(this.date));
19444 //                    this.update();
19445                     e.preventDefault();
19446                     dateChanged = true;
19447                 }
19448                 break;
19449             case 38: // up
19450             case 40: // down
19451                 if (!this.keyboardNavigation) {
19452                     break;
19453                 }
19454                 dir = e.keyCode == 38 ? -1 : 1;
19455                 if (e.ctrlKey){
19456                     newDate = this.moveYear(this.date, dir);
19457                     newViewDate = this.moveYear(this.viewDate, dir);
19458                 } else if (e.shiftKey){
19459                     newDate = this.moveMonth(this.date, dir);
19460                     newViewDate = this.moveMonth(this.viewDate, dir);
19461                 } else {
19462                     newDate = new Date(this.date);
19463                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19464                     newViewDate = new Date(this.viewDate);
19465                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19466                 }
19467                 if (this.dateWithinRange(newDate)){
19468                     this.date = newDate;
19469                     this.viewDate = newViewDate;
19470                     this.setValue(this.formatDate(this.date));
19471 //                    this.update();
19472                     e.preventDefault();
19473                     dateChanged = true;
19474                 }
19475                 break;
19476             case 13: // enter
19477                 this.setValue(this.formatDate(this.date));
19478                 this.hidePopup();
19479                 e.preventDefault();
19480                 break;
19481             case 9: // tab
19482                 this.setValue(this.formatDate(this.date));
19483                 this.hidePopup();
19484                 break;
19485             case 16: // shift
19486             case 17: // ctrl
19487             case 18: // alt
19488                 break;
19489             default :
19490                 this.hidePopup();
19491                 
19492         }
19493     },
19494     
19495     
19496     onClick: function(e) 
19497     {
19498         e.stopPropagation();
19499         e.preventDefault();
19500         
19501         var target = e.getTarget();
19502         
19503         if(target.nodeName.toLowerCase() === 'i'){
19504             target = Roo.get(target).dom.parentNode;
19505         }
19506         
19507         var nodeName = target.nodeName;
19508         var className = target.className;
19509         var html = target.innerHTML;
19510         //Roo.log(nodeName);
19511         
19512         switch(nodeName.toLowerCase()) {
19513             case 'th':
19514                 switch(className) {
19515                     case 'switch':
19516                         this.showMode(1);
19517                         break;
19518                     case 'prev':
19519                     case 'next':
19520                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19521                         switch(this.viewMode){
19522                                 case 0:
19523                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19524                                         break;
19525                                 case 1:
19526                                 case 2:
19527                                         this.viewDate = this.moveYear(this.viewDate, dir);
19528                                         break;
19529                         }
19530                         this.fill();
19531                         break;
19532                     case 'today':
19533                         var date = new Date();
19534                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19535 //                        this.fill()
19536                         this.setValue(this.formatDate(this.date));
19537                         
19538                         this.hidePopup();
19539                         break;
19540                 }
19541                 break;
19542             case 'span':
19543                 if (className.indexOf('disabled') < 0) {
19544                     this.viewDate.setUTCDate(1);
19545                     if (className.indexOf('month') > -1) {
19546                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19547                     } else {
19548                         var year = parseInt(html, 10) || 0;
19549                         this.viewDate.setUTCFullYear(year);
19550                         
19551                     }
19552                     
19553                     if(this.singleMode){
19554                         this.setValue(this.formatDate(this.viewDate));
19555                         this.hidePopup();
19556                         return;
19557                     }
19558                     
19559                     this.showMode(-1);
19560                     this.fill();
19561                 }
19562                 break;
19563                 
19564             case 'td':
19565                 //Roo.log(className);
19566                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19567                     var day = parseInt(html, 10) || 1;
19568                     var year = this.viewDate.getUTCFullYear(),
19569                         month = this.viewDate.getUTCMonth();
19570
19571                     if (className.indexOf('old') > -1) {
19572                         if(month === 0 ){
19573                             month = 11;
19574                             year -= 1;
19575                         }else{
19576                             month -= 1;
19577                         }
19578                     } else if (className.indexOf('new') > -1) {
19579                         if (month == 11) {
19580                             month = 0;
19581                             year += 1;
19582                         } else {
19583                             month += 1;
19584                         }
19585                     }
19586                     //Roo.log([year,month,day]);
19587                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19588                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19589 //                    this.fill();
19590                     //Roo.log(this.formatDate(this.date));
19591                     this.setValue(this.formatDate(this.date));
19592                     this.hidePopup();
19593                 }
19594                 break;
19595         }
19596     },
19597     
19598     setStartDate: function(startDate)
19599     {
19600         this.startDate = startDate || -Infinity;
19601         if (this.startDate !== -Infinity) {
19602             this.startDate = this.parseDate(this.startDate);
19603         }
19604         this.update();
19605         this.updateNavArrows();
19606     },
19607
19608     setEndDate: function(endDate)
19609     {
19610         this.endDate = endDate || Infinity;
19611         if (this.endDate !== Infinity) {
19612             this.endDate = this.parseDate(this.endDate);
19613         }
19614         this.update();
19615         this.updateNavArrows();
19616     },
19617     
19618     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19619     {
19620         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19621         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19622             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19623         }
19624         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19625             return parseInt(d, 10);
19626         });
19627         this.update();
19628         this.updateNavArrows();
19629     },
19630     
19631     updateNavArrows: function() 
19632     {
19633         if(this.singleMode){
19634             return;
19635         }
19636         
19637         var d = new Date(this.viewDate),
19638         year = d.getUTCFullYear(),
19639         month = d.getUTCMonth();
19640         
19641         Roo.each(this.picker().select('.prev', true).elements, function(v){
19642             v.show();
19643             switch (this.viewMode) {
19644                 case 0:
19645
19646                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19647                         v.hide();
19648                     }
19649                     break;
19650                 case 1:
19651                 case 2:
19652                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19653                         v.hide();
19654                     }
19655                     break;
19656             }
19657         });
19658         
19659         Roo.each(this.picker().select('.next', true).elements, function(v){
19660             v.show();
19661             switch (this.viewMode) {
19662                 case 0:
19663
19664                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19665                         v.hide();
19666                     }
19667                     break;
19668                 case 1:
19669                 case 2:
19670                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19671                         v.hide();
19672                     }
19673                     break;
19674             }
19675         })
19676     },
19677     
19678     moveMonth: function(date, dir)
19679     {
19680         if (!dir) {
19681             return date;
19682         }
19683         var new_date = new Date(date.valueOf()),
19684         day = new_date.getUTCDate(),
19685         month = new_date.getUTCMonth(),
19686         mag = Math.abs(dir),
19687         new_month, test;
19688         dir = dir > 0 ? 1 : -1;
19689         if (mag == 1){
19690             test = dir == -1
19691             // If going back one month, make sure month is not current month
19692             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19693             ? function(){
19694                 return new_date.getUTCMonth() == month;
19695             }
19696             // If going forward one month, make sure month is as expected
19697             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19698             : function(){
19699                 return new_date.getUTCMonth() != new_month;
19700             };
19701             new_month = month + dir;
19702             new_date.setUTCMonth(new_month);
19703             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19704             if (new_month < 0 || new_month > 11) {
19705                 new_month = (new_month + 12) % 12;
19706             }
19707         } else {
19708             // For magnitudes >1, move one month at a time...
19709             for (var i=0; i<mag; i++) {
19710                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19711                 new_date = this.moveMonth(new_date, dir);
19712             }
19713             // ...then reset the day, keeping it in the new month
19714             new_month = new_date.getUTCMonth();
19715             new_date.setUTCDate(day);
19716             test = function(){
19717                 return new_month != new_date.getUTCMonth();
19718             };
19719         }
19720         // Common date-resetting loop -- if date is beyond end of month, make it
19721         // end of month
19722         while (test()){
19723             new_date.setUTCDate(--day);
19724             new_date.setUTCMonth(new_month);
19725         }
19726         return new_date;
19727     },
19728
19729     moveYear: function(date, dir)
19730     {
19731         return this.moveMonth(date, dir*12);
19732     },
19733
19734     dateWithinRange: function(date)
19735     {
19736         return date >= this.startDate && date <= this.endDate;
19737     },
19738
19739     
19740     remove: function() 
19741     {
19742         this.picker().remove();
19743     },
19744     
19745     validateValue : function(value)
19746     {
19747         if(this.getVisibilityEl().hasClass('hidden')){
19748             return true;
19749         }
19750         
19751         if(value.length < 1)  {
19752             if(this.allowBlank){
19753                 return true;
19754             }
19755             return false;
19756         }
19757         
19758         if(value.length < this.minLength){
19759             return false;
19760         }
19761         if(value.length > this.maxLength){
19762             return false;
19763         }
19764         if(this.vtype){
19765             var vt = Roo.form.VTypes;
19766             if(!vt[this.vtype](value, this)){
19767                 return false;
19768             }
19769         }
19770         if(typeof this.validator == "function"){
19771             var msg = this.validator(value);
19772             if(msg !== true){
19773                 return false;
19774             }
19775         }
19776         
19777         if(this.regex && !this.regex.test(value)){
19778             return false;
19779         }
19780         
19781         if(typeof(this.parseDate(value)) == 'undefined'){
19782             return false;
19783         }
19784         
19785         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19786             return false;
19787         }      
19788         
19789         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19790             return false;
19791         } 
19792         
19793         
19794         return true;
19795     },
19796     
19797     reset : function()
19798     {
19799         this.date = this.viewDate = '';
19800         
19801         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19802     }
19803    
19804 });
19805
19806 Roo.apply(Roo.bootstrap.DateField,  {
19807     
19808     head : {
19809         tag: 'thead',
19810         cn: [
19811         {
19812             tag: 'tr',
19813             cn: [
19814             {
19815                 tag: 'th',
19816                 cls: 'prev',
19817                 html: '<i class="fa fa-arrow-left"/>'
19818             },
19819             {
19820                 tag: 'th',
19821                 cls: 'switch',
19822                 colspan: '5'
19823             },
19824             {
19825                 tag: 'th',
19826                 cls: 'next',
19827                 html: '<i class="fa fa-arrow-right"/>'
19828             }
19829
19830             ]
19831         }
19832         ]
19833     },
19834     
19835     content : {
19836         tag: 'tbody',
19837         cn: [
19838         {
19839             tag: 'tr',
19840             cn: [
19841             {
19842                 tag: 'td',
19843                 colspan: '7'
19844             }
19845             ]
19846         }
19847         ]
19848     },
19849     
19850     footer : {
19851         tag: 'tfoot',
19852         cn: [
19853         {
19854             tag: 'tr',
19855             cn: [
19856             {
19857                 tag: 'th',
19858                 colspan: '7',
19859                 cls: 'today'
19860             }
19861                     
19862             ]
19863         }
19864         ]
19865     },
19866     
19867     dates:{
19868         en: {
19869             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19870             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19871             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19872             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19873             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19874             today: "Today"
19875         }
19876     },
19877     
19878     modes: [
19879     {
19880         clsName: 'days',
19881         navFnc: 'Month',
19882         navStep: 1
19883     },
19884     {
19885         clsName: 'months',
19886         navFnc: 'FullYear',
19887         navStep: 1
19888     },
19889     {
19890         clsName: 'years',
19891         navFnc: 'FullYear',
19892         navStep: 10
19893     }]
19894 });
19895
19896 Roo.apply(Roo.bootstrap.DateField,  {
19897   
19898     template : {
19899         tag: 'div',
19900         cls: 'datepicker dropdown-menu roo-dynamic',
19901         cn: [
19902         {
19903             tag: 'div',
19904             cls: 'datepicker-days',
19905             cn: [
19906             {
19907                 tag: 'table',
19908                 cls: 'table-condensed',
19909                 cn:[
19910                 Roo.bootstrap.DateField.head,
19911                 {
19912                     tag: 'tbody'
19913                 },
19914                 Roo.bootstrap.DateField.footer
19915                 ]
19916             }
19917             ]
19918         },
19919         {
19920             tag: 'div',
19921             cls: 'datepicker-months',
19922             cn: [
19923             {
19924                 tag: 'table',
19925                 cls: 'table-condensed',
19926                 cn:[
19927                 Roo.bootstrap.DateField.head,
19928                 Roo.bootstrap.DateField.content,
19929                 Roo.bootstrap.DateField.footer
19930                 ]
19931             }
19932             ]
19933         },
19934         {
19935             tag: 'div',
19936             cls: 'datepicker-years',
19937             cn: [
19938             {
19939                 tag: 'table',
19940                 cls: 'table-condensed',
19941                 cn:[
19942                 Roo.bootstrap.DateField.head,
19943                 Roo.bootstrap.DateField.content,
19944                 Roo.bootstrap.DateField.footer
19945                 ]
19946             }
19947             ]
19948         }
19949         ]
19950     }
19951 });
19952
19953  
19954
19955  /*
19956  * - LGPL
19957  *
19958  * TimeField
19959  * 
19960  */
19961
19962 /**
19963  * @class Roo.bootstrap.TimeField
19964  * @extends Roo.bootstrap.Input
19965  * Bootstrap DateField class
19966  * 
19967  * 
19968  * @constructor
19969  * Create a new TimeField
19970  * @param {Object} config The config object
19971  */
19972
19973 Roo.bootstrap.TimeField = function(config){
19974     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19975     this.addEvents({
19976             /**
19977              * @event show
19978              * Fires when this field show.
19979              * @param {Roo.bootstrap.DateField} thisthis
19980              * @param {Mixed} date The date value
19981              */
19982             show : true,
19983             /**
19984              * @event show
19985              * Fires when this field hide.
19986              * @param {Roo.bootstrap.DateField} this
19987              * @param {Mixed} date The date value
19988              */
19989             hide : true,
19990             /**
19991              * @event select
19992              * Fires when select a date.
19993              * @param {Roo.bootstrap.DateField} this
19994              * @param {Mixed} date The date value
19995              */
19996             select : true
19997         });
19998 };
19999
20000 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20001     
20002     /**
20003      * @cfg {String} format
20004      * The default time format string which can be overriden for localization support.  The format must be
20005      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20006      */
20007     format : "H:i",
20008        
20009     onRender: function(ct, position)
20010     {
20011         
20012         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20013                 
20014         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20015         
20016         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20017         
20018         this.pop = this.picker().select('>.datepicker-time',true).first();
20019         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20020         
20021         this.picker().on('mousedown', this.onMousedown, this);
20022         this.picker().on('click', this.onClick, this);
20023         
20024         this.picker().addClass('datepicker-dropdown');
20025     
20026         this.fillTime();
20027         this.update();
20028             
20029         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20030         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20031         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20032         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20033         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20034         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20035
20036     },
20037     
20038     fireKey: function(e){
20039         if (!this.picker().isVisible()){
20040             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20041                 this.show();
20042             }
20043             return;
20044         }
20045
20046         e.preventDefault();
20047         
20048         switch(e.keyCode){
20049             case 27: // escape
20050                 this.hide();
20051                 break;
20052             case 37: // left
20053             case 39: // right
20054                 this.onTogglePeriod();
20055                 break;
20056             case 38: // up
20057                 this.onIncrementMinutes();
20058                 break;
20059             case 40: // down
20060                 this.onDecrementMinutes();
20061                 break;
20062             case 13: // enter
20063             case 9: // tab
20064                 this.setTime();
20065                 break;
20066         }
20067     },
20068     
20069     onClick: function(e) {
20070         e.stopPropagation();
20071         e.preventDefault();
20072     },
20073     
20074     picker : function()
20075     {
20076         return this.el.select('.datepicker', true).first();
20077     },
20078     
20079     fillTime: function()
20080     {    
20081         var time = this.pop.select('tbody', true).first();
20082         
20083         time.dom.innerHTML = '';
20084         
20085         time.createChild({
20086             tag: 'tr',
20087             cn: [
20088                 {
20089                     tag: 'td',
20090                     cn: [
20091                         {
20092                             tag: 'a',
20093                             href: '#',
20094                             cls: 'btn',
20095                             cn: [
20096                                 {
20097                                     tag: 'span',
20098                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20099                                 }
20100                             ]
20101                         } 
20102                     ]
20103                 },
20104                 {
20105                     tag: 'td',
20106                     cls: 'separator'
20107                 },
20108                 {
20109                     tag: 'td',
20110                     cn: [
20111                         {
20112                             tag: 'a',
20113                             href: '#',
20114                             cls: 'btn',
20115                             cn: [
20116                                 {
20117                                     tag: 'span',
20118                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20119                                 }
20120                             ]
20121                         }
20122                     ]
20123                 },
20124                 {
20125                     tag: 'td',
20126                     cls: 'separator'
20127                 }
20128             ]
20129         });
20130         
20131         time.createChild({
20132             tag: 'tr',
20133             cn: [
20134                 {
20135                     tag: 'td',
20136                     cn: [
20137                         {
20138                             tag: 'span',
20139                             cls: 'timepicker-hour',
20140                             html: '00'
20141                         }  
20142                     ]
20143                 },
20144                 {
20145                     tag: 'td',
20146                     cls: 'separator',
20147                     html: ':'
20148                 },
20149                 {
20150                     tag: 'td',
20151                     cn: [
20152                         {
20153                             tag: 'span',
20154                             cls: 'timepicker-minute',
20155                             html: '00'
20156                         }  
20157                     ]
20158                 },
20159                 {
20160                     tag: 'td',
20161                     cls: 'separator'
20162                 },
20163                 {
20164                     tag: 'td',
20165                     cn: [
20166                         {
20167                             tag: 'button',
20168                             type: 'button',
20169                             cls: 'btn btn-primary period',
20170                             html: 'AM'
20171                             
20172                         }
20173                     ]
20174                 }
20175             ]
20176         });
20177         
20178         time.createChild({
20179             tag: 'tr',
20180             cn: [
20181                 {
20182                     tag: 'td',
20183                     cn: [
20184                         {
20185                             tag: 'a',
20186                             href: '#',
20187                             cls: 'btn',
20188                             cn: [
20189                                 {
20190                                     tag: 'span',
20191                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20192                                 }
20193                             ]
20194                         }
20195                     ]
20196                 },
20197                 {
20198                     tag: 'td',
20199                     cls: 'separator'
20200                 },
20201                 {
20202                     tag: 'td',
20203                     cn: [
20204                         {
20205                             tag: 'a',
20206                             href: '#',
20207                             cls: 'btn',
20208                             cn: [
20209                                 {
20210                                     tag: 'span',
20211                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20212                                 }
20213                             ]
20214                         }
20215                     ]
20216                 },
20217                 {
20218                     tag: 'td',
20219                     cls: 'separator'
20220                 }
20221             ]
20222         });
20223         
20224     },
20225     
20226     update: function()
20227     {
20228         
20229         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20230         
20231         this.fill();
20232     },
20233     
20234     fill: function() 
20235     {
20236         var hours = this.time.getHours();
20237         var minutes = this.time.getMinutes();
20238         var period = 'AM';
20239         
20240         if(hours > 11){
20241             period = 'PM';
20242         }
20243         
20244         if(hours == 0){
20245             hours = 12;
20246         }
20247         
20248         
20249         if(hours > 12){
20250             hours = hours - 12;
20251         }
20252         
20253         if(hours < 10){
20254             hours = '0' + hours;
20255         }
20256         
20257         if(minutes < 10){
20258             minutes = '0' + minutes;
20259         }
20260         
20261         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20262         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20263         this.pop.select('button', true).first().dom.innerHTML = period;
20264         
20265     },
20266     
20267     place: function()
20268     {   
20269         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20270         
20271         var cls = ['bottom'];
20272         
20273         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20274             cls.pop();
20275             cls.push('top');
20276         }
20277         
20278         cls.push('right');
20279         
20280         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20281             cls.pop();
20282             cls.push('left');
20283         }
20284         
20285         this.picker().addClass(cls.join('-'));
20286         
20287         var _this = this;
20288         
20289         Roo.each(cls, function(c){
20290             if(c == 'bottom'){
20291                 _this.picker().setTop(_this.inputEl().getHeight());
20292                 return;
20293             }
20294             if(c == 'top'){
20295                 _this.picker().setTop(0 - _this.picker().getHeight());
20296                 return;
20297             }
20298             
20299             if(c == 'left'){
20300                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20301                 return;
20302             }
20303             if(c == 'right'){
20304                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20305                 return;
20306             }
20307         });
20308         
20309     },
20310   
20311     onFocus : function()
20312     {
20313         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20314         this.show();
20315     },
20316     
20317     onBlur : function()
20318     {
20319         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20320         this.hide();
20321     },
20322     
20323     show : function()
20324     {
20325         this.picker().show();
20326         this.pop.show();
20327         this.update();
20328         this.place();
20329         
20330         this.fireEvent('show', this, this.date);
20331     },
20332     
20333     hide : function()
20334     {
20335         this.picker().hide();
20336         this.pop.hide();
20337         
20338         this.fireEvent('hide', this, this.date);
20339     },
20340     
20341     setTime : function()
20342     {
20343         this.hide();
20344         this.setValue(this.time.format(this.format));
20345         
20346         this.fireEvent('select', this, this.date);
20347         
20348         
20349     },
20350     
20351     onMousedown: function(e){
20352         e.stopPropagation();
20353         e.preventDefault();
20354     },
20355     
20356     onIncrementHours: function()
20357     {
20358         Roo.log('onIncrementHours');
20359         this.time = this.time.add(Date.HOUR, 1);
20360         this.update();
20361         
20362     },
20363     
20364     onDecrementHours: function()
20365     {
20366         Roo.log('onDecrementHours');
20367         this.time = this.time.add(Date.HOUR, -1);
20368         this.update();
20369     },
20370     
20371     onIncrementMinutes: function()
20372     {
20373         Roo.log('onIncrementMinutes');
20374         this.time = this.time.add(Date.MINUTE, 1);
20375         this.update();
20376     },
20377     
20378     onDecrementMinutes: function()
20379     {
20380         Roo.log('onDecrementMinutes');
20381         this.time = this.time.add(Date.MINUTE, -1);
20382         this.update();
20383     },
20384     
20385     onTogglePeriod: function()
20386     {
20387         Roo.log('onTogglePeriod');
20388         this.time = this.time.add(Date.HOUR, 12);
20389         this.update();
20390     }
20391     
20392    
20393 });
20394
20395 Roo.apply(Roo.bootstrap.TimeField,  {
20396     
20397     content : {
20398         tag: 'tbody',
20399         cn: [
20400             {
20401                 tag: 'tr',
20402                 cn: [
20403                 {
20404                     tag: 'td',
20405                     colspan: '7'
20406                 }
20407                 ]
20408             }
20409         ]
20410     },
20411     
20412     footer : {
20413         tag: 'tfoot',
20414         cn: [
20415             {
20416                 tag: 'tr',
20417                 cn: [
20418                 {
20419                     tag: 'th',
20420                     colspan: '7',
20421                     cls: '',
20422                     cn: [
20423                         {
20424                             tag: 'button',
20425                             cls: 'btn btn-info ok',
20426                             html: 'OK'
20427                         }
20428                     ]
20429                 }
20430
20431                 ]
20432             }
20433         ]
20434     }
20435 });
20436
20437 Roo.apply(Roo.bootstrap.TimeField,  {
20438   
20439     template : {
20440         tag: 'div',
20441         cls: 'datepicker dropdown-menu',
20442         cn: [
20443             {
20444                 tag: 'div',
20445                 cls: 'datepicker-time',
20446                 cn: [
20447                 {
20448                     tag: 'table',
20449                     cls: 'table-condensed',
20450                     cn:[
20451                     Roo.bootstrap.TimeField.content,
20452                     Roo.bootstrap.TimeField.footer
20453                     ]
20454                 }
20455                 ]
20456             }
20457         ]
20458     }
20459 });
20460
20461  
20462
20463  /*
20464  * - LGPL
20465  *
20466  * MonthField
20467  * 
20468  */
20469
20470 /**
20471  * @class Roo.bootstrap.MonthField
20472  * @extends Roo.bootstrap.Input
20473  * Bootstrap MonthField class
20474  * 
20475  * @cfg {String} language default en
20476  * 
20477  * @constructor
20478  * Create a new MonthField
20479  * @param {Object} config The config object
20480  */
20481
20482 Roo.bootstrap.MonthField = function(config){
20483     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20484     
20485     this.addEvents({
20486         /**
20487          * @event show
20488          * Fires when this field show.
20489          * @param {Roo.bootstrap.MonthField} this
20490          * @param {Mixed} date The date value
20491          */
20492         show : true,
20493         /**
20494          * @event show
20495          * Fires when this field hide.
20496          * @param {Roo.bootstrap.MonthField} this
20497          * @param {Mixed} date The date value
20498          */
20499         hide : true,
20500         /**
20501          * @event select
20502          * Fires when select a date.
20503          * @param {Roo.bootstrap.MonthField} this
20504          * @param {String} oldvalue The old value
20505          * @param {String} newvalue The new value
20506          */
20507         select : true
20508     });
20509 };
20510
20511 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20512     
20513     onRender: function(ct, position)
20514     {
20515         
20516         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20517         
20518         this.language = this.language || 'en';
20519         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20520         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20521         
20522         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20523         this.isInline = false;
20524         this.isInput = true;
20525         this.component = this.el.select('.add-on', true).first() || false;
20526         this.component = (this.component && this.component.length === 0) ? false : this.component;
20527         this.hasInput = this.component && this.inputEL().length;
20528         
20529         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20530         
20531         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20532         
20533         this.picker().on('mousedown', this.onMousedown, this);
20534         this.picker().on('click', this.onClick, this);
20535         
20536         this.picker().addClass('datepicker-dropdown');
20537         
20538         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20539             v.setStyle('width', '189px');
20540         });
20541         
20542         this.fillMonths();
20543         
20544         this.update();
20545         
20546         if(this.isInline) {
20547             this.show();
20548         }
20549         
20550     },
20551     
20552     setValue: function(v, suppressEvent)
20553     {   
20554         var o = this.getValue();
20555         
20556         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20557         
20558         this.update();
20559
20560         if(suppressEvent !== true){
20561             this.fireEvent('select', this, o, v);
20562         }
20563         
20564     },
20565     
20566     getValue: function()
20567     {
20568         return this.value;
20569     },
20570     
20571     onClick: function(e) 
20572     {
20573         e.stopPropagation();
20574         e.preventDefault();
20575         
20576         var target = e.getTarget();
20577         
20578         if(target.nodeName.toLowerCase() === 'i'){
20579             target = Roo.get(target).dom.parentNode;
20580         }
20581         
20582         var nodeName = target.nodeName;
20583         var className = target.className;
20584         var html = target.innerHTML;
20585         
20586         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20587             return;
20588         }
20589         
20590         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20591         
20592         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20593         
20594         this.hide();
20595                         
20596     },
20597     
20598     picker : function()
20599     {
20600         return this.pickerEl;
20601     },
20602     
20603     fillMonths: function()
20604     {    
20605         var i = 0;
20606         var months = this.picker().select('>.datepicker-months td', true).first();
20607         
20608         months.dom.innerHTML = '';
20609         
20610         while (i < 12) {
20611             var month = {
20612                 tag: 'span',
20613                 cls: 'month',
20614                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20615             };
20616             
20617             months.createChild(month);
20618         }
20619         
20620     },
20621     
20622     update: function()
20623     {
20624         var _this = this;
20625         
20626         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20627             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20628         }
20629         
20630         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20631             e.removeClass('active');
20632             
20633             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20634                 e.addClass('active');
20635             }
20636         })
20637     },
20638     
20639     place: function()
20640     {
20641         if(this.isInline) {
20642             return;
20643         }
20644         
20645         this.picker().removeClass(['bottom', 'top']);
20646         
20647         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20648             /*
20649              * place to the top of element!
20650              *
20651              */
20652             
20653             this.picker().addClass('top');
20654             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20655             
20656             return;
20657         }
20658         
20659         this.picker().addClass('bottom');
20660         
20661         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20662     },
20663     
20664     onFocus : function()
20665     {
20666         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20667         this.show();
20668     },
20669     
20670     onBlur : function()
20671     {
20672         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20673         
20674         var d = this.inputEl().getValue();
20675         
20676         this.setValue(d);
20677                 
20678         this.hide();
20679     },
20680     
20681     show : function()
20682     {
20683         this.picker().show();
20684         this.picker().select('>.datepicker-months', true).first().show();
20685         this.update();
20686         this.place();
20687         
20688         this.fireEvent('show', this, this.date);
20689     },
20690     
20691     hide : function()
20692     {
20693         if(this.isInline) {
20694             return;
20695         }
20696         this.picker().hide();
20697         this.fireEvent('hide', this, this.date);
20698         
20699     },
20700     
20701     onMousedown: function(e)
20702     {
20703         e.stopPropagation();
20704         e.preventDefault();
20705     },
20706     
20707     keyup: function(e)
20708     {
20709         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20710         this.update();
20711     },
20712
20713     fireKey: function(e)
20714     {
20715         if (!this.picker().isVisible()){
20716             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20717                 this.show();
20718             }
20719             return;
20720         }
20721         
20722         var dir;
20723         
20724         switch(e.keyCode){
20725             case 27: // escape
20726                 this.hide();
20727                 e.preventDefault();
20728                 break;
20729             case 37: // left
20730             case 39: // right
20731                 dir = e.keyCode == 37 ? -1 : 1;
20732                 
20733                 this.vIndex = this.vIndex + dir;
20734                 
20735                 if(this.vIndex < 0){
20736                     this.vIndex = 0;
20737                 }
20738                 
20739                 if(this.vIndex > 11){
20740                     this.vIndex = 11;
20741                 }
20742                 
20743                 if(isNaN(this.vIndex)){
20744                     this.vIndex = 0;
20745                 }
20746                 
20747                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20748                 
20749                 break;
20750             case 38: // up
20751             case 40: // down
20752                 
20753                 dir = e.keyCode == 38 ? -1 : 1;
20754                 
20755                 this.vIndex = this.vIndex + dir * 4;
20756                 
20757                 if(this.vIndex < 0){
20758                     this.vIndex = 0;
20759                 }
20760                 
20761                 if(this.vIndex > 11){
20762                     this.vIndex = 11;
20763                 }
20764                 
20765                 if(isNaN(this.vIndex)){
20766                     this.vIndex = 0;
20767                 }
20768                 
20769                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20770                 break;
20771                 
20772             case 13: // enter
20773                 
20774                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20775                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20776                 }
20777                 
20778                 this.hide();
20779                 e.preventDefault();
20780                 break;
20781             case 9: // tab
20782                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20783                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20784                 }
20785                 this.hide();
20786                 break;
20787             case 16: // shift
20788             case 17: // ctrl
20789             case 18: // alt
20790                 break;
20791             default :
20792                 this.hide();
20793                 
20794         }
20795     },
20796     
20797     remove: function() 
20798     {
20799         this.picker().remove();
20800     }
20801    
20802 });
20803
20804 Roo.apply(Roo.bootstrap.MonthField,  {
20805     
20806     content : {
20807         tag: 'tbody',
20808         cn: [
20809         {
20810             tag: 'tr',
20811             cn: [
20812             {
20813                 tag: 'td',
20814                 colspan: '7'
20815             }
20816             ]
20817         }
20818         ]
20819     },
20820     
20821     dates:{
20822         en: {
20823             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20824             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20825         }
20826     }
20827 });
20828
20829 Roo.apply(Roo.bootstrap.MonthField,  {
20830   
20831     template : {
20832         tag: 'div',
20833         cls: 'datepicker dropdown-menu roo-dynamic',
20834         cn: [
20835             {
20836                 tag: 'div',
20837                 cls: 'datepicker-months',
20838                 cn: [
20839                 {
20840                     tag: 'table',
20841                     cls: 'table-condensed',
20842                     cn:[
20843                         Roo.bootstrap.DateField.content
20844                     ]
20845                 }
20846                 ]
20847             }
20848         ]
20849     }
20850 });
20851
20852  
20853
20854  
20855  /*
20856  * - LGPL
20857  *
20858  * CheckBox
20859  * 
20860  */
20861
20862 /**
20863  * @class Roo.bootstrap.CheckBox
20864  * @extends Roo.bootstrap.Input
20865  * Bootstrap CheckBox class
20866  * 
20867  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20868  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20869  * @cfg {String} boxLabel The text that appears beside the checkbox
20870  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20871  * @cfg {Boolean} checked initnal the element
20872  * @cfg {Boolean} inline inline the element (default false)
20873  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20874  * @cfg {String} tooltip label tooltip
20875  * 
20876  * @constructor
20877  * Create a new CheckBox
20878  * @param {Object} config The config object
20879  */
20880
20881 Roo.bootstrap.CheckBox = function(config){
20882     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20883    
20884     this.addEvents({
20885         /**
20886         * @event check
20887         * Fires when the element is checked or unchecked.
20888         * @param {Roo.bootstrap.CheckBox} this This input
20889         * @param {Boolean} checked The new checked value
20890         */
20891        check : true,
20892        /**
20893         * @event click
20894         * Fires when the element is click.
20895         * @param {Roo.bootstrap.CheckBox} this This input
20896         */
20897        click : true
20898     });
20899     
20900 };
20901
20902 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20903   
20904     inputType: 'checkbox',
20905     inputValue: 1,
20906     valueOff: 0,
20907     boxLabel: false,
20908     checked: false,
20909     weight : false,
20910     inline: false,
20911     tooltip : '',
20912     
20913     getAutoCreate : function()
20914     {
20915         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20916         
20917         var id = Roo.id();
20918         
20919         var cfg = {};
20920         
20921         cfg.cls = 'form-group ' + this.inputType; //input-group
20922         
20923         if(this.inline){
20924             cfg.cls += ' ' + this.inputType + '-inline';
20925         }
20926         
20927         var input =  {
20928             tag: 'input',
20929             id : id,
20930             type : this.inputType,
20931             value : this.inputValue,
20932             cls : 'roo-' + this.inputType, //'form-box',
20933             placeholder : this.placeholder || ''
20934             
20935         };
20936         
20937         if(this.inputType != 'radio'){
20938             var hidden =  {
20939                 tag: 'input',
20940                 type : 'hidden',
20941                 cls : 'roo-hidden-value',
20942                 value : this.checked ? this.inputValue : this.valueOff
20943             };
20944         }
20945         
20946             
20947         if (this.weight) { // Validity check?
20948             cfg.cls += " " + this.inputType + "-" + this.weight;
20949         }
20950         
20951         if (this.disabled) {
20952             input.disabled=true;
20953         }
20954         
20955         if(this.checked){
20956             input.checked = this.checked;
20957         }
20958         
20959         if (this.name) {
20960             
20961             input.name = this.name;
20962             
20963             if(this.inputType != 'radio'){
20964                 hidden.name = this.name;
20965                 input.name = '_hidden_' + this.name;
20966             }
20967         }
20968         
20969         if (this.size) {
20970             input.cls += ' input-' + this.size;
20971         }
20972         
20973         var settings=this;
20974         
20975         ['xs','sm','md','lg'].map(function(size){
20976             if (settings[size]) {
20977                 cfg.cls += ' col-' + size + '-' + settings[size];
20978             }
20979         });
20980         
20981         var inputblock = input;
20982          
20983         if (this.before || this.after) {
20984             
20985             inputblock = {
20986                 cls : 'input-group',
20987                 cn :  [] 
20988             };
20989             
20990             if (this.before) {
20991                 inputblock.cn.push({
20992                     tag :'span',
20993                     cls : 'input-group-addon',
20994                     html : this.before
20995                 });
20996             }
20997             
20998             inputblock.cn.push(input);
20999             
21000             if(this.inputType != 'radio'){
21001                 inputblock.cn.push(hidden);
21002             }
21003             
21004             if (this.after) {
21005                 inputblock.cn.push({
21006                     tag :'span',
21007                     cls : 'input-group-addon',
21008                     html : this.after
21009                 });
21010             }
21011             
21012         }
21013         
21014         if (align ==='left' && this.fieldLabel.length) {
21015 //                Roo.log("left and has label");
21016             cfg.cn = [
21017                 {
21018                     tag: 'label',
21019                     'for' :  id,
21020                     cls : 'control-label',
21021                     html : this.fieldLabel
21022                 },
21023                 {
21024                     cls : "", 
21025                     cn: [
21026                         inputblock
21027                     ]
21028                 }
21029             ];
21030             
21031             if(this.labelWidth > 12){
21032                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21033             }
21034             
21035             if(this.labelWidth < 13 && this.labelmd == 0){
21036                 this.labelmd = this.labelWidth;
21037             }
21038             
21039             if(this.labellg > 0){
21040                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21041                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21042             }
21043             
21044             if(this.labelmd > 0){
21045                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21046                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21047             }
21048             
21049             if(this.labelsm > 0){
21050                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21051                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21052             }
21053             
21054             if(this.labelxs > 0){
21055                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21056                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21057             }
21058             
21059         } else if ( this.fieldLabel.length) {
21060 //                Roo.log(" label");
21061                 cfg.cn = [
21062                    
21063                     {
21064                         tag: this.boxLabel ? 'span' : 'label',
21065                         'for': id,
21066                         cls: 'control-label box-input-label',
21067                         //cls : 'input-group-addon',
21068                         html : this.fieldLabel
21069                     },
21070                     
21071                     inputblock
21072                     
21073                 ];
21074
21075         } else {
21076             
21077 //                Roo.log(" no label && no align");
21078                 cfg.cn = [  inputblock ] ;
21079                 
21080                 
21081         }
21082         
21083         if(this.boxLabel){
21084              var boxLabelCfg = {
21085                 tag: 'label',
21086                 //'for': id, // box label is handled by onclick - so no for...
21087                 cls: 'box-label',
21088                 html: this.boxLabel
21089             };
21090             
21091             if(this.tooltip){
21092                 boxLabelCfg.tooltip = this.tooltip;
21093             }
21094              
21095             cfg.cn.push(boxLabelCfg);
21096         }
21097         
21098         if(this.inputType != 'radio'){
21099             cfg.cn.push(hidden);
21100         }
21101         
21102         return cfg;
21103         
21104     },
21105     
21106     /**
21107      * return the real input element.
21108      */
21109     inputEl: function ()
21110     {
21111         return this.el.select('input.roo-' + this.inputType,true).first();
21112     },
21113     hiddenEl: function ()
21114     {
21115         return this.el.select('input.roo-hidden-value',true).first();
21116     },
21117     
21118     labelEl: function()
21119     {
21120         return this.el.select('label.control-label',true).first();
21121     },
21122     /* depricated... */
21123     
21124     label: function()
21125     {
21126         return this.labelEl();
21127     },
21128     
21129     boxLabelEl: function()
21130     {
21131         return this.el.select('label.box-label',true).first();
21132     },
21133     
21134     initEvents : function()
21135     {
21136 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21137         
21138         this.inputEl().on('click', this.onClick,  this);
21139         
21140         if (this.boxLabel) { 
21141             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21142         }
21143         
21144         this.startValue = this.getValue();
21145         
21146         if(this.groupId){
21147             Roo.bootstrap.CheckBox.register(this);
21148         }
21149     },
21150     
21151     onClick : function(e)
21152     {   
21153         if(this.fireEvent('click', this, e) !== false){
21154             this.setChecked(!this.checked);
21155         }
21156         
21157     },
21158     
21159     setChecked : function(state,suppressEvent)
21160     {
21161         this.startValue = this.getValue();
21162
21163         if(this.inputType == 'radio'){
21164             
21165             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21166                 e.dom.checked = false;
21167             });
21168             
21169             this.inputEl().dom.checked = true;
21170             
21171             this.inputEl().dom.value = this.inputValue;
21172             
21173             if(suppressEvent !== true){
21174                 this.fireEvent('check', this, true);
21175             }
21176             
21177             this.validate();
21178             
21179             return;
21180         }
21181         
21182         this.checked = state;
21183         
21184         this.inputEl().dom.checked = state;
21185         
21186         
21187         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21188         
21189         if(suppressEvent !== true){
21190             this.fireEvent('check', this, state);
21191         }
21192         
21193         this.validate();
21194     },
21195     
21196     getValue : function()
21197     {
21198         if(this.inputType == 'radio'){
21199             return this.getGroupValue();
21200         }
21201         
21202         return this.hiddenEl().dom.value;
21203         
21204     },
21205     
21206     getGroupValue : function()
21207     {
21208         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21209             return '';
21210         }
21211         
21212         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21213     },
21214     
21215     setValue : function(v,suppressEvent)
21216     {
21217         if(this.inputType == 'radio'){
21218             this.setGroupValue(v, suppressEvent);
21219             return;
21220         }
21221         
21222         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21223         
21224         this.validate();
21225     },
21226     
21227     setGroupValue : function(v, suppressEvent)
21228     {
21229         this.startValue = this.getValue();
21230         
21231         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21232             e.dom.checked = false;
21233             
21234             if(e.dom.value == v){
21235                 e.dom.checked = true;
21236             }
21237         });
21238         
21239         if(suppressEvent !== true){
21240             this.fireEvent('check', this, true);
21241         }
21242
21243         this.validate();
21244         
21245         return;
21246     },
21247     
21248     validate : function()
21249     {
21250         if(this.getVisibilityEl().hasClass('hidden')){
21251             return true;
21252         }
21253         
21254         if(
21255                 this.disabled || 
21256                 (this.inputType == 'radio' && this.validateRadio()) ||
21257                 (this.inputType == 'checkbox' && this.validateCheckbox())
21258         ){
21259             this.markValid();
21260             return true;
21261         }
21262         
21263         this.markInvalid();
21264         return false;
21265     },
21266     
21267     validateRadio : function()
21268     {
21269         if(this.getVisibilityEl().hasClass('hidden')){
21270             return true;
21271         }
21272         
21273         if(this.allowBlank){
21274             return true;
21275         }
21276         
21277         var valid = false;
21278         
21279         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21280             if(!e.dom.checked){
21281                 return;
21282             }
21283             
21284             valid = true;
21285             
21286             return false;
21287         });
21288         
21289         return valid;
21290     },
21291     
21292     validateCheckbox : function()
21293     {
21294         if(!this.groupId){
21295             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21296             //return (this.getValue() == this.inputValue) ? true : false;
21297         }
21298         
21299         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21300         
21301         if(!group){
21302             return false;
21303         }
21304         
21305         var r = false;
21306         
21307         for(var i in group){
21308             if(group[i].el.isVisible(true)){
21309                 r = false;
21310                 break;
21311             }
21312             
21313             r = true;
21314         }
21315         
21316         for(var i in group){
21317             if(r){
21318                 break;
21319             }
21320             
21321             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21322         }
21323         
21324         return r;
21325     },
21326     
21327     /**
21328      * Mark this field as valid
21329      */
21330     markValid : function()
21331     {
21332         var _this = this;
21333         
21334         this.fireEvent('valid', this);
21335         
21336         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21337         
21338         if(this.groupId){
21339             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21340         }
21341         
21342         if(label){
21343             label.markValid();
21344         }
21345
21346         if(this.inputType == 'radio'){
21347             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21348                 var fg = e.findParent('.form-group', false, true);
21349                 if (Roo.bootstrap.version == 3) {
21350                     fg.removeClass([_this.invalidClass, _this.validClass]);
21351                     fg.addClass(_this.validClass);
21352                 } else {
21353                     fg.removeClass(['is-valid', 'is-invalid']);
21354                     fg.addClass('is-valid');
21355                 }
21356             });
21357             
21358             return;
21359         }
21360
21361         if(!this.groupId){
21362             var fg = this.el.findParent('.form-group', false, true);
21363             if (Roo.bootstrap.version == 3) {
21364                 fg.removeClass([this.invalidClass, this.validClass]);
21365                 fg.addClass(this.validClass);
21366             } else {
21367                 fg.removeClass(['is-valid', 'is-invalid']);
21368                 fg.addClass('is-valid');
21369             }
21370             return;
21371         }
21372         
21373         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21374         
21375         if(!group){
21376             return;
21377         }
21378         
21379         for(var i in group){
21380             var fg = group[i].el.findParent('.form-group', false, true);
21381             if (Roo.bootstrap.version == 3) {
21382                 fg.removeClass([this.invalidClass, this.validClass]);
21383                 fg.addClass(this.validClass);
21384             } else {
21385                 fg.removeClass(['is-valid', 'is-invalid']);
21386                 fg.addClass('is-valid');
21387             }
21388         }
21389     },
21390     
21391      /**
21392      * Mark this field as invalid
21393      * @param {String} msg The validation message
21394      */
21395     markInvalid : function(msg)
21396     {
21397         if(this.allowBlank){
21398             return;
21399         }
21400         
21401         var _this = this;
21402         
21403         this.fireEvent('invalid', this, msg);
21404         
21405         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21406         
21407         if(this.groupId){
21408             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21409         }
21410         
21411         if(label){
21412             label.markInvalid();
21413         }
21414             
21415         if(this.inputType == 'radio'){
21416             
21417             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21418                 var fg = e.findParent('.form-group', false, true);
21419                 if (Roo.bootstrap.version == 3) {
21420                     fg.removeClass([_this.invalidClass, _this.validClass]);
21421                     fg.addClass(_this.invalidClass);
21422                 } else {
21423                     fg.removeClass(['is-invalid', 'is-valid']);
21424                     fg.addClass('is-invalid');
21425                 }
21426             });
21427             
21428             return;
21429         }
21430         
21431         if(!this.groupId){
21432             var fg = this.el.findParent('.form-group', false, true);
21433             if (Roo.bootstrap.version == 3) {
21434                 fg.removeClass([_this.invalidClass, _this.validClass]);
21435                 fg.addClass(_this.invalidClass);
21436             } else {
21437                 fg.removeClass(['is-invalid', 'is-valid']);
21438                 fg.addClass('is-invalid');
21439             }
21440             return;
21441         }
21442         
21443         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21444         
21445         if(!group){
21446             return;
21447         }
21448         
21449         for(var i in group){
21450             var fg = group[i].el.findParent('.form-group', false, true);
21451             if (Roo.bootstrap.version == 3) {
21452                 fg.removeClass([_this.invalidClass, _this.validClass]);
21453                 fg.addClass(_this.invalidClass);
21454             } else {
21455                 fg.removeClass(['is-invalid', 'is-valid']);
21456                 fg.addClass('is-invalid');
21457             }
21458         }
21459         
21460     },
21461     
21462     clearInvalid : function()
21463     {
21464         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21465         
21466         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21467         
21468         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21469         
21470         if (label && label.iconEl) {
21471             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21472             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21473         }
21474     },
21475     
21476     disable : function()
21477     {
21478         if(this.inputType != 'radio'){
21479             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21480             return;
21481         }
21482         
21483         var _this = this;
21484         
21485         if(this.rendered){
21486             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21487                 _this.getActionEl().addClass(this.disabledClass);
21488                 e.dom.disabled = true;
21489             });
21490         }
21491         
21492         this.disabled = true;
21493         this.fireEvent("disable", this);
21494         return this;
21495     },
21496
21497     enable : function()
21498     {
21499         if(this.inputType != 'radio'){
21500             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21501             return;
21502         }
21503         
21504         var _this = this;
21505         
21506         if(this.rendered){
21507             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21508                 _this.getActionEl().removeClass(this.disabledClass);
21509                 e.dom.disabled = false;
21510             });
21511         }
21512         
21513         this.disabled = false;
21514         this.fireEvent("enable", this);
21515         return this;
21516     },
21517     
21518     setBoxLabel : function(v)
21519     {
21520         this.boxLabel = v;
21521         
21522         if(this.rendered){
21523             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21524         }
21525     }
21526
21527 });
21528
21529 Roo.apply(Roo.bootstrap.CheckBox, {
21530     
21531     groups: {},
21532     
21533      /**
21534     * register a CheckBox Group
21535     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21536     */
21537     register : function(checkbox)
21538     {
21539         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21540             this.groups[checkbox.groupId] = {};
21541         }
21542         
21543         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21544             return;
21545         }
21546         
21547         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21548         
21549     },
21550     /**
21551     * fetch a CheckBox Group based on the group ID
21552     * @param {string} the group ID
21553     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21554     */
21555     get: function(groupId) {
21556         if (typeof(this.groups[groupId]) == 'undefined') {
21557             return false;
21558         }
21559         
21560         return this.groups[groupId] ;
21561     }
21562     
21563     
21564 });
21565 /*
21566  * - LGPL
21567  *
21568  * RadioItem
21569  * 
21570  */
21571
21572 /**
21573  * @class Roo.bootstrap.Radio
21574  * @extends Roo.bootstrap.Component
21575  * Bootstrap Radio class
21576  * @cfg {String} boxLabel - the label associated
21577  * @cfg {String} value - the value of radio
21578  * 
21579  * @constructor
21580  * Create a new Radio
21581  * @param {Object} config The config object
21582  */
21583 Roo.bootstrap.Radio = function(config){
21584     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21585     
21586 };
21587
21588 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21589     
21590     boxLabel : '',
21591     
21592     value : '',
21593     
21594     getAutoCreate : function()
21595     {
21596         var cfg = {
21597             tag : 'div',
21598             cls : 'form-group radio',
21599             cn : [
21600                 {
21601                     tag : 'label',
21602                     cls : 'box-label',
21603                     html : this.boxLabel
21604                 }
21605             ]
21606         };
21607         
21608         return cfg;
21609     },
21610     
21611     initEvents : function() 
21612     {
21613         this.parent().register(this);
21614         
21615         this.el.on('click', this.onClick, this);
21616         
21617     },
21618     
21619     onClick : function(e)
21620     {
21621         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21622             this.setChecked(true);
21623         }
21624     },
21625     
21626     setChecked : function(state, suppressEvent)
21627     {
21628         this.parent().setValue(this.value, suppressEvent);
21629         
21630     },
21631     
21632     setBoxLabel : function(v)
21633     {
21634         this.boxLabel = v;
21635         
21636         if(this.rendered){
21637             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21638         }
21639     }
21640     
21641 });
21642  
21643
21644  /*
21645  * - LGPL
21646  *
21647  * Input
21648  * 
21649  */
21650
21651 /**
21652  * @class Roo.bootstrap.SecurePass
21653  * @extends Roo.bootstrap.Input
21654  * Bootstrap SecurePass class
21655  *
21656  * 
21657  * @constructor
21658  * Create a new SecurePass
21659  * @param {Object} config The config object
21660  */
21661  
21662 Roo.bootstrap.SecurePass = function (config) {
21663     // these go here, so the translation tool can replace them..
21664     this.errors = {
21665         PwdEmpty: "Please type a password, and then retype it to confirm.",
21666         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21667         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21668         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21669         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21670         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21671         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21672         TooWeak: "Your password is Too Weak."
21673     },
21674     this.meterLabel = "Password strength:";
21675     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21676     this.meterClass = [
21677         "roo-password-meter-tooweak", 
21678         "roo-password-meter-weak", 
21679         "roo-password-meter-medium", 
21680         "roo-password-meter-strong", 
21681         "roo-password-meter-grey"
21682     ];
21683     
21684     this.errors = {};
21685     
21686     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21687 }
21688
21689 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21690     /**
21691      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21692      * {
21693      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21694      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21695      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21696      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21697      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21698      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21699      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21700      * })
21701      */
21702     // private
21703     
21704     meterWidth: 300,
21705     errorMsg :'',    
21706     errors: false,
21707     imageRoot: '/',
21708     /**
21709      * @cfg {String/Object} Label for the strength meter (defaults to
21710      * 'Password strength:')
21711      */
21712     // private
21713     meterLabel: '',
21714     /**
21715      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21716      * ['Weak', 'Medium', 'Strong'])
21717      */
21718     // private    
21719     pwdStrengths: false,    
21720     // private
21721     strength: 0,
21722     // private
21723     _lastPwd: null,
21724     // private
21725     kCapitalLetter: 0,
21726     kSmallLetter: 1,
21727     kDigit: 2,
21728     kPunctuation: 3,
21729     
21730     insecure: false,
21731     // private
21732     initEvents: function ()
21733     {
21734         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21735
21736         if (this.el.is('input[type=password]') && Roo.isSafari) {
21737             this.el.on('keydown', this.SafariOnKeyDown, this);
21738         }
21739
21740         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21741     },
21742     // private
21743     onRender: function (ct, position)
21744     {
21745         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21746         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21747         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21748
21749         this.trigger.createChild({
21750                    cn: [
21751                     {
21752                     //id: 'PwdMeter',
21753                     tag: 'div',
21754                     cls: 'roo-password-meter-grey col-xs-12',
21755                     style: {
21756                         //width: 0,
21757                         //width: this.meterWidth + 'px'                                                
21758                         }
21759                     },
21760                     {                            
21761                          cls: 'roo-password-meter-text'                          
21762                     }
21763                 ]            
21764         });
21765
21766          
21767         if (this.hideTrigger) {
21768             this.trigger.setDisplayed(false);
21769         }
21770         this.setSize(this.width || '', this.height || '');
21771     },
21772     // private
21773     onDestroy: function ()
21774     {
21775         if (this.trigger) {
21776             this.trigger.removeAllListeners();
21777             this.trigger.remove();
21778         }
21779         if (this.wrap) {
21780             this.wrap.remove();
21781         }
21782         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21783     },
21784     // private
21785     checkStrength: function ()
21786     {
21787         var pwd = this.inputEl().getValue();
21788         if (pwd == this._lastPwd) {
21789             return;
21790         }
21791
21792         var strength;
21793         if (this.ClientSideStrongPassword(pwd)) {
21794             strength = 3;
21795         } else if (this.ClientSideMediumPassword(pwd)) {
21796             strength = 2;
21797         } else if (this.ClientSideWeakPassword(pwd)) {
21798             strength = 1;
21799         } else {
21800             strength = 0;
21801         }
21802         
21803         Roo.log('strength1: ' + strength);
21804         
21805         //var pm = this.trigger.child('div/div/div').dom;
21806         var pm = this.trigger.child('div/div');
21807         pm.removeClass(this.meterClass);
21808         pm.addClass(this.meterClass[strength]);
21809                 
21810         
21811         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21812                 
21813         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21814         
21815         this._lastPwd = pwd;
21816     },
21817     reset: function ()
21818     {
21819         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21820         
21821         this._lastPwd = '';
21822         
21823         var pm = this.trigger.child('div/div');
21824         pm.removeClass(this.meterClass);
21825         pm.addClass('roo-password-meter-grey');        
21826         
21827         
21828         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21829         
21830         pt.innerHTML = '';
21831         this.inputEl().dom.type='password';
21832     },
21833     // private
21834     validateValue: function (value)
21835     {
21836         
21837         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21838             return false;
21839         }
21840         if (value.length == 0) {
21841             if (this.allowBlank) {
21842                 this.clearInvalid();
21843                 return true;
21844             }
21845
21846             this.markInvalid(this.errors.PwdEmpty);
21847             this.errorMsg = this.errors.PwdEmpty;
21848             return false;
21849         }
21850         
21851         if(this.insecure){
21852             return true;
21853         }
21854         
21855         if ('[\x21-\x7e]*'.match(value)) {
21856             this.markInvalid(this.errors.PwdBadChar);
21857             this.errorMsg = this.errors.PwdBadChar;
21858             return false;
21859         }
21860         if (value.length < 6) {
21861             this.markInvalid(this.errors.PwdShort);
21862             this.errorMsg = this.errors.PwdShort;
21863             return false;
21864         }
21865         if (value.length > 16) {
21866             this.markInvalid(this.errors.PwdLong);
21867             this.errorMsg = this.errors.PwdLong;
21868             return false;
21869         }
21870         var strength;
21871         if (this.ClientSideStrongPassword(value)) {
21872             strength = 3;
21873         } else if (this.ClientSideMediumPassword(value)) {
21874             strength = 2;
21875         } else if (this.ClientSideWeakPassword(value)) {
21876             strength = 1;
21877         } else {
21878             strength = 0;
21879         }
21880
21881         
21882         if (strength < 2) {
21883             //this.markInvalid(this.errors.TooWeak);
21884             this.errorMsg = this.errors.TooWeak;
21885             //return false;
21886         }
21887         
21888         
21889         console.log('strength2: ' + strength);
21890         
21891         //var pm = this.trigger.child('div/div/div').dom;
21892         
21893         var pm = this.trigger.child('div/div');
21894         pm.removeClass(this.meterClass);
21895         pm.addClass(this.meterClass[strength]);
21896                 
21897         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21898                 
21899         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21900         
21901         this.errorMsg = ''; 
21902         return true;
21903     },
21904     // private
21905     CharacterSetChecks: function (type)
21906     {
21907         this.type = type;
21908         this.fResult = false;
21909     },
21910     // private
21911     isctype: function (character, type)
21912     {
21913         switch (type) {  
21914             case this.kCapitalLetter:
21915                 if (character >= 'A' && character <= 'Z') {
21916                     return true;
21917                 }
21918                 break;
21919             
21920             case this.kSmallLetter:
21921                 if (character >= 'a' && character <= 'z') {
21922                     return true;
21923                 }
21924                 break;
21925             
21926             case this.kDigit:
21927                 if (character >= '0' && character <= '9') {
21928                     return true;
21929                 }
21930                 break;
21931             
21932             case this.kPunctuation:
21933                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21934                     return true;
21935                 }
21936                 break;
21937             
21938             default:
21939                 return false;
21940         }
21941
21942     },
21943     // private
21944     IsLongEnough: function (pwd, size)
21945     {
21946         return !(pwd == null || isNaN(size) || pwd.length < size);
21947     },
21948     // private
21949     SpansEnoughCharacterSets: function (word, nb)
21950     {
21951         if (!this.IsLongEnough(word, nb))
21952         {
21953             return false;
21954         }
21955
21956         var characterSetChecks = new Array(
21957             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21958             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21959         );
21960         
21961         for (var index = 0; index < word.length; ++index) {
21962             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21963                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21964                     characterSetChecks[nCharSet].fResult = true;
21965                     break;
21966                 }
21967             }
21968         }
21969
21970         var nCharSets = 0;
21971         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21972             if (characterSetChecks[nCharSet].fResult) {
21973                 ++nCharSets;
21974             }
21975         }
21976
21977         if (nCharSets < nb) {
21978             return false;
21979         }
21980         return true;
21981     },
21982     // private
21983     ClientSideStrongPassword: function (pwd)
21984     {
21985         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21986     },
21987     // private
21988     ClientSideMediumPassword: function (pwd)
21989     {
21990         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21991     },
21992     // private
21993     ClientSideWeakPassword: function (pwd)
21994     {
21995         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21996     }
21997           
21998 })//<script type="text/javascript">
21999
22000 /*
22001  * Based  Ext JS Library 1.1.1
22002  * Copyright(c) 2006-2007, Ext JS, LLC.
22003  * LGPL
22004  *
22005  */
22006  
22007 /**
22008  * @class Roo.HtmlEditorCore
22009  * @extends Roo.Component
22010  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22011  *
22012  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22013  */
22014
22015 Roo.HtmlEditorCore = function(config){
22016     
22017     
22018     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22019     
22020     
22021     this.addEvents({
22022         /**
22023          * @event initialize
22024          * Fires when the editor is fully initialized (including the iframe)
22025          * @param {Roo.HtmlEditorCore} this
22026          */
22027         initialize: true,
22028         /**
22029          * @event activate
22030          * Fires when the editor is first receives the focus. Any insertion must wait
22031          * until after this event.
22032          * @param {Roo.HtmlEditorCore} this
22033          */
22034         activate: true,
22035          /**
22036          * @event beforesync
22037          * Fires before the textarea is updated with content from the editor iframe. Return false
22038          * to cancel the sync.
22039          * @param {Roo.HtmlEditorCore} this
22040          * @param {String} html
22041          */
22042         beforesync: true,
22043          /**
22044          * @event beforepush
22045          * Fires before the iframe editor is updated with content from the textarea. Return false
22046          * to cancel the push.
22047          * @param {Roo.HtmlEditorCore} this
22048          * @param {String} html
22049          */
22050         beforepush: true,
22051          /**
22052          * @event sync
22053          * Fires when the textarea is updated with content from the editor iframe.
22054          * @param {Roo.HtmlEditorCore} this
22055          * @param {String} html
22056          */
22057         sync: true,
22058          /**
22059          * @event push
22060          * Fires when the iframe editor is updated with content from the textarea.
22061          * @param {Roo.HtmlEditorCore} this
22062          * @param {String} html
22063          */
22064         push: true,
22065         
22066         /**
22067          * @event editorevent
22068          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22069          * @param {Roo.HtmlEditorCore} this
22070          */
22071         editorevent: true
22072         
22073     });
22074     
22075     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22076     
22077     // defaults : white / black...
22078     this.applyBlacklists();
22079     
22080     
22081     
22082 };
22083
22084
22085 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22086
22087
22088      /**
22089      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22090      */
22091     
22092     owner : false,
22093     
22094      /**
22095      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22096      *                        Roo.resizable.
22097      */
22098     resizable : false,
22099      /**
22100      * @cfg {Number} height (in pixels)
22101      */   
22102     height: 300,
22103    /**
22104      * @cfg {Number} width (in pixels)
22105      */   
22106     width: 500,
22107     
22108     /**
22109      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22110      * 
22111      */
22112     stylesheets: false,
22113     
22114     // id of frame..
22115     frameId: false,
22116     
22117     // private properties
22118     validationEvent : false,
22119     deferHeight: true,
22120     initialized : false,
22121     activated : false,
22122     sourceEditMode : false,
22123     onFocus : Roo.emptyFn,
22124     iframePad:3,
22125     hideMode:'offsets',
22126     
22127     clearUp: true,
22128     
22129     // blacklist + whitelisted elements..
22130     black: false,
22131     white: false,
22132      
22133     bodyCls : '',
22134
22135     /**
22136      * Protected method that will not generally be called directly. It
22137      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22138      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22139      */
22140     getDocMarkup : function(){
22141         // body styles..
22142         var st = '';
22143         
22144         // inherit styels from page...?? 
22145         if (this.stylesheets === false) {
22146             
22147             Roo.get(document.head).select('style').each(function(node) {
22148                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22149             });
22150             
22151             Roo.get(document.head).select('link').each(function(node) { 
22152                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22153             });
22154             
22155         } else if (!this.stylesheets.length) {
22156                 // simple..
22157                 st = '<style type="text/css">' +
22158                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22159                    '</style>';
22160         } else { 
22161             st = '<style type="text/css">' +
22162                     this.stylesheets +
22163                 '</style>';
22164         }
22165         
22166         st +=  '<style type="text/css">' +
22167             'IMG { cursor: pointer } ' +
22168         '</style>';
22169
22170         var cls = 'roo-htmleditor-body';
22171         
22172         if(this.bodyCls.length){
22173             cls += ' ' + this.bodyCls;
22174         }
22175         
22176         return '<html><head>' + st  +
22177             //<style type="text/css">' +
22178             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22179             //'</style>' +
22180             ' </head><body class="' +  cls + '"></body></html>';
22181     },
22182
22183     // private
22184     onRender : function(ct, position)
22185     {
22186         var _t = this;
22187         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22188         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22189         
22190         
22191         this.el.dom.style.border = '0 none';
22192         this.el.dom.setAttribute('tabIndex', -1);
22193         this.el.addClass('x-hidden hide');
22194         
22195         
22196         
22197         if(Roo.isIE){ // fix IE 1px bogus margin
22198             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22199         }
22200        
22201         
22202         this.frameId = Roo.id();
22203         
22204          
22205         
22206         var iframe = this.owner.wrap.createChild({
22207             tag: 'iframe',
22208             cls: 'form-control', // bootstrap..
22209             id: this.frameId,
22210             name: this.frameId,
22211             frameBorder : 'no',
22212             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22213         }, this.el
22214         );
22215         
22216         
22217         this.iframe = iframe.dom;
22218
22219          this.assignDocWin();
22220         
22221         this.doc.designMode = 'on';
22222        
22223         this.doc.open();
22224         this.doc.write(this.getDocMarkup());
22225         this.doc.close();
22226
22227         
22228         var task = { // must defer to wait for browser to be ready
22229             run : function(){
22230                 //console.log("run task?" + this.doc.readyState);
22231                 this.assignDocWin();
22232                 if(this.doc.body || this.doc.readyState == 'complete'){
22233                     try {
22234                         this.doc.designMode="on";
22235                     } catch (e) {
22236                         return;
22237                     }
22238                     Roo.TaskMgr.stop(task);
22239                     this.initEditor.defer(10, this);
22240                 }
22241             },
22242             interval : 10,
22243             duration: 10000,
22244             scope: this
22245         };
22246         Roo.TaskMgr.start(task);
22247
22248     },
22249
22250     // private
22251     onResize : function(w, h)
22252     {
22253          Roo.log('resize: ' +w + ',' + h );
22254         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22255         if(!this.iframe){
22256             return;
22257         }
22258         if(typeof w == 'number'){
22259             
22260             this.iframe.style.width = w + 'px';
22261         }
22262         if(typeof h == 'number'){
22263             
22264             this.iframe.style.height = h + 'px';
22265             if(this.doc){
22266                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22267             }
22268         }
22269         
22270     },
22271
22272     /**
22273      * Toggles the editor between standard and source edit mode.
22274      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22275      */
22276     toggleSourceEdit : function(sourceEditMode){
22277         
22278         this.sourceEditMode = sourceEditMode === true;
22279         
22280         if(this.sourceEditMode){
22281  
22282             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22283             
22284         }else{
22285             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22286             //this.iframe.className = '';
22287             this.deferFocus();
22288         }
22289         //this.setSize(this.owner.wrap.getSize());
22290         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22291     },
22292
22293     
22294   
22295
22296     /**
22297      * Protected method that will not generally be called directly. If you need/want
22298      * custom HTML cleanup, this is the method you should override.
22299      * @param {String} html The HTML to be cleaned
22300      * return {String} The cleaned HTML
22301      */
22302     cleanHtml : function(html){
22303         html = String(html);
22304         if(html.length > 5){
22305             if(Roo.isSafari){ // strip safari nonsense
22306                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22307             }
22308         }
22309         if(html == '&nbsp;'){
22310             html = '';
22311         }
22312         return html;
22313     },
22314
22315     /**
22316      * HTML Editor -> Textarea
22317      * Protected method that will not generally be called directly. Syncs the contents
22318      * of the editor iframe with the textarea.
22319      */
22320     syncValue : function(){
22321         if(this.initialized){
22322             var bd = (this.doc.body || this.doc.documentElement);
22323             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22324             var html = bd.innerHTML;
22325             if(Roo.isSafari){
22326                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22327                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22328                 if(m && m[1]){
22329                     html = '<div style="'+m[0]+'">' + html + '</div>';
22330                 }
22331             }
22332             html = this.cleanHtml(html);
22333             // fix up the special chars.. normaly like back quotes in word...
22334             // however we do not want to do this with chinese..
22335             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22336                 var cc = b.charCodeAt();
22337                 if (
22338                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22339                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22340                     (cc >= 0xf900 && cc < 0xfb00 )
22341                 ) {
22342                         return b;
22343                 }
22344                 return "&#"+cc+";" 
22345             });
22346             if(this.owner.fireEvent('beforesync', this, html) !== false){
22347                 this.el.dom.value = html;
22348                 this.owner.fireEvent('sync', this, html);
22349             }
22350         }
22351     },
22352
22353     /**
22354      * Protected method that will not generally be called directly. Pushes the value of the textarea
22355      * into the iframe editor.
22356      */
22357     pushValue : function(){
22358         if(this.initialized){
22359             var v = this.el.dom.value.trim();
22360             
22361 //            if(v.length < 1){
22362 //                v = '&#160;';
22363 //            }
22364             
22365             if(this.owner.fireEvent('beforepush', this, v) !== false){
22366                 var d = (this.doc.body || this.doc.documentElement);
22367                 d.innerHTML = v;
22368                 this.cleanUpPaste();
22369                 this.el.dom.value = d.innerHTML;
22370                 this.owner.fireEvent('push', this, v);
22371             }
22372         }
22373     },
22374
22375     // private
22376     deferFocus : function(){
22377         this.focus.defer(10, this);
22378     },
22379
22380     // doc'ed in Field
22381     focus : function(){
22382         if(this.win && !this.sourceEditMode){
22383             this.win.focus();
22384         }else{
22385             this.el.focus();
22386         }
22387     },
22388     
22389     assignDocWin: function()
22390     {
22391         var iframe = this.iframe;
22392         
22393          if(Roo.isIE){
22394             this.doc = iframe.contentWindow.document;
22395             this.win = iframe.contentWindow;
22396         } else {
22397 //            if (!Roo.get(this.frameId)) {
22398 //                return;
22399 //            }
22400 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22401 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22402             
22403             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22404                 return;
22405             }
22406             
22407             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22408             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22409         }
22410     },
22411     
22412     // private
22413     initEditor : function(){
22414         //console.log("INIT EDITOR");
22415         this.assignDocWin();
22416         
22417         
22418         
22419         this.doc.designMode="on";
22420         this.doc.open();
22421         this.doc.write(this.getDocMarkup());
22422         this.doc.close();
22423         
22424         var dbody = (this.doc.body || this.doc.documentElement);
22425         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22426         // this copies styles from the containing element into thsi one..
22427         // not sure why we need all of this..
22428         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22429         
22430         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22431         //ss['background-attachment'] = 'fixed'; // w3c
22432         dbody.bgProperties = 'fixed'; // ie
22433         //Roo.DomHelper.applyStyles(dbody, ss);
22434         Roo.EventManager.on(this.doc, {
22435             //'mousedown': this.onEditorEvent,
22436             'mouseup': this.onEditorEvent,
22437             'dblclick': this.onEditorEvent,
22438             'click': this.onEditorEvent,
22439             'keyup': this.onEditorEvent,
22440             buffer:100,
22441             scope: this
22442         });
22443         if(Roo.isGecko){
22444             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22445         }
22446         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22447             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22448         }
22449         this.initialized = true;
22450
22451         this.owner.fireEvent('initialize', this);
22452         this.pushValue();
22453     },
22454
22455     // private
22456     onDestroy : function(){
22457         
22458         
22459         
22460         if(this.rendered){
22461             
22462             //for (var i =0; i < this.toolbars.length;i++) {
22463             //    // fixme - ask toolbars for heights?
22464             //    this.toolbars[i].onDestroy();
22465            // }
22466             
22467             //this.wrap.dom.innerHTML = '';
22468             //this.wrap.remove();
22469         }
22470     },
22471
22472     // private
22473     onFirstFocus : function(){
22474         
22475         this.assignDocWin();
22476         
22477         
22478         this.activated = true;
22479          
22480     
22481         if(Roo.isGecko){ // prevent silly gecko errors
22482             this.win.focus();
22483             var s = this.win.getSelection();
22484             if(!s.focusNode || s.focusNode.nodeType != 3){
22485                 var r = s.getRangeAt(0);
22486                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22487                 r.collapse(true);
22488                 this.deferFocus();
22489             }
22490             try{
22491                 this.execCmd('useCSS', true);
22492                 this.execCmd('styleWithCSS', false);
22493             }catch(e){}
22494         }
22495         this.owner.fireEvent('activate', this);
22496     },
22497
22498     // private
22499     adjustFont: function(btn){
22500         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22501         //if(Roo.isSafari){ // safari
22502         //    adjust *= 2;
22503        // }
22504         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22505         if(Roo.isSafari){ // safari
22506             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22507             v =  (v < 10) ? 10 : v;
22508             v =  (v > 48) ? 48 : v;
22509             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22510             
22511         }
22512         
22513         
22514         v = Math.max(1, v+adjust);
22515         
22516         this.execCmd('FontSize', v  );
22517     },
22518
22519     onEditorEvent : function(e)
22520     {
22521         this.owner.fireEvent('editorevent', this, e);
22522       //  this.updateToolbar();
22523         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22524     },
22525
22526     insertTag : function(tg)
22527     {
22528         // could be a bit smarter... -> wrap the current selected tRoo..
22529         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22530             
22531             range = this.createRange(this.getSelection());
22532             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22533             wrappingNode.appendChild(range.extractContents());
22534             range.insertNode(wrappingNode);
22535
22536             return;
22537             
22538             
22539             
22540         }
22541         this.execCmd("formatblock",   tg);
22542         
22543     },
22544     
22545     insertText : function(txt)
22546     {
22547         
22548         
22549         var range = this.createRange();
22550         range.deleteContents();
22551                //alert(Sender.getAttribute('label'));
22552                
22553         range.insertNode(this.doc.createTextNode(txt));
22554     } ,
22555     
22556      
22557
22558     /**
22559      * Executes a Midas editor command on the editor document and performs necessary focus and
22560      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22561      * @param {String} cmd The Midas command
22562      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22563      */
22564     relayCmd : function(cmd, value){
22565         this.win.focus();
22566         this.execCmd(cmd, value);
22567         this.owner.fireEvent('editorevent', this);
22568         //this.updateToolbar();
22569         this.owner.deferFocus();
22570     },
22571
22572     /**
22573      * Executes a Midas editor command directly on the editor document.
22574      * For visual commands, you should use {@link #relayCmd} instead.
22575      * <b>This should only be called after the editor is initialized.</b>
22576      * @param {String} cmd The Midas command
22577      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22578      */
22579     execCmd : function(cmd, value){
22580         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22581         this.syncValue();
22582     },
22583  
22584  
22585    
22586     /**
22587      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22588      * to insert tRoo.
22589      * @param {String} text | dom node.. 
22590      */
22591     insertAtCursor : function(text)
22592     {
22593         
22594         if(!this.activated){
22595             return;
22596         }
22597         /*
22598         if(Roo.isIE){
22599             this.win.focus();
22600             var r = this.doc.selection.createRange();
22601             if(r){
22602                 r.collapse(true);
22603                 r.pasteHTML(text);
22604                 this.syncValue();
22605                 this.deferFocus();
22606             
22607             }
22608             return;
22609         }
22610         */
22611         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22612             this.win.focus();
22613             
22614             
22615             // from jquery ui (MIT licenced)
22616             var range, node;
22617             var win = this.win;
22618             
22619             if (win.getSelection && win.getSelection().getRangeAt) {
22620                 range = win.getSelection().getRangeAt(0);
22621                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22622                 range.insertNode(node);
22623             } else if (win.document.selection && win.document.selection.createRange) {
22624                 // no firefox support
22625                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22626                 win.document.selection.createRange().pasteHTML(txt);
22627             } else {
22628                 // no firefox support
22629                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22630                 this.execCmd('InsertHTML', txt);
22631             } 
22632             
22633             this.syncValue();
22634             
22635             this.deferFocus();
22636         }
22637     },
22638  // private
22639     mozKeyPress : function(e){
22640         if(e.ctrlKey){
22641             var c = e.getCharCode(), cmd;
22642           
22643             if(c > 0){
22644                 c = String.fromCharCode(c).toLowerCase();
22645                 switch(c){
22646                     case 'b':
22647                         cmd = 'bold';
22648                         break;
22649                     case 'i':
22650                         cmd = 'italic';
22651                         break;
22652                     
22653                     case 'u':
22654                         cmd = 'underline';
22655                         break;
22656                     
22657                     case 'v':
22658                         this.cleanUpPaste.defer(100, this);
22659                         return;
22660                         
22661                 }
22662                 if(cmd){
22663                     this.win.focus();
22664                     this.execCmd(cmd);
22665                     this.deferFocus();
22666                     e.preventDefault();
22667                 }
22668                 
22669             }
22670         }
22671     },
22672
22673     // private
22674     fixKeys : function(){ // load time branching for fastest keydown performance
22675         if(Roo.isIE){
22676             return function(e){
22677                 var k = e.getKey(), r;
22678                 if(k == e.TAB){
22679                     e.stopEvent();
22680                     r = this.doc.selection.createRange();
22681                     if(r){
22682                         r.collapse(true);
22683                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22684                         this.deferFocus();
22685                     }
22686                     return;
22687                 }
22688                 
22689                 if(k == e.ENTER){
22690                     r = this.doc.selection.createRange();
22691                     if(r){
22692                         var target = r.parentElement();
22693                         if(!target || target.tagName.toLowerCase() != 'li'){
22694                             e.stopEvent();
22695                             r.pasteHTML('<br />');
22696                             r.collapse(false);
22697                             r.select();
22698                         }
22699                     }
22700                 }
22701                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22702                     this.cleanUpPaste.defer(100, this);
22703                     return;
22704                 }
22705                 
22706                 
22707             };
22708         }else if(Roo.isOpera){
22709             return function(e){
22710                 var k = e.getKey();
22711                 if(k == e.TAB){
22712                     e.stopEvent();
22713                     this.win.focus();
22714                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22715                     this.deferFocus();
22716                 }
22717                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22718                     this.cleanUpPaste.defer(100, this);
22719                     return;
22720                 }
22721                 
22722             };
22723         }else if(Roo.isSafari){
22724             return function(e){
22725                 var k = e.getKey();
22726                 
22727                 if(k == e.TAB){
22728                     e.stopEvent();
22729                     this.execCmd('InsertText','\t');
22730                     this.deferFocus();
22731                     return;
22732                 }
22733                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22734                     this.cleanUpPaste.defer(100, this);
22735                     return;
22736                 }
22737                 
22738              };
22739         }
22740     }(),
22741     
22742     getAllAncestors: function()
22743     {
22744         var p = this.getSelectedNode();
22745         var a = [];
22746         if (!p) {
22747             a.push(p); // push blank onto stack..
22748             p = this.getParentElement();
22749         }
22750         
22751         
22752         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22753             a.push(p);
22754             p = p.parentNode;
22755         }
22756         a.push(this.doc.body);
22757         return a;
22758     },
22759     lastSel : false,
22760     lastSelNode : false,
22761     
22762     
22763     getSelection : function() 
22764     {
22765         this.assignDocWin();
22766         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22767     },
22768     
22769     getSelectedNode: function() 
22770     {
22771         // this may only work on Gecko!!!
22772         
22773         // should we cache this!!!!
22774         
22775         
22776         
22777          
22778         var range = this.createRange(this.getSelection()).cloneRange();
22779         
22780         if (Roo.isIE) {
22781             var parent = range.parentElement();
22782             while (true) {
22783                 var testRange = range.duplicate();
22784                 testRange.moveToElementText(parent);
22785                 if (testRange.inRange(range)) {
22786                     break;
22787                 }
22788                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22789                     break;
22790                 }
22791                 parent = parent.parentElement;
22792             }
22793             return parent;
22794         }
22795         
22796         // is ancestor a text element.
22797         var ac =  range.commonAncestorContainer;
22798         if (ac.nodeType == 3) {
22799             ac = ac.parentNode;
22800         }
22801         
22802         var ar = ac.childNodes;
22803          
22804         var nodes = [];
22805         var other_nodes = [];
22806         var has_other_nodes = false;
22807         for (var i=0;i<ar.length;i++) {
22808             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22809                 continue;
22810             }
22811             // fullly contained node.
22812             
22813             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22814                 nodes.push(ar[i]);
22815                 continue;
22816             }
22817             
22818             // probably selected..
22819             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22820                 other_nodes.push(ar[i]);
22821                 continue;
22822             }
22823             // outer..
22824             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22825                 continue;
22826             }
22827             
22828             
22829             has_other_nodes = true;
22830         }
22831         if (!nodes.length && other_nodes.length) {
22832             nodes= other_nodes;
22833         }
22834         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22835             return false;
22836         }
22837         
22838         return nodes[0];
22839     },
22840     createRange: function(sel)
22841     {
22842         // this has strange effects when using with 
22843         // top toolbar - not sure if it's a great idea.
22844         //this.editor.contentWindow.focus();
22845         if (typeof sel != "undefined") {
22846             try {
22847                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22848             } catch(e) {
22849                 return this.doc.createRange();
22850             }
22851         } else {
22852             return this.doc.createRange();
22853         }
22854     },
22855     getParentElement: function()
22856     {
22857         
22858         this.assignDocWin();
22859         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22860         
22861         var range = this.createRange(sel);
22862          
22863         try {
22864             var p = range.commonAncestorContainer;
22865             while (p.nodeType == 3) { // text node
22866                 p = p.parentNode;
22867             }
22868             return p;
22869         } catch (e) {
22870             return null;
22871         }
22872     
22873     },
22874     /***
22875      *
22876      * Range intersection.. the hard stuff...
22877      *  '-1' = before
22878      *  '0' = hits..
22879      *  '1' = after.
22880      *         [ -- selected range --- ]
22881      *   [fail]                        [fail]
22882      *
22883      *    basically..
22884      *      if end is before start or  hits it. fail.
22885      *      if start is after end or hits it fail.
22886      *
22887      *   if either hits (but other is outside. - then it's not 
22888      *   
22889      *    
22890      **/
22891     
22892     
22893     // @see http://www.thismuchiknow.co.uk/?p=64.
22894     rangeIntersectsNode : function(range, node)
22895     {
22896         var nodeRange = node.ownerDocument.createRange();
22897         try {
22898             nodeRange.selectNode(node);
22899         } catch (e) {
22900             nodeRange.selectNodeContents(node);
22901         }
22902     
22903         var rangeStartRange = range.cloneRange();
22904         rangeStartRange.collapse(true);
22905     
22906         var rangeEndRange = range.cloneRange();
22907         rangeEndRange.collapse(false);
22908     
22909         var nodeStartRange = nodeRange.cloneRange();
22910         nodeStartRange.collapse(true);
22911     
22912         var nodeEndRange = nodeRange.cloneRange();
22913         nodeEndRange.collapse(false);
22914     
22915         return rangeStartRange.compareBoundaryPoints(
22916                  Range.START_TO_START, nodeEndRange) == -1 &&
22917                rangeEndRange.compareBoundaryPoints(
22918                  Range.START_TO_START, nodeStartRange) == 1;
22919         
22920          
22921     },
22922     rangeCompareNode : function(range, node)
22923     {
22924         var nodeRange = node.ownerDocument.createRange();
22925         try {
22926             nodeRange.selectNode(node);
22927         } catch (e) {
22928             nodeRange.selectNodeContents(node);
22929         }
22930         
22931         
22932         range.collapse(true);
22933     
22934         nodeRange.collapse(true);
22935      
22936         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22937         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22938          
22939         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22940         
22941         var nodeIsBefore   =  ss == 1;
22942         var nodeIsAfter    = ee == -1;
22943         
22944         if (nodeIsBefore && nodeIsAfter) {
22945             return 0; // outer
22946         }
22947         if (!nodeIsBefore && nodeIsAfter) {
22948             return 1; //right trailed.
22949         }
22950         
22951         if (nodeIsBefore && !nodeIsAfter) {
22952             return 2;  // left trailed.
22953         }
22954         // fully contined.
22955         return 3;
22956     },
22957
22958     // private? - in a new class?
22959     cleanUpPaste :  function()
22960     {
22961         // cleans up the whole document..
22962         Roo.log('cleanuppaste');
22963         
22964         this.cleanUpChildren(this.doc.body);
22965         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22966         if (clean != this.doc.body.innerHTML) {
22967             this.doc.body.innerHTML = clean;
22968         }
22969         
22970     },
22971     
22972     cleanWordChars : function(input) {// change the chars to hex code
22973         var he = Roo.HtmlEditorCore;
22974         
22975         var output = input;
22976         Roo.each(he.swapCodes, function(sw) { 
22977             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22978             
22979             output = output.replace(swapper, sw[1]);
22980         });
22981         
22982         return output;
22983     },
22984     
22985     
22986     cleanUpChildren : function (n)
22987     {
22988         if (!n.childNodes.length) {
22989             return;
22990         }
22991         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22992            this.cleanUpChild(n.childNodes[i]);
22993         }
22994     },
22995     
22996     
22997         
22998     
22999     cleanUpChild : function (node)
23000     {
23001         var ed = this;
23002         //console.log(node);
23003         if (node.nodeName == "#text") {
23004             // clean up silly Windows -- stuff?
23005             return; 
23006         }
23007         if (node.nodeName == "#comment") {
23008             node.parentNode.removeChild(node);
23009             // clean up silly Windows -- stuff?
23010             return; 
23011         }
23012         var lcname = node.tagName.toLowerCase();
23013         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23014         // whitelist of tags..
23015         
23016         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23017             // remove node.
23018             node.parentNode.removeChild(node);
23019             return;
23020             
23021         }
23022         
23023         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23024         
23025         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23026         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23027         
23028         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23029         //    remove_keep_children = true;
23030         //}
23031         
23032         if (remove_keep_children) {
23033             this.cleanUpChildren(node);
23034             // inserts everything just before this node...
23035             while (node.childNodes.length) {
23036                 var cn = node.childNodes[0];
23037                 node.removeChild(cn);
23038                 node.parentNode.insertBefore(cn, node);
23039             }
23040             node.parentNode.removeChild(node);
23041             return;
23042         }
23043         
23044         if (!node.attributes || !node.attributes.length) {
23045             this.cleanUpChildren(node);
23046             return;
23047         }
23048         
23049         function cleanAttr(n,v)
23050         {
23051             
23052             if (v.match(/^\./) || v.match(/^\//)) {
23053                 return;
23054             }
23055             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23056                 return;
23057             }
23058             if (v.match(/^#/)) {
23059                 return;
23060             }
23061 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23062             node.removeAttribute(n);
23063             
23064         }
23065         
23066         var cwhite = this.cwhite;
23067         var cblack = this.cblack;
23068             
23069         function cleanStyle(n,v)
23070         {
23071             if (v.match(/expression/)) { //XSS?? should we even bother..
23072                 node.removeAttribute(n);
23073                 return;
23074             }
23075             
23076             var parts = v.split(/;/);
23077             var clean = [];
23078             
23079             Roo.each(parts, function(p) {
23080                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23081                 if (!p.length) {
23082                     return true;
23083                 }
23084                 var l = p.split(':').shift().replace(/\s+/g,'');
23085                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23086                 
23087                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23088 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23089                     //node.removeAttribute(n);
23090                     return true;
23091                 }
23092                 //Roo.log()
23093                 // only allow 'c whitelisted system attributes'
23094                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23095 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23096                     //node.removeAttribute(n);
23097                     return true;
23098                 }
23099                 
23100                 
23101                  
23102                 
23103                 clean.push(p);
23104                 return true;
23105             });
23106             if (clean.length) { 
23107                 node.setAttribute(n, clean.join(';'));
23108             } else {
23109                 node.removeAttribute(n);
23110             }
23111             
23112         }
23113         
23114         
23115         for (var i = node.attributes.length-1; i > -1 ; i--) {
23116             var a = node.attributes[i];
23117             //console.log(a);
23118             
23119             if (a.name.toLowerCase().substr(0,2)=='on')  {
23120                 node.removeAttribute(a.name);
23121                 continue;
23122             }
23123             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23124                 node.removeAttribute(a.name);
23125                 continue;
23126             }
23127             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23128                 cleanAttr(a.name,a.value); // fixme..
23129                 continue;
23130             }
23131             if (a.name == 'style') {
23132                 cleanStyle(a.name,a.value);
23133                 continue;
23134             }
23135             /// clean up MS crap..
23136             // tecnically this should be a list of valid class'es..
23137             
23138             
23139             if (a.name == 'class') {
23140                 if (a.value.match(/^Mso/)) {
23141                     node.className = '';
23142                 }
23143                 
23144                 if (a.value.match(/^body$/)) {
23145                     node.className = '';
23146                 }
23147                 continue;
23148             }
23149             
23150             // style cleanup!?
23151             // class cleanup?
23152             
23153         }
23154         
23155         
23156         this.cleanUpChildren(node);
23157         
23158         
23159     },
23160     
23161     /**
23162      * Clean up MS wordisms...
23163      */
23164     cleanWord : function(node)
23165     {
23166         
23167         
23168         if (!node) {
23169             this.cleanWord(this.doc.body);
23170             return;
23171         }
23172         if (node.nodeName == "#text") {
23173             // clean up silly Windows -- stuff?
23174             return; 
23175         }
23176         if (node.nodeName == "#comment") {
23177             node.parentNode.removeChild(node);
23178             // clean up silly Windows -- stuff?
23179             return; 
23180         }
23181         
23182         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23183             node.parentNode.removeChild(node);
23184             return;
23185         }
23186         
23187         // remove - but keep children..
23188         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23189             while (node.childNodes.length) {
23190                 var cn = node.childNodes[0];
23191                 node.removeChild(cn);
23192                 node.parentNode.insertBefore(cn, node);
23193             }
23194             node.parentNode.removeChild(node);
23195             this.iterateChildren(node, this.cleanWord);
23196             return;
23197         }
23198         // clean styles
23199         if (node.className.length) {
23200             
23201             var cn = node.className.split(/\W+/);
23202             var cna = [];
23203             Roo.each(cn, function(cls) {
23204                 if (cls.match(/Mso[a-zA-Z]+/)) {
23205                     return;
23206                 }
23207                 cna.push(cls);
23208             });
23209             node.className = cna.length ? cna.join(' ') : '';
23210             if (!cna.length) {
23211                 node.removeAttribute("class");
23212             }
23213         }
23214         
23215         if (node.hasAttribute("lang")) {
23216             node.removeAttribute("lang");
23217         }
23218         
23219         if (node.hasAttribute("style")) {
23220             
23221             var styles = node.getAttribute("style").split(";");
23222             var nstyle = [];
23223             Roo.each(styles, function(s) {
23224                 if (!s.match(/:/)) {
23225                     return;
23226                 }
23227                 var kv = s.split(":");
23228                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23229                     return;
23230                 }
23231                 // what ever is left... we allow.
23232                 nstyle.push(s);
23233             });
23234             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23235             if (!nstyle.length) {
23236                 node.removeAttribute('style');
23237             }
23238         }
23239         this.iterateChildren(node, this.cleanWord);
23240         
23241         
23242         
23243     },
23244     /**
23245      * iterateChildren of a Node, calling fn each time, using this as the scole..
23246      * @param {DomNode} node node to iterate children of.
23247      * @param {Function} fn method of this class to call on each item.
23248      */
23249     iterateChildren : function(node, fn)
23250     {
23251         if (!node.childNodes.length) {
23252                 return;
23253         }
23254         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23255            fn.call(this, node.childNodes[i])
23256         }
23257     },
23258     
23259     
23260     /**
23261      * cleanTableWidths.
23262      *
23263      * Quite often pasting from word etc.. results in tables with column and widths.
23264      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23265      *
23266      */
23267     cleanTableWidths : function(node)
23268     {
23269          
23270          
23271         if (!node) {
23272             this.cleanTableWidths(this.doc.body);
23273             return;
23274         }
23275         
23276         // ignore list...
23277         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23278             return; 
23279         }
23280         Roo.log(node.tagName);
23281         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23282             this.iterateChildren(node, this.cleanTableWidths);
23283             return;
23284         }
23285         if (node.hasAttribute('width')) {
23286             node.removeAttribute('width');
23287         }
23288         
23289          
23290         if (node.hasAttribute("style")) {
23291             // pretty basic...
23292             
23293             var styles = node.getAttribute("style").split(";");
23294             var nstyle = [];
23295             Roo.each(styles, function(s) {
23296                 if (!s.match(/:/)) {
23297                     return;
23298                 }
23299                 var kv = s.split(":");
23300                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23301                     return;
23302                 }
23303                 // what ever is left... we allow.
23304                 nstyle.push(s);
23305             });
23306             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23307             if (!nstyle.length) {
23308                 node.removeAttribute('style');
23309             }
23310         }
23311         
23312         this.iterateChildren(node, this.cleanTableWidths);
23313         
23314         
23315     },
23316     
23317     
23318     
23319     
23320     domToHTML : function(currentElement, depth, nopadtext) {
23321         
23322         depth = depth || 0;
23323         nopadtext = nopadtext || false;
23324     
23325         if (!currentElement) {
23326             return this.domToHTML(this.doc.body);
23327         }
23328         
23329         //Roo.log(currentElement);
23330         var j;
23331         var allText = false;
23332         var nodeName = currentElement.nodeName;
23333         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23334         
23335         if  (nodeName == '#text') {
23336             
23337             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23338         }
23339         
23340         
23341         var ret = '';
23342         if (nodeName != 'BODY') {
23343              
23344             var i = 0;
23345             // Prints the node tagName, such as <A>, <IMG>, etc
23346             if (tagName) {
23347                 var attr = [];
23348                 for(i = 0; i < currentElement.attributes.length;i++) {
23349                     // quoting?
23350                     var aname = currentElement.attributes.item(i).name;
23351                     if (!currentElement.attributes.item(i).value.length) {
23352                         continue;
23353                     }
23354                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23355                 }
23356                 
23357                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23358             } 
23359             else {
23360                 
23361                 // eack
23362             }
23363         } else {
23364             tagName = false;
23365         }
23366         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23367             return ret;
23368         }
23369         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23370             nopadtext = true;
23371         }
23372         
23373         
23374         // Traverse the tree
23375         i = 0;
23376         var currentElementChild = currentElement.childNodes.item(i);
23377         var allText = true;
23378         var innerHTML  = '';
23379         lastnode = '';
23380         while (currentElementChild) {
23381             // Formatting code (indent the tree so it looks nice on the screen)
23382             var nopad = nopadtext;
23383             if (lastnode == 'SPAN') {
23384                 nopad  = true;
23385             }
23386             // text
23387             if  (currentElementChild.nodeName == '#text') {
23388                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23389                 toadd = nopadtext ? toadd : toadd.trim();
23390                 if (!nopad && toadd.length > 80) {
23391                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23392                 }
23393                 innerHTML  += toadd;
23394                 
23395                 i++;
23396                 currentElementChild = currentElement.childNodes.item(i);
23397                 lastNode = '';
23398                 continue;
23399             }
23400             allText = false;
23401             
23402             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23403                 
23404             // Recursively traverse the tree structure of the child node
23405             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23406             lastnode = currentElementChild.nodeName;
23407             i++;
23408             currentElementChild=currentElement.childNodes.item(i);
23409         }
23410         
23411         ret += innerHTML;
23412         
23413         if (!allText) {
23414                 // The remaining code is mostly for formatting the tree
23415             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23416         }
23417         
23418         
23419         if (tagName) {
23420             ret+= "</"+tagName+">";
23421         }
23422         return ret;
23423         
23424     },
23425         
23426     applyBlacklists : function()
23427     {
23428         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23429         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23430         
23431         this.white = [];
23432         this.black = [];
23433         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23434             if (b.indexOf(tag) > -1) {
23435                 return;
23436             }
23437             this.white.push(tag);
23438             
23439         }, this);
23440         
23441         Roo.each(w, function(tag) {
23442             if (b.indexOf(tag) > -1) {
23443                 return;
23444             }
23445             if (this.white.indexOf(tag) > -1) {
23446                 return;
23447             }
23448             this.white.push(tag);
23449             
23450         }, this);
23451         
23452         
23453         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23454             if (w.indexOf(tag) > -1) {
23455                 return;
23456             }
23457             this.black.push(tag);
23458             
23459         }, this);
23460         
23461         Roo.each(b, function(tag) {
23462             if (w.indexOf(tag) > -1) {
23463                 return;
23464             }
23465             if (this.black.indexOf(tag) > -1) {
23466                 return;
23467             }
23468             this.black.push(tag);
23469             
23470         }, this);
23471         
23472         
23473         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23474         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23475         
23476         this.cwhite = [];
23477         this.cblack = [];
23478         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23479             if (b.indexOf(tag) > -1) {
23480                 return;
23481             }
23482             this.cwhite.push(tag);
23483             
23484         }, this);
23485         
23486         Roo.each(w, function(tag) {
23487             if (b.indexOf(tag) > -1) {
23488                 return;
23489             }
23490             if (this.cwhite.indexOf(tag) > -1) {
23491                 return;
23492             }
23493             this.cwhite.push(tag);
23494             
23495         }, this);
23496         
23497         
23498         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23499             if (w.indexOf(tag) > -1) {
23500                 return;
23501             }
23502             this.cblack.push(tag);
23503             
23504         }, this);
23505         
23506         Roo.each(b, function(tag) {
23507             if (w.indexOf(tag) > -1) {
23508                 return;
23509             }
23510             if (this.cblack.indexOf(tag) > -1) {
23511                 return;
23512             }
23513             this.cblack.push(tag);
23514             
23515         }, this);
23516     },
23517     
23518     setStylesheets : function(stylesheets)
23519     {
23520         if(typeof(stylesheets) == 'string'){
23521             Roo.get(this.iframe.contentDocument.head).createChild({
23522                 tag : 'link',
23523                 rel : 'stylesheet',
23524                 type : 'text/css',
23525                 href : stylesheets
23526             });
23527             
23528             return;
23529         }
23530         var _this = this;
23531      
23532         Roo.each(stylesheets, function(s) {
23533             if(!s.length){
23534                 return;
23535             }
23536             
23537             Roo.get(_this.iframe.contentDocument.head).createChild({
23538                 tag : 'link',
23539                 rel : 'stylesheet',
23540                 type : 'text/css',
23541                 href : s
23542             });
23543         });
23544
23545         
23546     },
23547     
23548     removeStylesheets : function()
23549     {
23550         var _this = this;
23551         
23552         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23553             s.remove();
23554         });
23555     },
23556     
23557     setStyle : function(style)
23558     {
23559         Roo.get(this.iframe.contentDocument.head).createChild({
23560             tag : 'style',
23561             type : 'text/css',
23562             html : style
23563         });
23564
23565         return;
23566     }
23567     
23568     // hide stuff that is not compatible
23569     /**
23570      * @event blur
23571      * @hide
23572      */
23573     /**
23574      * @event change
23575      * @hide
23576      */
23577     /**
23578      * @event focus
23579      * @hide
23580      */
23581     /**
23582      * @event specialkey
23583      * @hide
23584      */
23585     /**
23586      * @cfg {String} fieldClass @hide
23587      */
23588     /**
23589      * @cfg {String} focusClass @hide
23590      */
23591     /**
23592      * @cfg {String} autoCreate @hide
23593      */
23594     /**
23595      * @cfg {String} inputType @hide
23596      */
23597     /**
23598      * @cfg {String} invalidClass @hide
23599      */
23600     /**
23601      * @cfg {String} invalidText @hide
23602      */
23603     /**
23604      * @cfg {String} msgFx @hide
23605      */
23606     /**
23607      * @cfg {String} validateOnBlur @hide
23608      */
23609 });
23610
23611 Roo.HtmlEditorCore.white = [
23612         'area', 'br', 'img', 'input', 'hr', 'wbr',
23613         
23614        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23615        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23616        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23617        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23618        'table',   'ul',         'xmp', 
23619        
23620        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23621       'thead',   'tr', 
23622      
23623       'dir', 'menu', 'ol', 'ul', 'dl',
23624        
23625       'embed',  'object'
23626 ];
23627
23628
23629 Roo.HtmlEditorCore.black = [
23630     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23631         'applet', // 
23632         'base',   'basefont', 'bgsound', 'blink',  'body', 
23633         'frame',  'frameset', 'head',    'html',   'ilayer', 
23634         'iframe', 'layer',  'link',     'meta',    'object',   
23635         'script', 'style' ,'title',  'xml' // clean later..
23636 ];
23637 Roo.HtmlEditorCore.clean = [
23638     'script', 'style', 'title', 'xml'
23639 ];
23640 Roo.HtmlEditorCore.remove = [
23641     'font'
23642 ];
23643 // attributes..
23644
23645 Roo.HtmlEditorCore.ablack = [
23646     'on'
23647 ];
23648     
23649 Roo.HtmlEditorCore.aclean = [ 
23650     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23651 ];
23652
23653 // protocols..
23654 Roo.HtmlEditorCore.pwhite= [
23655         'http',  'https',  'mailto'
23656 ];
23657
23658 // white listed style attributes.
23659 Roo.HtmlEditorCore.cwhite= [
23660       //  'text-align', /// default is to allow most things..
23661       
23662          
23663 //        'font-size'//??
23664 ];
23665
23666 // black listed style attributes.
23667 Roo.HtmlEditorCore.cblack= [
23668       //  'font-size' -- this can be set by the project 
23669 ];
23670
23671
23672 Roo.HtmlEditorCore.swapCodes   =[ 
23673     [    8211, "--" ], 
23674     [    8212, "--" ], 
23675     [    8216,  "'" ],  
23676     [    8217, "'" ],  
23677     [    8220, '"' ],  
23678     [    8221, '"' ],  
23679     [    8226, "*" ],  
23680     [    8230, "..." ]
23681 ]; 
23682
23683     /*
23684  * - LGPL
23685  *
23686  * HtmlEditor
23687  * 
23688  */
23689
23690 /**
23691  * @class Roo.bootstrap.HtmlEditor
23692  * @extends Roo.bootstrap.TextArea
23693  * Bootstrap HtmlEditor class
23694
23695  * @constructor
23696  * Create a new HtmlEditor
23697  * @param {Object} config The config object
23698  */
23699
23700 Roo.bootstrap.HtmlEditor = function(config){
23701     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23702     if (!this.toolbars) {
23703         this.toolbars = [];
23704     }
23705     
23706     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23707     this.addEvents({
23708             /**
23709              * @event initialize
23710              * Fires when the editor is fully initialized (including the iframe)
23711              * @param {HtmlEditor} this
23712              */
23713             initialize: true,
23714             /**
23715              * @event activate
23716              * Fires when the editor is first receives the focus. Any insertion must wait
23717              * until after this event.
23718              * @param {HtmlEditor} this
23719              */
23720             activate: true,
23721              /**
23722              * @event beforesync
23723              * Fires before the textarea is updated with content from the editor iframe. Return false
23724              * to cancel the sync.
23725              * @param {HtmlEditor} this
23726              * @param {String} html
23727              */
23728             beforesync: true,
23729              /**
23730              * @event beforepush
23731              * Fires before the iframe editor is updated with content from the textarea. Return false
23732              * to cancel the push.
23733              * @param {HtmlEditor} this
23734              * @param {String} html
23735              */
23736             beforepush: true,
23737              /**
23738              * @event sync
23739              * Fires when the textarea is updated with content from the editor iframe.
23740              * @param {HtmlEditor} this
23741              * @param {String} html
23742              */
23743             sync: true,
23744              /**
23745              * @event push
23746              * Fires when the iframe editor is updated with content from the textarea.
23747              * @param {HtmlEditor} this
23748              * @param {String} html
23749              */
23750             push: true,
23751              /**
23752              * @event editmodechange
23753              * Fires when the editor switches edit modes
23754              * @param {HtmlEditor} this
23755              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23756              */
23757             editmodechange: true,
23758             /**
23759              * @event editorevent
23760              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23761              * @param {HtmlEditor} this
23762              */
23763             editorevent: true,
23764             /**
23765              * @event firstfocus
23766              * Fires when on first focus - needed by toolbars..
23767              * @param {HtmlEditor} this
23768              */
23769             firstfocus: true,
23770             /**
23771              * @event autosave
23772              * Auto save the htmlEditor value as a file into Events
23773              * @param {HtmlEditor} this
23774              */
23775             autosave: true,
23776             /**
23777              * @event savedpreview
23778              * preview the saved version of htmlEditor
23779              * @param {HtmlEditor} this
23780              */
23781             savedpreview: true
23782         });
23783 };
23784
23785
23786 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23787     
23788     
23789       /**
23790      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23791      */
23792     toolbars : false,
23793     
23794      /**
23795     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23796     */
23797     btns : [],
23798    
23799      /**
23800      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23801      *                        Roo.resizable.
23802      */
23803     resizable : false,
23804      /**
23805      * @cfg {Number} height (in pixels)
23806      */   
23807     height: 300,
23808    /**
23809      * @cfg {Number} width (in pixels)
23810      */   
23811     width: false,
23812     
23813     /**
23814      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23815      * 
23816      */
23817     stylesheets: false,
23818     
23819     // id of frame..
23820     frameId: false,
23821     
23822     // private properties
23823     validationEvent : false,
23824     deferHeight: true,
23825     initialized : false,
23826     activated : false,
23827     
23828     onFocus : Roo.emptyFn,
23829     iframePad:3,
23830     hideMode:'offsets',
23831     
23832     tbContainer : false,
23833     
23834     bodyCls : '',
23835     
23836     toolbarContainer :function() {
23837         return this.wrap.select('.x-html-editor-tb',true).first();
23838     },
23839
23840     /**
23841      * Protected method that will not generally be called directly. It
23842      * is called when the editor creates its toolbar. Override this method if you need to
23843      * add custom toolbar buttons.
23844      * @param {HtmlEditor} editor
23845      */
23846     createToolbar : function(){
23847         Roo.log('renewing');
23848         Roo.log("create toolbars");
23849         
23850         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23851         this.toolbars[0].render(this.toolbarContainer());
23852         
23853         return;
23854         
23855 //        if (!editor.toolbars || !editor.toolbars.length) {
23856 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23857 //        }
23858 //        
23859 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23860 //            editor.toolbars[i] = Roo.factory(
23861 //                    typeof(editor.toolbars[i]) == 'string' ?
23862 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23863 //                Roo.bootstrap.HtmlEditor);
23864 //            editor.toolbars[i].init(editor);
23865 //        }
23866     },
23867
23868      
23869     // private
23870     onRender : function(ct, position)
23871     {
23872        // Roo.log("Call onRender: " + this.xtype);
23873         var _t = this;
23874         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23875       
23876         this.wrap = this.inputEl().wrap({
23877             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23878         });
23879         
23880         this.editorcore.onRender(ct, position);
23881          
23882         if (this.resizable) {
23883             this.resizeEl = new Roo.Resizable(this.wrap, {
23884                 pinned : true,
23885                 wrap: true,
23886                 dynamic : true,
23887                 minHeight : this.height,
23888                 height: this.height,
23889                 handles : this.resizable,
23890                 width: this.width,
23891                 listeners : {
23892                     resize : function(r, w, h) {
23893                         _t.onResize(w,h); // -something
23894                     }
23895                 }
23896             });
23897             
23898         }
23899         this.createToolbar(this);
23900        
23901         
23902         if(!this.width && this.resizable){
23903             this.setSize(this.wrap.getSize());
23904         }
23905         if (this.resizeEl) {
23906             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23907             // should trigger onReize..
23908         }
23909         
23910     },
23911
23912     // private
23913     onResize : function(w, h)
23914     {
23915         Roo.log('resize: ' +w + ',' + h );
23916         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23917         var ew = false;
23918         var eh = false;
23919         
23920         if(this.inputEl() ){
23921             if(typeof w == 'number'){
23922                 var aw = w - this.wrap.getFrameWidth('lr');
23923                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23924                 ew = aw;
23925             }
23926             if(typeof h == 'number'){
23927                  var tbh = -11;  // fixme it needs to tool bar size!
23928                 for (var i =0; i < this.toolbars.length;i++) {
23929                     // fixme - ask toolbars for heights?
23930                     tbh += this.toolbars[i].el.getHeight();
23931                     //if (this.toolbars[i].footer) {
23932                     //    tbh += this.toolbars[i].footer.el.getHeight();
23933                     //}
23934                 }
23935               
23936                 
23937                 
23938                 
23939                 
23940                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23941                 ah -= 5; // knock a few pixes off for look..
23942                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23943                 var eh = ah;
23944             }
23945         }
23946         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23947         this.editorcore.onResize(ew,eh);
23948         
23949     },
23950
23951     /**
23952      * Toggles the editor between standard and source edit mode.
23953      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23954      */
23955     toggleSourceEdit : function(sourceEditMode)
23956     {
23957         this.editorcore.toggleSourceEdit(sourceEditMode);
23958         
23959         if(this.editorcore.sourceEditMode){
23960             Roo.log('editor - showing textarea');
23961             
23962 //            Roo.log('in');
23963 //            Roo.log(this.syncValue());
23964             this.syncValue();
23965             this.inputEl().removeClass(['hide', 'x-hidden']);
23966             this.inputEl().dom.removeAttribute('tabIndex');
23967             this.inputEl().focus();
23968         }else{
23969             Roo.log('editor - hiding textarea');
23970 //            Roo.log('out')
23971 //            Roo.log(this.pushValue()); 
23972             this.pushValue();
23973             
23974             this.inputEl().addClass(['hide', 'x-hidden']);
23975             this.inputEl().dom.setAttribute('tabIndex', -1);
23976             //this.deferFocus();
23977         }
23978          
23979         if(this.resizable){
23980             this.setSize(this.wrap.getSize());
23981         }
23982         
23983         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23984     },
23985  
23986     // private (for BoxComponent)
23987     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23988
23989     // private (for BoxComponent)
23990     getResizeEl : function(){
23991         return this.wrap;
23992     },
23993
23994     // private (for BoxComponent)
23995     getPositionEl : function(){
23996         return this.wrap;
23997     },
23998
23999     // private
24000     initEvents : function(){
24001         this.originalValue = this.getValue();
24002     },
24003
24004 //    /**
24005 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24006 //     * @method
24007 //     */
24008 //    markInvalid : Roo.emptyFn,
24009 //    /**
24010 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24011 //     * @method
24012 //     */
24013 //    clearInvalid : Roo.emptyFn,
24014
24015     setValue : function(v){
24016         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24017         this.editorcore.pushValue();
24018     },
24019
24020      
24021     // private
24022     deferFocus : function(){
24023         this.focus.defer(10, this);
24024     },
24025
24026     // doc'ed in Field
24027     focus : function(){
24028         this.editorcore.focus();
24029         
24030     },
24031       
24032
24033     // private
24034     onDestroy : function(){
24035         
24036         
24037         
24038         if(this.rendered){
24039             
24040             for (var i =0; i < this.toolbars.length;i++) {
24041                 // fixme - ask toolbars for heights?
24042                 this.toolbars[i].onDestroy();
24043             }
24044             
24045             this.wrap.dom.innerHTML = '';
24046             this.wrap.remove();
24047         }
24048     },
24049
24050     // private
24051     onFirstFocus : function(){
24052         //Roo.log("onFirstFocus");
24053         this.editorcore.onFirstFocus();
24054          for (var i =0; i < this.toolbars.length;i++) {
24055             this.toolbars[i].onFirstFocus();
24056         }
24057         
24058     },
24059     
24060     // private
24061     syncValue : function()
24062     {   
24063         this.editorcore.syncValue();
24064     },
24065     
24066     pushValue : function()
24067     {   
24068         this.editorcore.pushValue();
24069     }
24070      
24071     
24072     // hide stuff that is not compatible
24073     /**
24074      * @event blur
24075      * @hide
24076      */
24077     /**
24078      * @event change
24079      * @hide
24080      */
24081     /**
24082      * @event focus
24083      * @hide
24084      */
24085     /**
24086      * @event specialkey
24087      * @hide
24088      */
24089     /**
24090      * @cfg {String} fieldClass @hide
24091      */
24092     /**
24093      * @cfg {String} focusClass @hide
24094      */
24095     /**
24096      * @cfg {String} autoCreate @hide
24097      */
24098     /**
24099      * @cfg {String} inputType @hide
24100      */
24101      
24102     /**
24103      * @cfg {String} invalidText @hide
24104      */
24105     /**
24106      * @cfg {String} msgFx @hide
24107      */
24108     /**
24109      * @cfg {String} validateOnBlur @hide
24110      */
24111 });
24112  
24113     
24114    
24115    
24116    
24117       
24118 Roo.namespace('Roo.bootstrap.htmleditor');
24119 /**
24120  * @class Roo.bootstrap.HtmlEditorToolbar1
24121  * Basic Toolbar
24122  * 
24123  * @example
24124  * Usage:
24125  *
24126  new Roo.bootstrap.HtmlEditor({
24127     ....
24128     toolbars : [
24129         new Roo.bootstrap.HtmlEditorToolbar1({
24130             disable : { fonts: 1 , format: 1, ..., ... , ...],
24131             btns : [ .... ]
24132         })
24133     }
24134      
24135  * 
24136  * @cfg {Object} disable List of elements to disable..
24137  * @cfg {Array} btns List of additional buttons.
24138  * 
24139  * 
24140  * NEEDS Extra CSS? 
24141  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24142  */
24143  
24144 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24145 {
24146     
24147     Roo.apply(this, config);
24148     
24149     // default disabled, based on 'good practice'..
24150     this.disable = this.disable || {};
24151     Roo.applyIf(this.disable, {
24152         fontSize : true,
24153         colors : true,
24154         specialElements : true
24155     });
24156     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24157     
24158     this.editor = config.editor;
24159     this.editorcore = config.editor.editorcore;
24160     
24161     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24162     
24163     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24164     // dont call parent... till later.
24165 }
24166 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24167      
24168     bar : true,
24169     
24170     editor : false,
24171     editorcore : false,
24172     
24173     
24174     formats : [
24175         "p" ,  
24176         "h1","h2","h3","h4","h5","h6", 
24177         "pre", "code", 
24178         "abbr", "acronym", "address", "cite", "samp", "var",
24179         'div','span'
24180     ],
24181     
24182     onRender : function(ct, position)
24183     {
24184        // Roo.log("Call onRender: " + this.xtype);
24185         
24186        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24187        Roo.log(this.el);
24188        this.el.dom.style.marginBottom = '0';
24189        var _this = this;
24190        var editorcore = this.editorcore;
24191        var editor= this.editor;
24192        
24193        var children = [];
24194        var btn = function(id,cmd , toggle, handler, html){
24195        
24196             var  event = toggle ? 'toggle' : 'click';
24197        
24198             var a = {
24199                 size : 'sm',
24200                 xtype: 'Button',
24201                 xns: Roo.bootstrap,
24202                 //glyphicon : id,
24203                 fa: id,
24204                 cmd : id || cmd,
24205                 enableToggle:toggle !== false,
24206                 html : html || '',
24207                 pressed : toggle ? false : null,
24208                 listeners : {}
24209             };
24210             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24211                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24212             };
24213             children.push(a);
24214             return a;
24215        }
24216        
24217     //    var cb_box = function...
24218         
24219         var style = {
24220                 xtype: 'Button',
24221                 size : 'sm',
24222                 xns: Roo.bootstrap,
24223                 fa : 'font',
24224                 //html : 'submit'
24225                 menu : {
24226                     xtype: 'Menu',
24227                     xns: Roo.bootstrap,
24228                     items:  []
24229                 }
24230         };
24231         Roo.each(this.formats, function(f) {
24232             style.menu.items.push({
24233                 xtype :'MenuItem',
24234                 xns: Roo.bootstrap,
24235                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24236                 tagname : f,
24237                 listeners : {
24238                     click : function()
24239                     {
24240                         editorcore.insertTag(this.tagname);
24241                         editor.focus();
24242                     }
24243                 }
24244                 
24245             });
24246         });
24247         children.push(style);   
24248         
24249         btn('bold',false,true);
24250         btn('italic',false,true);
24251         btn('align-left', 'justifyleft',true);
24252         btn('align-center', 'justifycenter',true);
24253         btn('align-right' , 'justifyright',true);
24254         btn('link', false, false, function(btn) {
24255             //Roo.log("create link?");
24256             var url = prompt(this.createLinkText, this.defaultLinkValue);
24257             if(url && url != 'http:/'+'/'){
24258                 this.editorcore.relayCmd('createlink', url);
24259             }
24260         }),
24261         btn('list','insertunorderedlist',true);
24262         btn('pencil', false,true, function(btn){
24263                 Roo.log(this);
24264                 this.toggleSourceEdit(btn.pressed);
24265         });
24266         
24267         if (this.editor.btns.length > 0) {
24268             for (var i = 0; i<this.editor.btns.length; i++) {
24269                 children.push(this.editor.btns[i]);
24270             }
24271         }
24272         
24273         /*
24274         var cog = {
24275                 xtype: 'Button',
24276                 size : 'sm',
24277                 xns: Roo.bootstrap,
24278                 glyphicon : 'cog',
24279                 //html : 'submit'
24280                 menu : {
24281                     xtype: 'Menu',
24282                     xns: Roo.bootstrap,
24283                     items:  []
24284                 }
24285         };
24286         
24287         cog.menu.items.push({
24288             xtype :'MenuItem',
24289             xns: Roo.bootstrap,
24290             html : Clean styles,
24291             tagname : f,
24292             listeners : {
24293                 click : function()
24294                 {
24295                     editorcore.insertTag(this.tagname);
24296                     editor.focus();
24297                 }
24298             }
24299             
24300         });
24301        */
24302         
24303          
24304        this.xtype = 'NavSimplebar';
24305         
24306         for(var i=0;i< children.length;i++) {
24307             
24308             this.buttons.add(this.addxtypeChild(children[i]));
24309             
24310         }
24311         
24312         editor.on('editorevent', this.updateToolbar, this);
24313     },
24314     onBtnClick : function(id)
24315     {
24316        this.editorcore.relayCmd(id);
24317        this.editorcore.focus();
24318     },
24319     
24320     /**
24321      * Protected method that will not generally be called directly. It triggers
24322      * a toolbar update by reading the markup state of the current selection in the editor.
24323      */
24324     updateToolbar: function(){
24325
24326         if(!this.editorcore.activated){
24327             this.editor.onFirstFocus(); // is this neeed?
24328             return;
24329         }
24330
24331         var btns = this.buttons; 
24332         var doc = this.editorcore.doc;
24333         btns.get('bold').setActive(doc.queryCommandState('bold'));
24334         btns.get('italic').setActive(doc.queryCommandState('italic'));
24335         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24336         
24337         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24338         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24339         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24340         
24341         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24342         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24343          /*
24344         
24345         var ans = this.editorcore.getAllAncestors();
24346         if (this.formatCombo) {
24347             
24348             
24349             var store = this.formatCombo.store;
24350             this.formatCombo.setValue("");
24351             for (var i =0; i < ans.length;i++) {
24352                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24353                     // select it..
24354                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24355                     break;
24356                 }
24357             }
24358         }
24359         
24360         
24361         
24362         // hides menus... - so this cant be on a menu...
24363         Roo.bootstrap.MenuMgr.hideAll();
24364         */
24365         Roo.bootstrap.MenuMgr.hideAll();
24366         //this.editorsyncValue();
24367     },
24368     onFirstFocus: function() {
24369         this.buttons.each(function(item){
24370            item.enable();
24371         });
24372     },
24373     toggleSourceEdit : function(sourceEditMode){
24374         
24375           
24376         if(sourceEditMode){
24377             Roo.log("disabling buttons");
24378            this.buttons.each( function(item){
24379                 if(item.cmd != 'pencil'){
24380                     item.disable();
24381                 }
24382             });
24383           
24384         }else{
24385             Roo.log("enabling buttons");
24386             if(this.editorcore.initialized){
24387                 this.buttons.each( function(item){
24388                     item.enable();
24389                 });
24390             }
24391             
24392         }
24393         Roo.log("calling toggole on editor");
24394         // tell the editor that it's been pressed..
24395         this.editor.toggleSourceEdit(sourceEditMode);
24396        
24397     }
24398 });
24399
24400
24401
24402
24403
24404 /**
24405  * @class Roo.bootstrap.Table.AbstractSelectionModel
24406  * @extends Roo.util.Observable
24407  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24408  * implemented by descendant classes.  This class should not be directly instantiated.
24409  * @constructor
24410  */
24411 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24412     this.locked = false;
24413     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24414 };
24415
24416
24417 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24418     /** @ignore Called by the grid automatically. Do not call directly. */
24419     init : function(grid){
24420         this.grid = grid;
24421         this.initEvents();
24422     },
24423
24424     /**
24425      * Locks the selections.
24426      */
24427     lock : function(){
24428         this.locked = true;
24429     },
24430
24431     /**
24432      * Unlocks the selections.
24433      */
24434     unlock : function(){
24435         this.locked = false;
24436     },
24437
24438     /**
24439      * Returns true if the selections are locked.
24440      * @return {Boolean}
24441      */
24442     isLocked : function(){
24443         return this.locked;
24444     }
24445 });
24446 /**
24447  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24448  * @class Roo.bootstrap.Table.RowSelectionModel
24449  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24450  * It supports multiple selections and keyboard selection/navigation. 
24451  * @constructor
24452  * @param {Object} config
24453  */
24454
24455 Roo.bootstrap.Table.RowSelectionModel = function(config){
24456     Roo.apply(this, config);
24457     this.selections = new Roo.util.MixedCollection(false, function(o){
24458         return o.id;
24459     });
24460
24461     this.last = false;
24462     this.lastActive = false;
24463
24464     this.addEvents({
24465         /**
24466              * @event selectionchange
24467              * Fires when the selection changes
24468              * @param {SelectionModel} this
24469              */
24470             "selectionchange" : true,
24471         /**
24472              * @event afterselectionchange
24473              * Fires after the selection changes (eg. by key press or clicking)
24474              * @param {SelectionModel} this
24475              */
24476             "afterselectionchange" : true,
24477         /**
24478              * @event beforerowselect
24479              * Fires when a row is selected being selected, return false to cancel.
24480              * @param {SelectionModel} this
24481              * @param {Number} rowIndex The selected index
24482              * @param {Boolean} keepExisting False if other selections will be cleared
24483              */
24484             "beforerowselect" : true,
24485         /**
24486              * @event rowselect
24487              * Fires when a row is selected.
24488              * @param {SelectionModel} this
24489              * @param {Number} rowIndex The selected index
24490              * @param {Roo.data.Record} r The record
24491              */
24492             "rowselect" : true,
24493         /**
24494              * @event rowdeselect
24495              * Fires when a row is deselected.
24496              * @param {SelectionModel} this
24497              * @param {Number} rowIndex The selected index
24498              */
24499         "rowdeselect" : true
24500     });
24501     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24502     this.locked = false;
24503  };
24504
24505 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24506     /**
24507      * @cfg {Boolean} singleSelect
24508      * True to allow selection of only one row at a time (defaults to false)
24509      */
24510     singleSelect : false,
24511
24512     // private
24513     initEvents : function()
24514     {
24515
24516         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24517         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24518         //}else{ // allow click to work like normal
24519          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24520         //}
24521         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24522         this.grid.on("rowclick", this.handleMouseDown, this);
24523         
24524         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24525             "up" : function(e){
24526                 if(!e.shiftKey){
24527                     this.selectPrevious(e.shiftKey);
24528                 }else if(this.last !== false && this.lastActive !== false){
24529                     var last = this.last;
24530                     this.selectRange(this.last,  this.lastActive-1);
24531                     this.grid.getView().focusRow(this.lastActive);
24532                     if(last !== false){
24533                         this.last = last;
24534                     }
24535                 }else{
24536                     this.selectFirstRow();
24537                 }
24538                 this.fireEvent("afterselectionchange", this);
24539             },
24540             "down" : function(e){
24541                 if(!e.shiftKey){
24542                     this.selectNext(e.shiftKey);
24543                 }else if(this.last !== false && this.lastActive !== false){
24544                     var last = this.last;
24545                     this.selectRange(this.last,  this.lastActive+1);
24546                     this.grid.getView().focusRow(this.lastActive);
24547                     if(last !== false){
24548                         this.last = last;
24549                     }
24550                 }else{
24551                     this.selectFirstRow();
24552                 }
24553                 this.fireEvent("afterselectionchange", this);
24554             },
24555             scope: this
24556         });
24557         this.grid.store.on('load', function(){
24558             this.selections.clear();
24559         },this);
24560         /*
24561         var view = this.grid.view;
24562         view.on("refresh", this.onRefresh, this);
24563         view.on("rowupdated", this.onRowUpdated, this);
24564         view.on("rowremoved", this.onRemove, this);
24565         */
24566     },
24567
24568     // private
24569     onRefresh : function()
24570     {
24571         var ds = this.grid.store, i, v = this.grid.view;
24572         var s = this.selections;
24573         s.each(function(r){
24574             if((i = ds.indexOfId(r.id)) != -1){
24575                 v.onRowSelect(i);
24576             }else{
24577                 s.remove(r);
24578             }
24579         });
24580     },
24581
24582     // private
24583     onRemove : function(v, index, r){
24584         this.selections.remove(r);
24585     },
24586
24587     // private
24588     onRowUpdated : function(v, index, r){
24589         if(this.isSelected(r)){
24590             v.onRowSelect(index);
24591         }
24592     },
24593
24594     /**
24595      * Select records.
24596      * @param {Array} records The records to select
24597      * @param {Boolean} keepExisting (optional) True to keep existing selections
24598      */
24599     selectRecords : function(records, keepExisting)
24600     {
24601         if(!keepExisting){
24602             this.clearSelections();
24603         }
24604             var ds = this.grid.store;
24605         for(var i = 0, len = records.length; i < len; i++){
24606             this.selectRow(ds.indexOf(records[i]), true);
24607         }
24608     },
24609
24610     /**
24611      * Gets the number of selected rows.
24612      * @return {Number}
24613      */
24614     getCount : function(){
24615         return this.selections.length;
24616     },
24617
24618     /**
24619      * Selects the first row in the grid.
24620      */
24621     selectFirstRow : function(){
24622         this.selectRow(0);
24623     },
24624
24625     /**
24626      * Select the last row.
24627      * @param {Boolean} keepExisting (optional) True to keep existing selections
24628      */
24629     selectLastRow : function(keepExisting){
24630         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24631         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24632     },
24633
24634     /**
24635      * Selects the row immediately following the last selected row.
24636      * @param {Boolean} keepExisting (optional) True to keep existing selections
24637      */
24638     selectNext : function(keepExisting)
24639     {
24640             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24641             this.selectRow(this.last+1, keepExisting);
24642             this.grid.getView().focusRow(this.last);
24643         }
24644     },
24645
24646     /**
24647      * Selects the row that precedes the last selected row.
24648      * @param {Boolean} keepExisting (optional) True to keep existing selections
24649      */
24650     selectPrevious : function(keepExisting){
24651         if(this.last){
24652             this.selectRow(this.last-1, keepExisting);
24653             this.grid.getView().focusRow(this.last);
24654         }
24655     },
24656
24657     /**
24658      * Returns the selected records
24659      * @return {Array} Array of selected records
24660      */
24661     getSelections : function(){
24662         return [].concat(this.selections.items);
24663     },
24664
24665     /**
24666      * Returns the first selected record.
24667      * @return {Record}
24668      */
24669     getSelected : function(){
24670         return this.selections.itemAt(0);
24671     },
24672
24673
24674     /**
24675      * Clears all selections.
24676      */
24677     clearSelections : function(fast)
24678     {
24679         if(this.locked) {
24680             return;
24681         }
24682         if(fast !== true){
24683                 var ds = this.grid.store;
24684             var s = this.selections;
24685             s.each(function(r){
24686                 this.deselectRow(ds.indexOfId(r.id));
24687             }, this);
24688             s.clear();
24689         }else{
24690             this.selections.clear();
24691         }
24692         this.last = false;
24693     },
24694
24695
24696     /**
24697      * Selects all rows.
24698      */
24699     selectAll : function(){
24700         if(this.locked) {
24701             return;
24702         }
24703         this.selections.clear();
24704         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24705             this.selectRow(i, true);
24706         }
24707     },
24708
24709     /**
24710      * Returns True if there is a selection.
24711      * @return {Boolean}
24712      */
24713     hasSelection : function(){
24714         return this.selections.length > 0;
24715     },
24716
24717     /**
24718      * Returns True if the specified row is selected.
24719      * @param {Number/Record} record The record or index of the record to check
24720      * @return {Boolean}
24721      */
24722     isSelected : function(index){
24723             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24724         return (r && this.selections.key(r.id) ? true : false);
24725     },
24726
24727     /**
24728      * Returns True if the specified record id is selected.
24729      * @param {String} id The id of record to check
24730      * @return {Boolean}
24731      */
24732     isIdSelected : function(id){
24733         return (this.selections.key(id) ? true : false);
24734     },
24735
24736
24737     // private
24738     handleMouseDBClick : function(e, t){
24739         
24740     },
24741     // private
24742     handleMouseDown : function(e, t)
24743     {
24744             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24745         if(this.isLocked() || rowIndex < 0 ){
24746             return;
24747         };
24748         if(e.shiftKey && this.last !== false){
24749             var last = this.last;
24750             this.selectRange(last, rowIndex, e.ctrlKey);
24751             this.last = last; // reset the last
24752             t.focus();
24753     
24754         }else{
24755             var isSelected = this.isSelected(rowIndex);
24756             //Roo.log("select row:" + rowIndex);
24757             if(isSelected){
24758                 this.deselectRow(rowIndex);
24759             } else {
24760                         this.selectRow(rowIndex, true);
24761             }
24762     
24763             /*
24764                 if(e.button !== 0 && isSelected){
24765                 alert('rowIndex 2: ' + rowIndex);
24766                     view.focusRow(rowIndex);
24767                 }else if(e.ctrlKey && isSelected){
24768                     this.deselectRow(rowIndex);
24769                 }else if(!isSelected){
24770                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24771                     view.focusRow(rowIndex);
24772                 }
24773             */
24774         }
24775         this.fireEvent("afterselectionchange", this);
24776     },
24777     // private
24778     handleDragableRowClick :  function(grid, rowIndex, e) 
24779     {
24780         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24781             this.selectRow(rowIndex, false);
24782             grid.view.focusRow(rowIndex);
24783              this.fireEvent("afterselectionchange", this);
24784         }
24785     },
24786     
24787     /**
24788      * Selects multiple rows.
24789      * @param {Array} rows Array of the indexes of the row to select
24790      * @param {Boolean} keepExisting (optional) True to keep existing selections
24791      */
24792     selectRows : function(rows, keepExisting){
24793         if(!keepExisting){
24794             this.clearSelections();
24795         }
24796         for(var i = 0, len = rows.length; i < len; i++){
24797             this.selectRow(rows[i], true);
24798         }
24799     },
24800
24801     /**
24802      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24803      * @param {Number} startRow The index of the first row in the range
24804      * @param {Number} endRow The index of the last row in the range
24805      * @param {Boolean} keepExisting (optional) True to retain existing selections
24806      */
24807     selectRange : function(startRow, endRow, keepExisting){
24808         if(this.locked) {
24809             return;
24810         }
24811         if(!keepExisting){
24812             this.clearSelections();
24813         }
24814         if(startRow <= endRow){
24815             for(var i = startRow; i <= endRow; i++){
24816                 this.selectRow(i, true);
24817             }
24818         }else{
24819             for(var i = startRow; i >= endRow; i--){
24820                 this.selectRow(i, true);
24821             }
24822         }
24823     },
24824
24825     /**
24826      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24827      * @param {Number} startRow The index of the first row in the range
24828      * @param {Number} endRow The index of the last row in the range
24829      */
24830     deselectRange : function(startRow, endRow, preventViewNotify){
24831         if(this.locked) {
24832             return;
24833         }
24834         for(var i = startRow; i <= endRow; i++){
24835             this.deselectRow(i, preventViewNotify);
24836         }
24837     },
24838
24839     /**
24840      * Selects a row.
24841      * @param {Number} row The index of the row to select
24842      * @param {Boolean} keepExisting (optional) True to keep existing selections
24843      */
24844     selectRow : function(index, keepExisting, preventViewNotify)
24845     {
24846             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24847             return;
24848         }
24849         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24850             if(!keepExisting || this.singleSelect){
24851                 this.clearSelections();
24852             }
24853             
24854             var r = this.grid.store.getAt(index);
24855             //console.log('selectRow - record id :' + r.id);
24856             
24857             this.selections.add(r);
24858             this.last = this.lastActive = index;
24859             if(!preventViewNotify){
24860                 var proxy = new Roo.Element(
24861                                 this.grid.getRowDom(index)
24862                 );
24863                 proxy.addClass('bg-info info');
24864             }
24865             this.fireEvent("rowselect", this, index, r);
24866             this.fireEvent("selectionchange", this);
24867         }
24868     },
24869
24870     /**
24871      * Deselects a row.
24872      * @param {Number} row The index of the row to deselect
24873      */
24874     deselectRow : function(index, preventViewNotify)
24875     {
24876         if(this.locked) {
24877             return;
24878         }
24879         if(this.last == index){
24880             this.last = false;
24881         }
24882         if(this.lastActive == index){
24883             this.lastActive = false;
24884         }
24885         
24886         var r = this.grid.store.getAt(index);
24887         if (!r) {
24888             return;
24889         }
24890         
24891         this.selections.remove(r);
24892         //.console.log('deselectRow - record id :' + r.id);
24893         if(!preventViewNotify){
24894         
24895             var proxy = new Roo.Element(
24896                 this.grid.getRowDom(index)
24897             );
24898             proxy.removeClass('bg-info info');
24899         }
24900         this.fireEvent("rowdeselect", this, index);
24901         this.fireEvent("selectionchange", this);
24902     },
24903
24904     // private
24905     restoreLast : function(){
24906         if(this._last){
24907             this.last = this._last;
24908         }
24909     },
24910
24911     // private
24912     acceptsNav : function(row, col, cm){
24913         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24914     },
24915
24916     // private
24917     onEditorKey : function(field, e){
24918         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24919         if(k == e.TAB){
24920             e.stopEvent();
24921             ed.completeEdit();
24922             if(e.shiftKey){
24923                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24924             }else{
24925                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24926             }
24927         }else if(k == e.ENTER && !e.ctrlKey){
24928             e.stopEvent();
24929             ed.completeEdit();
24930             if(e.shiftKey){
24931                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24932             }else{
24933                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24934             }
24935         }else if(k == e.ESC){
24936             ed.cancelEdit();
24937         }
24938         if(newCell){
24939             g.startEditing(newCell[0], newCell[1]);
24940         }
24941     }
24942 });
24943 /*
24944  * Based on:
24945  * Ext JS Library 1.1.1
24946  * Copyright(c) 2006-2007, Ext JS, LLC.
24947  *
24948  * Originally Released Under LGPL - original licence link has changed is not relivant.
24949  *
24950  * Fork - LGPL
24951  * <script type="text/javascript">
24952  */
24953  
24954 /**
24955  * @class Roo.bootstrap.PagingToolbar
24956  * @extends Roo.bootstrap.NavSimplebar
24957  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24958  * @constructor
24959  * Create a new PagingToolbar
24960  * @param {Object} config The config object
24961  * @param {Roo.data.Store} store
24962  */
24963 Roo.bootstrap.PagingToolbar = function(config)
24964 {
24965     // old args format still supported... - xtype is prefered..
24966         // created from xtype...
24967     
24968     this.ds = config.dataSource;
24969     
24970     if (config.store && !this.ds) {
24971         this.store= Roo.factory(config.store, Roo.data);
24972         this.ds = this.store;
24973         this.ds.xmodule = this.xmodule || false;
24974     }
24975     
24976     this.toolbarItems = [];
24977     if (config.items) {
24978         this.toolbarItems = config.items;
24979     }
24980     
24981     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24982     
24983     this.cursor = 0;
24984     
24985     if (this.ds) { 
24986         this.bind(this.ds);
24987     }
24988     
24989     if (Roo.bootstrap.version == 4) {
24990         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24991     } else {
24992         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24993     }
24994     
24995 };
24996
24997 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24998     /**
24999      * @cfg {Roo.data.Store} dataSource
25000      * The underlying data store providing the paged data
25001      */
25002     /**
25003      * @cfg {String/HTMLElement/Element} container
25004      * container The id or element that will contain the toolbar
25005      */
25006     /**
25007      * @cfg {Boolean} displayInfo
25008      * True to display the displayMsg (defaults to false)
25009      */
25010     /**
25011      * @cfg {Number} pageSize
25012      * The number of records to display per page (defaults to 20)
25013      */
25014     pageSize: 20,
25015     /**
25016      * @cfg {String} displayMsg
25017      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25018      */
25019     displayMsg : 'Displaying {0} - {1} of {2}',
25020     /**
25021      * @cfg {String} emptyMsg
25022      * The message to display when no records are found (defaults to "No data to display")
25023      */
25024     emptyMsg : 'No data to display',
25025     /**
25026      * Customizable piece of the default paging text (defaults to "Page")
25027      * @type String
25028      */
25029     beforePageText : "Page",
25030     /**
25031      * Customizable piece of the default paging text (defaults to "of %0")
25032      * @type String
25033      */
25034     afterPageText : "of {0}",
25035     /**
25036      * Customizable piece of the default paging text (defaults to "First Page")
25037      * @type String
25038      */
25039     firstText : "First Page",
25040     /**
25041      * Customizable piece of the default paging text (defaults to "Previous Page")
25042      * @type String
25043      */
25044     prevText : "Previous Page",
25045     /**
25046      * Customizable piece of the default paging text (defaults to "Next Page")
25047      * @type String
25048      */
25049     nextText : "Next Page",
25050     /**
25051      * Customizable piece of the default paging text (defaults to "Last Page")
25052      * @type String
25053      */
25054     lastText : "Last Page",
25055     /**
25056      * Customizable piece of the default paging text (defaults to "Refresh")
25057      * @type String
25058      */
25059     refreshText : "Refresh",
25060
25061     buttons : false,
25062     // private
25063     onRender : function(ct, position) 
25064     {
25065         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25066         this.navgroup.parentId = this.id;
25067         this.navgroup.onRender(this.el, null);
25068         // add the buttons to the navgroup
25069         
25070         if(this.displayInfo){
25071             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25072             this.displayEl = this.el.select('.x-paging-info', true).first();
25073 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25074 //            this.displayEl = navel.el.select('span',true).first();
25075         }
25076         
25077         var _this = this;
25078         
25079         if(this.buttons){
25080             Roo.each(_this.buttons, function(e){ // this might need to use render????
25081                Roo.factory(e).render(_this.el);
25082             });
25083         }
25084             
25085         Roo.each(_this.toolbarItems, function(e) {
25086             _this.navgroup.addItem(e);
25087         });
25088         
25089         
25090         this.first = this.navgroup.addItem({
25091             tooltip: this.firstText,
25092             cls: "prev btn-outline-secondary",
25093             html : ' <i class="fa fa-step-backward"></i>',
25094             disabled: true,
25095             preventDefault: true,
25096             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25097         });
25098         
25099         this.prev =  this.navgroup.addItem({
25100             tooltip: this.prevText,
25101             cls: "prev btn-outline-secondary",
25102             html : ' <i class="fa fa-backward"></i>',
25103             disabled: true,
25104             preventDefault: true,
25105             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25106         });
25107     //this.addSeparator();
25108         
25109         
25110         var field = this.navgroup.addItem( {
25111             tagtype : 'span',
25112             cls : 'x-paging-position  btn-outline-secondary',
25113              disabled: true,
25114             html : this.beforePageText  +
25115                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25116                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25117          } ); //?? escaped?
25118         
25119         this.field = field.el.select('input', true).first();
25120         this.field.on("keydown", this.onPagingKeydown, this);
25121         this.field.on("focus", function(){this.dom.select();});
25122     
25123     
25124         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25125         //this.field.setHeight(18);
25126         //this.addSeparator();
25127         this.next = this.navgroup.addItem({
25128             tooltip: this.nextText,
25129             cls: "next btn-outline-secondary",
25130             html : ' <i class="fa fa-forward"></i>',
25131             disabled: true,
25132             preventDefault: true,
25133             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25134         });
25135         this.last = this.navgroup.addItem({
25136             tooltip: this.lastText,
25137             html : ' <i class="fa fa-step-forward"></i>',
25138             cls: "next btn-outline-secondary",
25139             disabled: true,
25140             preventDefault: true,
25141             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25142         });
25143     //this.addSeparator();
25144         this.loading = this.navgroup.addItem({
25145             tooltip: this.refreshText,
25146             cls: "btn-outline-secondary",
25147             html : ' <i class="fa fa-refresh"></i>',
25148             preventDefault: true,
25149             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25150         });
25151         
25152     },
25153
25154     // private
25155     updateInfo : function(){
25156         if(this.displayEl){
25157             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25158             var msg = count == 0 ?
25159                 this.emptyMsg :
25160                 String.format(
25161                     this.displayMsg,
25162                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25163                 );
25164             this.displayEl.update(msg);
25165         }
25166     },
25167
25168     // private
25169     onLoad : function(ds, r, o)
25170     {
25171         this.cursor = o.params.start ? o.params.start : 0;
25172         
25173         var d = this.getPageData(),
25174             ap = d.activePage,
25175             ps = d.pages;
25176         
25177         
25178         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25179         this.field.dom.value = ap;
25180         this.first.setDisabled(ap == 1);
25181         this.prev.setDisabled(ap == 1);
25182         this.next.setDisabled(ap == ps);
25183         this.last.setDisabled(ap == ps);
25184         this.loading.enable();
25185         this.updateInfo();
25186     },
25187
25188     // private
25189     getPageData : function(){
25190         var total = this.ds.getTotalCount();
25191         return {
25192             total : total,
25193             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25194             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25195         };
25196     },
25197
25198     // private
25199     onLoadError : function(){
25200         this.loading.enable();
25201     },
25202
25203     // private
25204     onPagingKeydown : function(e){
25205         var k = e.getKey();
25206         var d = this.getPageData();
25207         if(k == e.RETURN){
25208             var v = this.field.dom.value, pageNum;
25209             if(!v || isNaN(pageNum = parseInt(v, 10))){
25210                 this.field.dom.value = d.activePage;
25211                 return;
25212             }
25213             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25214             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25215             e.stopEvent();
25216         }
25217         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))
25218         {
25219           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25220           this.field.dom.value = pageNum;
25221           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25222           e.stopEvent();
25223         }
25224         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25225         {
25226           var v = this.field.dom.value, pageNum; 
25227           var increment = (e.shiftKey) ? 10 : 1;
25228           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25229                 increment *= -1;
25230           }
25231           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25232             this.field.dom.value = d.activePage;
25233             return;
25234           }
25235           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25236           {
25237             this.field.dom.value = parseInt(v, 10) + increment;
25238             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25239             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25240           }
25241           e.stopEvent();
25242         }
25243     },
25244
25245     // private
25246     beforeLoad : function(){
25247         if(this.loading){
25248             this.loading.disable();
25249         }
25250     },
25251
25252     // private
25253     onClick : function(which){
25254         
25255         var ds = this.ds;
25256         if (!ds) {
25257             return;
25258         }
25259         
25260         switch(which){
25261             case "first":
25262                 ds.load({params:{start: 0, limit: this.pageSize}});
25263             break;
25264             case "prev":
25265                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25266             break;
25267             case "next":
25268                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25269             break;
25270             case "last":
25271                 var total = ds.getTotalCount();
25272                 var extra = total % this.pageSize;
25273                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25274                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25275             break;
25276             case "refresh":
25277                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25278             break;
25279         }
25280     },
25281
25282     /**
25283      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25284      * @param {Roo.data.Store} store The data store to unbind
25285      */
25286     unbind : function(ds){
25287         ds.un("beforeload", this.beforeLoad, this);
25288         ds.un("load", this.onLoad, this);
25289         ds.un("loadexception", this.onLoadError, this);
25290         ds.un("remove", this.updateInfo, this);
25291         ds.un("add", this.updateInfo, this);
25292         this.ds = undefined;
25293     },
25294
25295     /**
25296      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25297      * @param {Roo.data.Store} store The data store to bind
25298      */
25299     bind : function(ds){
25300         ds.on("beforeload", this.beforeLoad, this);
25301         ds.on("load", this.onLoad, this);
25302         ds.on("loadexception", this.onLoadError, this);
25303         ds.on("remove", this.updateInfo, this);
25304         ds.on("add", this.updateInfo, this);
25305         this.ds = ds;
25306     }
25307 });/*
25308  * - LGPL
25309  *
25310  * element
25311  * 
25312  */
25313
25314 /**
25315  * @class Roo.bootstrap.MessageBar
25316  * @extends Roo.bootstrap.Component
25317  * Bootstrap MessageBar class
25318  * @cfg {String} html contents of the MessageBar
25319  * @cfg {String} weight (info | success | warning | danger) default info
25320  * @cfg {String} beforeClass insert the bar before the given class
25321  * @cfg {Boolean} closable (true | false) default false
25322  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25323  * 
25324  * @constructor
25325  * Create a new Element
25326  * @param {Object} config The config object
25327  */
25328
25329 Roo.bootstrap.MessageBar = function(config){
25330     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25331 };
25332
25333 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25334     
25335     html: '',
25336     weight: 'info',
25337     closable: false,
25338     fixed: false,
25339     beforeClass: 'bootstrap-sticky-wrap',
25340     
25341     getAutoCreate : function(){
25342         
25343         var cfg = {
25344             tag: 'div',
25345             cls: 'alert alert-dismissable alert-' + this.weight,
25346             cn: [
25347                 {
25348                     tag: 'span',
25349                     cls: 'message',
25350                     html: this.html || ''
25351                 }
25352             ]
25353         };
25354         
25355         if(this.fixed){
25356             cfg.cls += ' alert-messages-fixed';
25357         }
25358         
25359         if(this.closable){
25360             cfg.cn.push({
25361                 tag: 'button',
25362                 cls: 'close',
25363                 html: 'x'
25364             });
25365         }
25366         
25367         return cfg;
25368     },
25369     
25370     onRender : function(ct, position)
25371     {
25372         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25373         
25374         if(!this.el){
25375             var cfg = Roo.apply({},  this.getAutoCreate());
25376             cfg.id = Roo.id();
25377             
25378             if (this.cls) {
25379                 cfg.cls += ' ' + this.cls;
25380             }
25381             if (this.style) {
25382                 cfg.style = this.style;
25383             }
25384             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25385             
25386             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25387         }
25388         
25389         this.el.select('>button.close').on('click', this.hide, this);
25390         
25391     },
25392     
25393     show : function()
25394     {
25395         if (!this.rendered) {
25396             this.render();
25397         }
25398         
25399         this.el.show();
25400         
25401         this.fireEvent('show', this);
25402         
25403     },
25404     
25405     hide : function()
25406     {
25407         if (!this.rendered) {
25408             this.render();
25409         }
25410         
25411         this.el.hide();
25412         
25413         this.fireEvent('hide', this);
25414     },
25415     
25416     update : function()
25417     {
25418 //        var e = this.el.dom.firstChild;
25419 //        
25420 //        if(this.closable){
25421 //            e = e.nextSibling;
25422 //        }
25423 //        
25424 //        e.data = this.html || '';
25425
25426         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25427     }
25428    
25429 });
25430
25431  
25432
25433      /*
25434  * - LGPL
25435  *
25436  * Graph
25437  * 
25438  */
25439
25440
25441 /**
25442  * @class Roo.bootstrap.Graph
25443  * @extends Roo.bootstrap.Component
25444  * Bootstrap Graph class
25445 > Prameters
25446  -sm {number} sm 4
25447  -md {number} md 5
25448  @cfg {String} graphtype  bar | vbar | pie
25449  @cfg {number} g_x coodinator | centre x (pie)
25450  @cfg {number} g_y coodinator | centre y (pie)
25451  @cfg {number} g_r radius (pie)
25452  @cfg {number} g_height height of the chart (respected by all elements in the set)
25453  @cfg {number} g_width width of the chart (respected by all elements in the set)
25454  @cfg {Object} title The title of the chart
25455     
25456  -{Array}  values
25457  -opts (object) options for the chart 
25458      o {
25459      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25460      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25461      o vgutter (number)
25462      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.
25463      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25464      o to
25465      o stretch (boolean)
25466      o }
25467  -opts (object) options for the pie
25468      o{
25469      o cut
25470      o startAngle (number)
25471      o endAngle (number)
25472      } 
25473  *
25474  * @constructor
25475  * Create a new Input
25476  * @param {Object} config The config object
25477  */
25478
25479 Roo.bootstrap.Graph = function(config){
25480     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25481     
25482     this.addEvents({
25483         // img events
25484         /**
25485          * @event click
25486          * The img click event for the img.
25487          * @param {Roo.EventObject} e
25488          */
25489         "click" : true
25490     });
25491 };
25492
25493 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25494     
25495     sm: 4,
25496     md: 5,
25497     graphtype: 'bar',
25498     g_height: 250,
25499     g_width: 400,
25500     g_x: 50,
25501     g_y: 50,
25502     g_r: 30,
25503     opts:{
25504         //g_colors: this.colors,
25505         g_type: 'soft',
25506         g_gutter: '20%'
25507
25508     },
25509     title : false,
25510
25511     getAutoCreate : function(){
25512         
25513         var cfg = {
25514             tag: 'div',
25515             html : null
25516         };
25517         
25518         
25519         return  cfg;
25520     },
25521
25522     onRender : function(ct,position){
25523         
25524         
25525         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25526         
25527         if (typeof(Raphael) == 'undefined') {
25528             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25529             return;
25530         }
25531         
25532         this.raphael = Raphael(this.el.dom);
25533         
25534                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25535                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25536                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25537                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25538                 /*
25539                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25540                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25541                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25542                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25543                 
25544                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25545                 r.barchart(330, 10, 300, 220, data1);
25546                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25547                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25548                 */
25549                 
25550                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25551                 // r.barchart(30, 30, 560, 250,  xdata, {
25552                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25553                 //     axis : "0 0 1 1",
25554                 //     axisxlabels :  xdata
25555                 //     //yvalues : cols,
25556                    
25557                 // });
25558 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25559 //        
25560 //        this.load(null,xdata,{
25561 //                axis : "0 0 1 1",
25562 //                axisxlabels :  xdata
25563 //                });
25564
25565     },
25566
25567     load : function(graphtype,xdata,opts)
25568     {
25569         this.raphael.clear();
25570         if(!graphtype) {
25571             graphtype = this.graphtype;
25572         }
25573         if(!opts){
25574             opts = this.opts;
25575         }
25576         var r = this.raphael,
25577             fin = function () {
25578                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25579             },
25580             fout = function () {
25581                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25582             },
25583             pfin = function() {
25584                 this.sector.stop();
25585                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25586
25587                 if (this.label) {
25588                     this.label[0].stop();
25589                     this.label[0].attr({ r: 7.5 });
25590                     this.label[1].attr({ "font-weight": 800 });
25591                 }
25592             },
25593             pfout = function() {
25594                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25595
25596                 if (this.label) {
25597                     this.label[0].animate({ r: 5 }, 500, "bounce");
25598                     this.label[1].attr({ "font-weight": 400 });
25599                 }
25600             };
25601
25602         switch(graphtype){
25603             case 'bar':
25604                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25605                 break;
25606             case 'hbar':
25607                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25608                 break;
25609             case 'pie':
25610 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25611 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25612 //            
25613                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25614                 
25615                 break;
25616
25617         }
25618         
25619         if(this.title){
25620             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25621         }
25622         
25623     },
25624     
25625     setTitle: function(o)
25626     {
25627         this.title = o;
25628     },
25629     
25630     initEvents: function() {
25631         
25632         if(!this.href){
25633             this.el.on('click', this.onClick, this);
25634         }
25635     },
25636     
25637     onClick : function(e)
25638     {
25639         Roo.log('img onclick');
25640         this.fireEvent('click', this, e);
25641     }
25642    
25643 });
25644
25645  
25646 /*
25647  * - LGPL
25648  *
25649  * numberBox
25650  * 
25651  */
25652 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25653
25654 /**
25655  * @class Roo.bootstrap.dash.NumberBox
25656  * @extends Roo.bootstrap.Component
25657  * Bootstrap NumberBox class
25658  * @cfg {String} headline Box headline
25659  * @cfg {String} content Box content
25660  * @cfg {String} icon Box icon
25661  * @cfg {String} footer Footer text
25662  * @cfg {String} fhref Footer href
25663  * 
25664  * @constructor
25665  * Create a new NumberBox
25666  * @param {Object} config The config object
25667  */
25668
25669
25670 Roo.bootstrap.dash.NumberBox = function(config){
25671     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25672     
25673 };
25674
25675 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25676     
25677     headline : '',
25678     content : '',
25679     icon : '',
25680     footer : '',
25681     fhref : '',
25682     ficon : '',
25683     
25684     getAutoCreate : function(){
25685         
25686         var cfg = {
25687             tag : 'div',
25688             cls : 'small-box ',
25689             cn : [
25690                 {
25691                     tag : 'div',
25692                     cls : 'inner',
25693                     cn :[
25694                         {
25695                             tag : 'h3',
25696                             cls : 'roo-headline',
25697                             html : this.headline
25698                         },
25699                         {
25700                             tag : 'p',
25701                             cls : 'roo-content',
25702                             html : this.content
25703                         }
25704                     ]
25705                 }
25706             ]
25707         };
25708         
25709         if(this.icon){
25710             cfg.cn.push({
25711                 tag : 'div',
25712                 cls : 'icon',
25713                 cn :[
25714                     {
25715                         tag : 'i',
25716                         cls : 'ion ' + this.icon
25717                     }
25718                 ]
25719             });
25720         }
25721         
25722         if(this.footer){
25723             var footer = {
25724                 tag : 'a',
25725                 cls : 'small-box-footer',
25726                 href : this.fhref || '#',
25727                 html : this.footer
25728             };
25729             
25730             cfg.cn.push(footer);
25731             
25732         }
25733         
25734         return  cfg;
25735     },
25736
25737     onRender : function(ct,position){
25738         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25739
25740
25741        
25742                 
25743     },
25744
25745     setHeadline: function (value)
25746     {
25747         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25748     },
25749     
25750     setFooter: function (value, href)
25751     {
25752         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25753         
25754         if(href){
25755             this.el.select('a.small-box-footer',true).first().attr('href', href);
25756         }
25757         
25758     },
25759
25760     setContent: function (value)
25761     {
25762         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25763     },
25764
25765     initEvents: function() 
25766     {   
25767         
25768     }
25769     
25770 });
25771
25772  
25773 /*
25774  * - LGPL
25775  *
25776  * TabBox
25777  * 
25778  */
25779 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25780
25781 /**
25782  * @class Roo.bootstrap.dash.TabBox
25783  * @extends Roo.bootstrap.Component
25784  * Bootstrap TabBox class
25785  * @cfg {String} title Title of the TabBox
25786  * @cfg {String} icon Icon of the TabBox
25787  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25788  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25789  * 
25790  * @constructor
25791  * Create a new TabBox
25792  * @param {Object} config The config object
25793  */
25794
25795
25796 Roo.bootstrap.dash.TabBox = function(config){
25797     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25798     this.addEvents({
25799         // raw events
25800         /**
25801          * @event addpane
25802          * When a pane is added
25803          * @param {Roo.bootstrap.dash.TabPane} pane
25804          */
25805         "addpane" : true,
25806         /**
25807          * @event activatepane
25808          * When a pane is activated
25809          * @param {Roo.bootstrap.dash.TabPane} pane
25810          */
25811         "activatepane" : true
25812         
25813          
25814     });
25815     
25816     this.panes = [];
25817 };
25818
25819 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25820
25821     title : '',
25822     icon : false,
25823     showtabs : true,
25824     tabScrollable : false,
25825     
25826     getChildContainer : function()
25827     {
25828         return this.el.select('.tab-content', true).first();
25829     },
25830     
25831     getAutoCreate : function(){
25832         
25833         var header = {
25834             tag: 'li',
25835             cls: 'pull-left header',
25836             html: this.title,
25837             cn : []
25838         };
25839         
25840         if(this.icon){
25841             header.cn.push({
25842                 tag: 'i',
25843                 cls: 'fa ' + this.icon
25844             });
25845         }
25846         
25847         var h = {
25848             tag: 'ul',
25849             cls: 'nav nav-tabs pull-right',
25850             cn: [
25851                 header
25852             ]
25853         };
25854         
25855         if(this.tabScrollable){
25856             h = {
25857                 tag: 'div',
25858                 cls: 'tab-header',
25859                 cn: [
25860                     {
25861                         tag: 'ul',
25862                         cls: 'nav nav-tabs pull-right',
25863                         cn: [
25864                             header
25865                         ]
25866                     }
25867                 ]
25868             };
25869         }
25870         
25871         var cfg = {
25872             tag: 'div',
25873             cls: 'nav-tabs-custom',
25874             cn: [
25875                 h,
25876                 {
25877                     tag: 'div',
25878                     cls: 'tab-content no-padding',
25879                     cn: []
25880                 }
25881             ]
25882         };
25883
25884         return  cfg;
25885     },
25886     initEvents : function()
25887     {
25888         //Roo.log('add add pane handler');
25889         this.on('addpane', this.onAddPane, this);
25890     },
25891      /**
25892      * Updates the box title
25893      * @param {String} html to set the title to.
25894      */
25895     setTitle : function(value)
25896     {
25897         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25898     },
25899     onAddPane : function(pane)
25900     {
25901         this.panes.push(pane);
25902         //Roo.log('addpane');
25903         //Roo.log(pane);
25904         // tabs are rendere left to right..
25905         if(!this.showtabs){
25906             return;
25907         }
25908         
25909         var ctr = this.el.select('.nav-tabs', true).first();
25910          
25911          
25912         var existing = ctr.select('.nav-tab',true);
25913         var qty = existing.getCount();;
25914         
25915         
25916         var tab = ctr.createChild({
25917             tag : 'li',
25918             cls : 'nav-tab' + (qty ? '' : ' active'),
25919             cn : [
25920                 {
25921                     tag : 'a',
25922                     href:'#',
25923                     html : pane.title
25924                 }
25925             ]
25926         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25927         pane.tab = tab;
25928         
25929         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25930         if (!qty) {
25931             pane.el.addClass('active');
25932         }
25933         
25934                 
25935     },
25936     onTabClick : function(ev,un,ob,pane)
25937     {
25938         //Roo.log('tab - prev default');
25939         ev.preventDefault();
25940         
25941         
25942         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25943         pane.tab.addClass('active');
25944         //Roo.log(pane.title);
25945         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25946         // technically we should have a deactivate event.. but maybe add later.
25947         // and it should not de-activate the selected tab...
25948         this.fireEvent('activatepane', pane);
25949         pane.el.addClass('active');
25950         pane.fireEvent('activate');
25951         
25952         
25953     },
25954     
25955     getActivePane : function()
25956     {
25957         var r = false;
25958         Roo.each(this.panes, function(p) {
25959             if(p.el.hasClass('active')){
25960                 r = p;
25961                 return false;
25962             }
25963             
25964             return;
25965         });
25966         
25967         return r;
25968     }
25969     
25970     
25971 });
25972
25973  
25974 /*
25975  * - LGPL
25976  *
25977  * Tab pane
25978  * 
25979  */
25980 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25981 /**
25982  * @class Roo.bootstrap.TabPane
25983  * @extends Roo.bootstrap.Component
25984  * Bootstrap TabPane class
25985  * @cfg {Boolean} active (false | true) Default false
25986  * @cfg {String} title title of panel
25987
25988  * 
25989  * @constructor
25990  * Create a new TabPane
25991  * @param {Object} config The config object
25992  */
25993
25994 Roo.bootstrap.dash.TabPane = function(config){
25995     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25996     
25997     this.addEvents({
25998         // raw events
25999         /**
26000          * @event activate
26001          * When a pane is activated
26002          * @param {Roo.bootstrap.dash.TabPane} pane
26003          */
26004         "activate" : true
26005          
26006     });
26007 };
26008
26009 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26010     
26011     active : false,
26012     title : '',
26013     
26014     // the tabBox that this is attached to.
26015     tab : false,
26016      
26017     getAutoCreate : function() 
26018     {
26019         var cfg = {
26020             tag: 'div',
26021             cls: 'tab-pane'
26022         };
26023         
26024         if(this.active){
26025             cfg.cls += ' active';
26026         }
26027         
26028         return cfg;
26029     },
26030     initEvents  : function()
26031     {
26032         //Roo.log('trigger add pane handler');
26033         this.parent().fireEvent('addpane', this)
26034     },
26035     
26036      /**
26037      * Updates the tab title 
26038      * @param {String} html to set the title to.
26039      */
26040     setTitle: function(str)
26041     {
26042         if (!this.tab) {
26043             return;
26044         }
26045         this.title = str;
26046         this.tab.select('a', true).first().dom.innerHTML = str;
26047         
26048     }
26049     
26050     
26051     
26052 });
26053
26054  
26055
26056
26057  /*
26058  * - LGPL
26059  *
26060  * menu
26061  * 
26062  */
26063 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26064
26065 /**
26066  * @class Roo.bootstrap.menu.Menu
26067  * @extends Roo.bootstrap.Component
26068  * Bootstrap Menu class - container for Menu
26069  * @cfg {String} html Text of the menu
26070  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26071  * @cfg {String} icon Font awesome icon
26072  * @cfg {String} pos Menu align to (top | bottom) default bottom
26073  * 
26074  * 
26075  * @constructor
26076  * Create a new Menu
26077  * @param {Object} config The config object
26078  */
26079
26080
26081 Roo.bootstrap.menu.Menu = function(config){
26082     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26083     
26084     this.addEvents({
26085         /**
26086          * @event beforeshow
26087          * Fires before this menu is displayed
26088          * @param {Roo.bootstrap.menu.Menu} this
26089          */
26090         beforeshow : true,
26091         /**
26092          * @event beforehide
26093          * Fires before this menu is hidden
26094          * @param {Roo.bootstrap.menu.Menu} this
26095          */
26096         beforehide : true,
26097         /**
26098          * @event show
26099          * Fires after this menu is displayed
26100          * @param {Roo.bootstrap.menu.Menu} this
26101          */
26102         show : true,
26103         /**
26104          * @event hide
26105          * Fires after this menu is hidden
26106          * @param {Roo.bootstrap.menu.Menu} this
26107          */
26108         hide : true,
26109         /**
26110          * @event click
26111          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26112          * @param {Roo.bootstrap.menu.Menu} this
26113          * @param {Roo.EventObject} e
26114          */
26115         click : true
26116     });
26117     
26118 };
26119
26120 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26121     
26122     submenu : false,
26123     html : '',
26124     weight : 'default',
26125     icon : false,
26126     pos : 'bottom',
26127     
26128     
26129     getChildContainer : function() {
26130         if(this.isSubMenu){
26131             return this.el;
26132         }
26133         
26134         return this.el.select('ul.dropdown-menu', true).first();  
26135     },
26136     
26137     getAutoCreate : function()
26138     {
26139         var text = [
26140             {
26141                 tag : 'span',
26142                 cls : 'roo-menu-text',
26143                 html : this.html
26144             }
26145         ];
26146         
26147         if(this.icon){
26148             text.unshift({
26149                 tag : 'i',
26150                 cls : 'fa ' + this.icon
26151             })
26152         }
26153         
26154         
26155         var cfg = {
26156             tag : 'div',
26157             cls : 'btn-group',
26158             cn : [
26159                 {
26160                     tag : 'button',
26161                     cls : 'dropdown-button btn btn-' + this.weight,
26162                     cn : text
26163                 },
26164                 {
26165                     tag : 'button',
26166                     cls : 'dropdown-toggle btn btn-' + this.weight,
26167                     cn : [
26168                         {
26169                             tag : 'span',
26170                             cls : 'caret'
26171                         }
26172                     ]
26173                 },
26174                 {
26175                     tag : 'ul',
26176                     cls : 'dropdown-menu'
26177                 }
26178             ]
26179             
26180         };
26181         
26182         if(this.pos == 'top'){
26183             cfg.cls += ' dropup';
26184         }
26185         
26186         if(this.isSubMenu){
26187             cfg = {
26188                 tag : 'ul',
26189                 cls : 'dropdown-menu'
26190             }
26191         }
26192         
26193         return cfg;
26194     },
26195     
26196     onRender : function(ct, position)
26197     {
26198         this.isSubMenu = ct.hasClass('dropdown-submenu');
26199         
26200         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26201     },
26202     
26203     initEvents : function() 
26204     {
26205         if(this.isSubMenu){
26206             return;
26207         }
26208         
26209         this.hidden = true;
26210         
26211         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26212         this.triggerEl.on('click', this.onTriggerPress, this);
26213         
26214         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26215         this.buttonEl.on('click', this.onClick, this);
26216         
26217     },
26218     
26219     list : function()
26220     {
26221         if(this.isSubMenu){
26222             return this.el;
26223         }
26224         
26225         return this.el.select('ul.dropdown-menu', true).first();
26226     },
26227     
26228     onClick : function(e)
26229     {
26230         this.fireEvent("click", this, e);
26231     },
26232     
26233     onTriggerPress  : function(e)
26234     {   
26235         if (this.isVisible()) {
26236             this.hide();
26237         } else {
26238             this.show();
26239         }
26240     },
26241     
26242     isVisible : function(){
26243         return !this.hidden;
26244     },
26245     
26246     show : function()
26247     {
26248         this.fireEvent("beforeshow", this);
26249         
26250         this.hidden = false;
26251         this.el.addClass('open');
26252         
26253         Roo.get(document).on("mouseup", this.onMouseUp, this);
26254         
26255         this.fireEvent("show", this);
26256         
26257         
26258     },
26259     
26260     hide : function()
26261     {
26262         this.fireEvent("beforehide", this);
26263         
26264         this.hidden = true;
26265         this.el.removeClass('open');
26266         
26267         Roo.get(document).un("mouseup", this.onMouseUp);
26268         
26269         this.fireEvent("hide", this);
26270     },
26271     
26272     onMouseUp : function()
26273     {
26274         this.hide();
26275     }
26276     
26277 });
26278
26279  
26280  /*
26281  * - LGPL
26282  *
26283  * menu item
26284  * 
26285  */
26286 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26287
26288 /**
26289  * @class Roo.bootstrap.menu.Item
26290  * @extends Roo.bootstrap.Component
26291  * Bootstrap MenuItem class
26292  * @cfg {Boolean} submenu (true | false) default false
26293  * @cfg {String} html text of the item
26294  * @cfg {String} href the link
26295  * @cfg {Boolean} disable (true | false) default false
26296  * @cfg {Boolean} preventDefault (true | false) default true
26297  * @cfg {String} icon Font awesome icon
26298  * @cfg {String} pos Submenu align to (left | right) default right 
26299  * 
26300  * 
26301  * @constructor
26302  * Create a new Item
26303  * @param {Object} config The config object
26304  */
26305
26306
26307 Roo.bootstrap.menu.Item = function(config){
26308     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26309     this.addEvents({
26310         /**
26311          * @event mouseover
26312          * Fires when the mouse is hovering over this menu
26313          * @param {Roo.bootstrap.menu.Item} this
26314          * @param {Roo.EventObject} e
26315          */
26316         mouseover : true,
26317         /**
26318          * @event mouseout
26319          * Fires when the mouse exits this menu
26320          * @param {Roo.bootstrap.menu.Item} this
26321          * @param {Roo.EventObject} e
26322          */
26323         mouseout : true,
26324         // raw events
26325         /**
26326          * @event click
26327          * The raw click event for the entire grid.
26328          * @param {Roo.EventObject} e
26329          */
26330         click : true
26331     });
26332 };
26333
26334 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26335     
26336     submenu : false,
26337     href : '',
26338     html : '',
26339     preventDefault: true,
26340     disable : false,
26341     icon : false,
26342     pos : 'right',
26343     
26344     getAutoCreate : function()
26345     {
26346         var text = [
26347             {
26348                 tag : 'span',
26349                 cls : 'roo-menu-item-text',
26350                 html : this.html
26351             }
26352         ];
26353         
26354         if(this.icon){
26355             text.unshift({
26356                 tag : 'i',
26357                 cls : 'fa ' + this.icon
26358             })
26359         }
26360         
26361         var cfg = {
26362             tag : 'li',
26363             cn : [
26364                 {
26365                     tag : 'a',
26366                     href : this.href || '#',
26367                     cn : text
26368                 }
26369             ]
26370         };
26371         
26372         if(this.disable){
26373             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26374         }
26375         
26376         if(this.submenu){
26377             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26378             
26379             if(this.pos == 'left'){
26380                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26381             }
26382         }
26383         
26384         return cfg;
26385     },
26386     
26387     initEvents : function() 
26388     {
26389         this.el.on('mouseover', this.onMouseOver, this);
26390         this.el.on('mouseout', this.onMouseOut, this);
26391         
26392         this.el.select('a', true).first().on('click', this.onClick, this);
26393         
26394     },
26395     
26396     onClick : function(e)
26397     {
26398         if(this.preventDefault){
26399             e.preventDefault();
26400         }
26401         
26402         this.fireEvent("click", this, e);
26403     },
26404     
26405     onMouseOver : function(e)
26406     {
26407         if(this.submenu && this.pos == 'left'){
26408             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26409         }
26410         
26411         this.fireEvent("mouseover", this, e);
26412     },
26413     
26414     onMouseOut : function(e)
26415     {
26416         this.fireEvent("mouseout", this, e);
26417     }
26418 });
26419
26420  
26421
26422  /*
26423  * - LGPL
26424  *
26425  * menu separator
26426  * 
26427  */
26428 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26429
26430 /**
26431  * @class Roo.bootstrap.menu.Separator
26432  * @extends Roo.bootstrap.Component
26433  * Bootstrap Separator class
26434  * 
26435  * @constructor
26436  * Create a new Separator
26437  * @param {Object} config The config object
26438  */
26439
26440
26441 Roo.bootstrap.menu.Separator = function(config){
26442     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26443 };
26444
26445 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26446     
26447     getAutoCreate : function(){
26448         var cfg = {
26449             tag : 'li',
26450             cls: 'divider'
26451         };
26452         
26453         return cfg;
26454     }
26455    
26456 });
26457
26458  
26459
26460  /*
26461  * - LGPL
26462  *
26463  * Tooltip
26464  * 
26465  */
26466
26467 /**
26468  * @class Roo.bootstrap.Tooltip
26469  * Bootstrap Tooltip class
26470  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26471  * to determine which dom element triggers the tooltip.
26472  * 
26473  * It needs to add support for additional attributes like tooltip-position
26474  * 
26475  * @constructor
26476  * Create a new Toolti
26477  * @param {Object} config The config object
26478  */
26479
26480 Roo.bootstrap.Tooltip = function(config){
26481     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26482     
26483     this.alignment = Roo.bootstrap.Tooltip.alignment;
26484     
26485     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26486         this.alignment = config.alignment;
26487     }
26488     
26489 };
26490
26491 Roo.apply(Roo.bootstrap.Tooltip, {
26492     /**
26493      * @function init initialize tooltip monitoring.
26494      * @static
26495      */
26496     currentEl : false,
26497     currentTip : false,
26498     currentRegion : false,
26499     
26500     //  init : delay?
26501     
26502     init : function()
26503     {
26504         Roo.get(document).on('mouseover', this.enter ,this);
26505         Roo.get(document).on('mouseout', this.leave, this);
26506          
26507         
26508         this.currentTip = new Roo.bootstrap.Tooltip();
26509     },
26510     
26511     enter : function(ev)
26512     {
26513         var dom = ev.getTarget();
26514         
26515         //Roo.log(['enter',dom]);
26516         var el = Roo.fly(dom);
26517         if (this.currentEl) {
26518             //Roo.log(dom);
26519             //Roo.log(this.currentEl);
26520             //Roo.log(this.currentEl.contains(dom));
26521             if (this.currentEl == el) {
26522                 return;
26523             }
26524             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26525                 return;
26526             }
26527
26528         }
26529         
26530         if (this.currentTip.el) {
26531             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26532         }    
26533         //Roo.log(ev);
26534         
26535         if(!el || el.dom == document){
26536             return;
26537         }
26538         
26539         var bindEl = el;
26540         
26541         // you can not look for children, as if el is the body.. then everythign is the child..
26542         if (!el.attr('tooltip')) { //
26543             if (!el.select("[tooltip]").elements.length) {
26544                 return;
26545             }
26546             // is the mouse over this child...?
26547             bindEl = el.select("[tooltip]").first();
26548             var xy = ev.getXY();
26549             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26550                 //Roo.log("not in region.");
26551                 return;
26552             }
26553             //Roo.log("child element over..");
26554             
26555         }
26556         this.currentEl = bindEl;
26557         this.currentTip.bind(bindEl);
26558         this.currentRegion = Roo.lib.Region.getRegion(dom);
26559         this.currentTip.enter();
26560         
26561     },
26562     leave : function(ev)
26563     {
26564         var dom = ev.getTarget();
26565         //Roo.log(['leave',dom]);
26566         if (!this.currentEl) {
26567             return;
26568         }
26569         
26570         
26571         if (dom != this.currentEl.dom) {
26572             return;
26573         }
26574         var xy = ev.getXY();
26575         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26576             return;
26577         }
26578         // only activate leave if mouse cursor is outside... bounding box..
26579         
26580         
26581         
26582         
26583         if (this.currentTip) {
26584             this.currentTip.leave();
26585         }
26586         //Roo.log('clear currentEl');
26587         this.currentEl = false;
26588         
26589         
26590     },
26591     alignment : {
26592         'left' : ['r-l', [-2,0], 'right'],
26593         'right' : ['l-r', [2,0], 'left'],
26594         'bottom' : ['t-b', [0,2], 'top'],
26595         'top' : [ 'b-t', [0,-2], 'bottom']
26596     }
26597     
26598 });
26599
26600
26601 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26602     
26603     
26604     bindEl : false,
26605     
26606     delay : null, // can be { show : 300 , hide: 500}
26607     
26608     timeout : null,
26609     
26610     hoverState : null, //???
26611     
26612     placement : 'bottom', 
26613     
26614     alignment : false,
26615     
26616     getAutoCreate : function(){
26617     
26618         var cfg = {
26619            cls : 'tooltip',
26620            role : 'tooltip',
26621            cn : [
26622                 {
26623                     cls : 'tooltip-arrow'
26624                 },
26625                 {
26626                     cls : 'tooltip-inner'
26627                 }
26628            ]
26629         };
26630         
26631         return cfg;
26632     },
26633     bind : function(el)
26634     {
26635         this.bindEl = el;
26636     },
26637       
26638     
26639     enter : function () {
26640        
26641         if (this.timeout != null) {
26642             clearTimeout(this.timeout);
26643         }
26644         
26645         this.hoverState = 'in';
26646          //Roo.log("enter - show");
26647         if (!this.delay || !this.delay.show) {
26648             this.show();
26649             return;
26650         }
26651         var _t = this;
26652         this.timeout = setTimeout(function () {
26653             if (_t.hoverState == 'in') {
26654                 _t.show();
26655             }
26656         }, this.delay.show);
26657     },
26658     leave : function()
26659     {
26660         clearTimeout(this.timeout);
26661     
26662         this.hoverState = 'out';
26663          if (!this.delay || !this.delay.hide) {
26664             this.hide();
26665             return;
26666         }
26667        
26668         var _t = this;
26669         this.timeout = setTimeout(function () {
26670             //Roo.log("leave - timeout");
26671             
26672             if (_t.hoverState == 'out') {
26673                 _t.hide();
26674                 Roo.bootstrap.Tooltip.currentEl = false;
26675             }
26676         }, delay);
26677     },
26678     
26679     show : function (msg)
26680     {
26681         if (!this.el) {
26682             this.render(document.body);
26683         }
26684         // set content.
26685         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26686         
26687         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26688         
26689         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26690         
26691         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26692         
26693         var placement = typeof this.placement == 'function' ?
26694             this.placement.call(this, this.el, on_el) :
26695             this.placement;
26696             
26697         var autoToken = /\s?auto?\s?/i;
26698         var autoPlace = autoToken.test(placement);
26699         if (autoPlace) {
26700             placement = placement.replace(autoToken, '') || 'top';
26701         }
26702         
26703         //this.el.detach()
26704         //this.el.setXY([0,0]);
26705         this.el.show();
26706         //this.el.dom.style.display='block';
26707         
26708         //this.el.appendTo(on_el);
26709         
26710         var p = this.getPosition();
26711         var box = this.el.getBox();
26712         
26713         if (autoPlace) {
26714             // fixme..
26715         }
26716         
26717         var align = this.alignment[placement];
26718         
26719         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26720         
26721         if(placement == 'top' || placement == 'bottom'){
26722             if(xy[0] < 0){
26723                 placement = 'right';
26724             }
26725             
26726             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26727                 placement = 'left';
26728             }
26729             
26730             var scroll = Roo.select('body', true).first().getScroll();
26731             
26732             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26733                 placement = 'top';
26734             }
26735             
26736             align = this.alignment[placement];
26737         }
26738         
26739         this.el.alignTo(this.bindEl, align[0],align[1]);
26740         //var arrow = this.el.select('.arrow',true).first();
26741         //arrow.set(align[2], 
26742         
26743         this.el.addClass(placement);
26744         
26745         this.el.addClass('in fade');
26746         
26747         this.hoverState = null;
26748         
26749         if (this.el.hasClass('fade')) {
26750             // fade it?
26751         }
26752         
26753     },
26754     hide : function()
26755     {
26756          
26757         if (!this.el) {
26758             return;
26759         }
26760         //this.el.setXY([0,0]);
26761         this.el.removeClass('in');
26762         //this.el.hide();
26763         
26764     }
26765     
26766 });
26767  
26768
26769  /*
26770  * - LGPL
26771  *
26772  * Location Picker
26773  * 
26774  */
26775
26776 /**
26777  * @class Roo.bootstrap.LocationPicker
26778  * @extends Roo.bootstrap.Component
26779  * Bootstrap LocationPicker class
26780  * @cfg {Number} latitude Position when init default 0
26781  * @cfg {Number} longitude Position when init default 0
26782  * @cfg {Number} zoom default 15
26783  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26784  * @cfg {Boolean} mapTypeControl default false
26785  * @cfg {Boolean} disableDoubleClickZoom default false
26786  * @cfg {Boolean} scrollwheel default true
26787  * @cfg {Boolean} streetViewControl default false
26788  * @cfg {Number} radius default 0
26789  * @cfg {String} locationName
26790  * @cfg {Boolean} draggable default true
26791  * @cfg {Boolean} enableAutocomplete default false
26792  * @cfg {Boolean} enableReverseGeocode default true
26793  * @cfg {String} markerTitle
26794  * 
26795  * @constructor
26796  * Create a new LocationPicker
26797  * @param {Object} config The config object
26798  */
26799
26800
26801 Roo.bootstrap.LocationPicker = function(config){
26802     
26803     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26804     
26805     this.addEvents({
26806         /**
26807          * @event initial
26808          * Fires when the picker initialized.
26809          * @param {Roo.bootstrap.LocationPicker} this
26810          * @param {Google Location} location
26811          */
26812         initial : true,
26813         /**
26814          * @event positionchanged
26815          * Fires when the picker position changed.
26816          * @param {Roo.bootstrap.LocationPicker} this
26817          * @param {Google Location} location
26818          */
26819         positionchanged : true,
26820         /**
26821          * @event resize
26822          * Fires when the map resize.
26823          * @param {Roo.bootstrap.LocationPicker} this
26824          */
26825         resize : true,
26826         /**
26827          * @event show
26828          * Fires when the map show.
26829          * @param {Roo.bootstrap.LocationPicker} this
26830          */
26831         show : true,
26832         /**
26833          * @event hide
26834          * Fires when the map hide.
26835          * @param {Roo.bootstrap.LocationPicker} this
26836          */
26837         hide : true,
26838         /**
26839          * @event mapClick
26840          * Fires when click the map.
26841          * @param {Roo.bootstrap.LocationPicker} this
26842          * @param {Map event} e
26843          */
26844         mapClick : true,
26845         /**
26846          * @event mapRightClick
26847          * Fires when right click the map.
26848          * @param {Roo.bootstrap.LocationPicker} this
26849          * @param {Map event} e
26850          */
26851         mapRightClick : true,
26852         /**
26853          * @event markerClick
26854          * Fires when click the marker.
26855          * @param {Roo.bootstrap.LocationPicker} this
26856          * @param {Map event} e
26857          */
26858         markerClick : true,
26859         /**
26860          * @event markerRightClick
26861          * Fires when right click the marker.
26862          * @param {Roo.bootstrap.LocationPicker} this
26863          * @param {Map event} e
26864          */
26865         markerRightClick : true,
26866         /**
26867          * @event OverlayViewDraw
26868          * Fires when OverlayView Draw
26869          * @param {Roo.bootstrap.LocationPicker} this
26870          */
26871         OverlayViewDraw : true,
26872         /**
26873          * @event OverlayViewOnAdd
26874          * Fires when OverlayView Draw
26875          * @param {Roo.bootstrap.LocationPicker} this
26876          */
26877         OverlayViewOnAdd : true,
26878         /**
26879          * @event OverlayViewOnRemove
26880          * Fires when OverlayView Draw
26881          * @param {Roo.bootstrap.LocationPicker} this
26882          */
26883         OverlayViewOnRemove : true,
26884         /**
26885          * @event OverlayViewShow
26886          * Fires when OverlayView Draw
26887          * @param {Roo.bootstrap.LocationPicker} this
26888          * @param {Pixel} cpx
26889          */
26890         OverlayViewShow : true,
26891         /**
26892          * @event OverlayViewHide
26893          * Fires when OverlayView Draw
26894          * @param {Roo.bootstrap.LocationPicker} this
26895          */
26896         OverlayViewHide : true,
26897         /**
26898          * @event loadexception
26899          * Fires when load google lib failed.
26900          * @param {Roo.bootstrap.LocationPicker} this
26901          */
26902         loadexception : true
26903     });
26904         
26905 };
26906
26907 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26908     
26909     gMapContext: false,
26910     
26911     latitude: 0,
26912     longitude: 0,
26913     zoom: 15,
26914     mapTypeId: false,
26915     mapTypeControl: false,
26916     disableDoubleClickZoom: false,
26917     scrollwheel: true,
26918     streetViewControl: false,
26919     radius: 0,
26920     locationName: '',
26921     draggable: true,
26922     enableAutocomplete: false,
26923     enableReverseGeocode: true,
26924     markerTitle: '',
26925     
26926     getAutoCreate: function()
26927     {
26928
26929         var cfg = {
26930             tag: 'div',
26931             cls: 'roo-location-picker'
26932         };
26933         
26934         return cfg
26935     },
26936     
26937     initEvents: function(ct, position)
26938     {       
26939         if(!this.el.getWidth() || this.isApplied()){
26940             return;
26941         }
26942         
26943         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26944         
26945         this.initial();
26946     },
26947     
26948     initial: function()
26949     {
26950         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26951             this.fireEvent('loadexception', this);
26952             return;
26953         }
26954         
26955         if(!this.mapTypeId){
26956             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26957         }
26958         
26959         this.gMapContext = this.GMapContext();
26960         
26961         this.initOverlayView();
26962         
26963         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26964         
26965         var _this = this;
26966                 
26967         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26968             _this.setPosition(_this.gMapContext.marker.position);
26969         });
26970         
26971         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26972             _this.fireEvent('mapClick', this, event);
26973             
26974         });
26975
26976         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26977             _this.fireEvent('mapRightClick', this, event);
26978             
26979         });
26980         
26981         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26982             _this.fireEvent('markerClick', this, event);
26983             
26984         });
26985
26986         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26987             _this.fireEvent('markerRightClick', this, event);
26988             
26989         });
26990         
26991         this.setPosition(this.gMapContext.location);
26992         
26993         this.fireEvent('initial', this, this.gMapContext.location);
26994     },
26995     
26996     initOverlayView: function()
26997     {
26998         var _this = this;
26999         
27000         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27001             
27002             draw: function()
27003             {
27004                 _this.fireEvent('OverlayViewDraw', _this);
27005             },
27006             
27007             onAdd: function()
27008             {
27009                 _this.fireEvent('OverlayViewOnAdd', _this);
27010             },
27011             
27012             onRemove: function()
27013             {
27014                 _this.fireEvent('OverlayViewOnRemove', _this);
27015             },
27016             
27017             show: function(cpx)
27018             {
27019                 _this.fireEvent('OverlayViewShow', _this, cpx);
27020             },
27021             
27022             hide: function()
27023             {
27024                 _this.fireEvent('OverlayViewHide', _this);
27025             }
27026             
27027         });
27028     },
27029     
27030     fromLatLngToContainerPixel: function(event)
27031     {
27032         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27033     },
27034     
27035     isApplied: function() 
27036     {
27037         return this.getGmapContext() == false ? false : true;
27038     },
27039     
27040     getGmapContext: function() 
27041     {
27042         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27043     },
27044     
27045     GMapContext: function() 
27046     {
27047         var position = new google.maps.LatLng(this.latitude, this.longitude);
27048         
27049         var _map = new google.maps.Map(this.el.dom, {
27050             center: position,
27051             zoom: this.zoom,
27052             mapTypeId: this.mapTypeId,
27053             mapTypeControl: this.mapTypeControl,
27054             disableDoubleClickZoom: this.disableDoubleClickZoom,
27055             scrollwheel: this.scrollwheel,
27056             streetViewControl: this.streetViewControl,
27057             locationName: this.locationName,
27058             draggable: this.draggable,
27059             enableAutocomplete: this.enableAutocomplete,
27060             enableReverseGeocode: this.enableReverseGeocode
27061         });
27062         
27063         var _marker = new google.maps.Marker({
27064             position: position,
27065             map: _map,
27066             title: this.markerTitle,
27067             draggable: this.draggable
27068         });
27069         
27070         return {
27071             map: _map,
27072             marker: _marker,
27073             circle: null,
27074             location: position,
27075             radius: this.radius,
27076             locationName: this.locationName,
27077             addressComponents: {
27078                 formatted_address: null,
27079                 addressLine1: null,
27080                 addressLine2: null,
27081                 streetName: null,
27082                 streetNumber: null,
27083                 city: null,
27084                 district: null,
27085                 state: null,
27086                 stateOrProvince: null
27087             },
27088             settings: this,
27089             domContainer: this.el.dom,
27090             geodecoder: new google.maps.Geocoder()
27091         };
27092     },
27093     
27094     drawCircle: function(center, radius, options) 
27095     {
27096         if (this.gMapContext.circle != null) {
27097             this.gMapContext.circle.setMap(null);
27098         }
27099         if (radius > 0) {
27100             radius *= 1;
27101             options = Roo.apply({}, options, {
27102                 strokeColor: "#0000FF",
27103                 strokeOpacity: .35,
27104                 strokeWeight: 2,
27105                 fillColor: "#0000FF",
27106                 fillOpacity: .2
27107             });
27108             
27109             options.map = this.gMapContext.map;
27110             options.radius = radius;
27111             options.center = center;
27112             this.gMapContext.circle = new google.maps.Circle(options);
27113             return this.gMapContext.circle;
27114         }
27115         
27116         return null;
27117     },
27118     
27119     setPosition: function(location) 
27120     {
27121         this.gMapContext.location = location;
27122         this.gMapContext.marker.setPosition(location);
27123         this.gMapContext.map.panTo(location);
27124         this.drawCircle(location, this.gMapContext.radius, {});
27125         
27126         var _this = this;
27127         
27128         if (this.gMapContext.settings.enableReverseGeocode) {
27129             this.gMapContext.geodecoder.geocode({
27130                 latLng: this.gMapContext.location
27131             }, function(results, status) {
27132                 
27133                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27134                     _this.gMapContext.locationName = results[0].formatted_address;
27135                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27136                     
27137                     _this.fireEvent('positionchanged', this, location);
27138                 }
27139             });
27140             
27141             return;
27142         }
27143         
27144         this.fireEvent('positionchanged', this, location);
27145     },
27146     
27147     resize: function()
27148     {
27149         google.maps.event.trigger(this.gMapContext.map, "resize");
27150         
27151         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27152         
27153         this.fireEvent('resize', this);
27154     },
27155     
27156     setPositionByLatLng: function(latitude, longitude)
27157     {
27158         this.setPosition(new google.maps.LatLng(latitude, longitude));
27159     },
27160     
27161     getCurrentPosition: function() 
27162     {
27163         return {
27164             latitude: this.gMapContext.location.lat(),
27165             longitude: this.gMapContext.location.lng()
27166         };
27167     },
27168     
27169     getAddressName: function() 
27170     {
27171         return this.gMapContext.locationName;
27172     },
27173     
27174     getAddressComponents: function() 
27175     {
27176         return this.gMapContext.addressComponents;
27177     },
27178     
27179     address_component_from_google_geocode: function(address_components) 
27180     {
27181         var result = {};
27182         
27183         for (var i = 0; i < address_components.length; i++) {
27184             var component = address_components[i];
27185             if (component.types.indexOf("postal_code") >= 0) {
27186                 result.postalCode = component.short_name;
27187             } else if (component.types.indexOf("street_number") >= 0) {
27188                 result.streetNumber = component.short_name;
27189             } else if (component.types.indexOf("route") >= 0) {
27190                 result.streetName = component.short_name;
27191             } else if (component.types.indexOf("neighborhood") >= 0) {
27192                 result.city = component.short_name;
27193             } else if (component.types.indexOf("locality") >= 0) {
27194                 result.city = component.short_name;
27195             } else if (component.types.indexOf("sublocality") >= 0) {
27196                 result.district = component.short_name;
27197             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27198                 result.stateOrProvince = component.short_name;
27199             } else if (component.types.indexOf("country") >= 0) {
27200                 result.country = component.short_name;
27201             }
27202         }
27203         
27204         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27205         result.addressLine2 = "";
27206         return result;
27207     },
27208     
27209     setZoomLevel: function(zoom)
27210     {
27211         this.gMapContext.map.setZoom(zoom);
27212     },
27213     
27214     show: function()
27215     {
27216         if(!this.el){
27217             return;
27218         }
27219         
27220         this.el.show();
27221         
27222         this.resize();
27223         
27224         this.fireEvent('show', this);
27225     },
27226     
27227     hide: function()
27228     {
27229         if(!this.el){
27230             return;
27231         }
27232         
27233         this.el.hide();
27234         
27235         this.fireEvent('hide', this);
27236     }
27237     
27238 });
27239
27240 Roo.apply(Roo.bootstrap.LocationPicker, {
27241     
27242     OverlayView : function(map, options)
27243     {
27244         options = options || {};
27245         
27246         this.setMap(map);
27247     }
27248     
27249     
27250 });/**
27251  * @class Roo.bootstrap.Alert
27252  * @extends Roo.bootstrap.Component
27253  * Bootstrap Alert class - shows an alert area box
27254  * eg
27255  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27256   Enter a valid email address
27257 </div>
27258  * @licence LGPL
27259  * @cfg {String} title The title of alert
27260  * @cfg {String} html The content of alert
27261  * @cfg {String} weight (  success | info | warning | danger )
27262  * @cfg {String} faicon font-awesomeicon
27263  * 
27264  * @constructor
27265  * Create a new alert
27266  * @param {Object} config The config object
27267  */
27268
27269
27270 Roo.bootstrap.Alert = function(config){
27271     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27272     
27273 };
27274
27275 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27276     
27277     title: '',
27278     html: '',
27279     weight: false,
27280     faicon: false,
27281     
27282     getAutoCreate : function()
27283     {
27284         
27285         var cfg = {
27286             tag : 'div',
27287             cls : 'alert',
27288             cn : [
27289                 {
27290                     tag : 'i',
27291                     cls : 'roo-alert-icon'
27292                     
27293                 },
27294                 {
27295                     tag : 'b',
27296                     cls : 'roo-alert-title',
27297                     html : this.title
27298                 },
27299                 {
27300                     tag : 'span',
27301                     cls : 'roo-alert-text',
27302                     html : this.html
27303                 }
27304             ]
27305         };
27306         
27307         if(this.faicon){
27308             cfg.cn[0].cls += ' fa ' + this.faicon;
27309         }
27310         
27311         if(this.weight){
27312             cfg.cls += ' alert-' + this.weight;
27313         }
27314         
27315         return cfg;
27316     },
27317     
27318     initEvents: function() 
27319     {
27320         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27321     },
27322     
27323     setTitle : function(str)
27324     {
27325         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27326     },
27327     
27328     setText : function(str)
27329     {
27330         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27331     },
27332     
27333     setWeight : function(weight)
27334     {
27335         if(this.weight){
27336             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27337         }
27338         
27339         this.weight = weight;
27340         
27341         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27342     },
27343     
27344     setIcon : function(icon)
27345     {
27346         if(this.faicon){
27347             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27348         }
27349         
27350         this.faicon = icon;
27351         
27352         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27353     },
27354     
27355     hide: function() 
27356     {
27357         this.el.hide();   
27358     },
27359     
27360     show: function() 
27361     {  
27362         this.el.show();   
27363     }
27364     
27365 });
27366
27367  
27368 /*
27369 * Licence: LGPL
27370 */
27371
27372 /**
27373  * @class Roo.bootstrap.UploadCropbox
27374  * @extends Roo.bootstrap.Component
27375  * Bootstrap UploadCropbox class
27376  * @cfg {String} emptyText show when image has been loaded
27377  * @cfg {String} rotateNotify show when image too small to rotate
27378  * @cfg {Number} errorTimeout default 3000
27379  * @cfg {Number} minWidth default 300
27380  * @cfg {Number} minHeight default 300
27381  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27382  * @cfg {Boolean} isDocument (true|false) default false
27383  * @cfg {String} url action url
27384  * @cfg {String} paramName default 'imageUpload'
27385  * @cfg {String} method default POST
27386  * @cfg {Boolean} loadMask (true|false) default true
27387  * @cfg {Boolean} loadingText default 'Loading...'
27388  * 
27389  * @constructor
27390  * Create a new UploadCropbox
27391  * @param {Object} config The config object
27392  */
27393
27394 Roo.bootstrap.UploadCropbox = function(config){
27395     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27396     
27397     this.addEvents({
27398         /**
27399          * @event beforeselectfile
27400          * Fire before select file
27401          * @param {Roo.bootstrap.UploadCropbox} this
27402          */
27403         "beforeselectfile" : true,
27404         /**
27405          * @event initial
27406          * Fire after initEvent
27407          * @param {Roo.bootstrap.UploadCropbox} this
27408          */
27409         "initial" : true,
27410         /**
27411          * @event crop
27412          * Fire after initEvent
27413          * @param {Roo.bootstrap.UploadCropbox} this
27414          * @param {String} data
27415          */
27416         "crop" : true,
27417         /**
27418          * @event prepare
27419          * Fire when preparing the file data
27420          * @param {Roo.bootstrap.UploadCropbox} this
27421          * @param {Object} file
27422          */
27423         "prepare" : true,
27424         /**
27425          * @event exception
27426          * Fire when get exception
27427          * @param {Roo.bootstrap.UploadCropbox} this
27428          * @param {XMLHttpRequest} xhr
27429          */
27430         "exception" : true,
27431         /**
27432          * @event beforeloadcanvas
27433          * Fire before load the canvas
27434          * @param {Roo.bootstrap.UploadCropbox} this
27435          * @param {String} src
27436          */
27437         "beforeloadcanvas" : true,
27438         /**
27439          * @event trash
27440          * Fire when trash image
27441          * @param {Roo.bootstrap.UploadCropbox} this
27442          */
27443         "trash" : true,
27444         /**
27445          * @event download
27446          * Fire when download the image
27447          * @param {Roo.bootstrap.UploadCropbox} this
27448          */
27449         "download" : true,
27450         /**
27451          * @event footerbuttonclick
27452          * Fire when footerbuttonclick
27453          * @param {Roo.bootstrap.UploadCropbox} this
27454          * @param {String} type
27455          */
27456         "footerbuttonclick" : true,
27457         /**
27458          * @event resize
27459          * Fire when resize
27460          * @param {Roo.bootstrap.UploadCropbox} this
27461          */
27462         "resize" : true,
27463         /**
27464          * @event rotate
27465          * Fire when rotate the image
27466          * @param {Roo.bootstrap.UploadCropbox} this
27467          * @param {String} pos
27468          */
27469         "rotate" : true,
27470         /**
27471          * @event inspect
27472          * Fire when inspect the file
27473          * @param {Roo.bootstrap.UploadCropbox} this
27474          * @param {Object} file
27475          */
27476         "inspect" : true,
27477         /**
27478          * @event upload
27479          * Fire when xhr upload the file
27480          * @param {Roo.bootstrap.UploadCropbox} this
27481          * @param {Object} data
27482          */
27483         "upload" : true,
27484         /**
27485          * @event arrange
27486          * Fire when arrange the file data
27487          * @param {Roo.bootstrap.UploadCropbox} this
27488          * @param {Object} formData
27489          */
27490         "arrange" : true
27491     });
27492     
27493     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27494 };
27495
27496 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27497     
27498     emptyText : 'Click to upload image',
27499     rotateNotify : 'Image is too small to rotate',
27500     errorTimeout : 3000,
27501     scale : 0,
27502     baseScale : 1,
27503     rotate : 0,
27504     dragable : false,
27505     pinching : false,
27506     mouseX : 0,
27507     mouseY : 0,
27508     cropData : false,
27509     minWidth : 300,
27510     minHeight : 300,
27511     file : false,
27512     exif : {},
27513     baseRotate : 1,
27514     cropType : 'image/jpeg',
27515     buttons : false,
27516     canvasLoaded : false,
27517     isDocument : false,
27518     method : 'POST',
27519     paramName : 'imageUpload',
27520     loadMask : true,
27521     loadingText : 'Loading...',
27522     maskEl : false,
27523     
27524     getAutoCreate : function()
27525     {
27526         var cfg = {
27527             tag : 'div',
27528             cls : 'roo-upload-cropbox',
27529             cn : [
27530                 {
27531                     tag : 'input',
27532                     cls : 'roo-upload-cropbox-selector',
27533                     type : 'file'
27534                 },
27535                 {
27536                     tag : 'div',
27537                     cls : 'roo-upload-cropbox-body',
27538                     style : 'cursor:pointer',
27539                     cn : [
27540                         {
27541                             tag : 'div',
27542                             cls : 'roo-upload-cropbox-preview'
27543                         },
27544                         {
27545                             tag : 'div',
27546                             cls : 'roo-upload-cropbox-thumb'
27547                         },
27548                         {
27549                             tag : 'div',
27550                             cls : 'roo-upload-cropbox-empty-notify',
27551                             html : this.emptyText
27552                         },
27553                         {
27554                             tag : 'div',
27555                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27556                             html : this.rotateNotify
27557                         }
27558                     ]
27559                 },
27560                 {
27561                     tag : 'div',
27562                     cls : 'roo-upload-cropbox-footer',
27563                     cn : {
27564                         tag : 'div',
27565                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27566                         cn : []
27567                     }
27568                 }
27569             ]
27570         };
27571         
27572         return cfg;
27573     },
27574     
27575     onRender : function(ct, position)
27576     {
27577         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27578         
27579         if (this.buttons.length) {
27580             
27581             Roo.each(this.buttons, function(bb) {
27582                 
27583                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27584                 
27585                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27586                 
27587             }, this);
27588         }
27589         
27590         if(this.loadMask){
27591             this.maskEl = this.el;
27592         }
27593     },
27594     
27595     initEvents : function()
27596     {
27597         this.urlAPI = (window.createObjectURL && window) || 
27598                                 (window.URL && URL.revokeObjectURL && URL) || 
27599                                 (window.webkitURL && webkitURL);
27600                         
27601         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27602         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27603         
27604         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27605         this.selectorEl.hide();
27606         
27607         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27608         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27609         
27610         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27611         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27612         this.thumbEl.hide();
27613         
27614         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27615         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27616         
27617         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27618         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27619         this.errorEl.hide();
27620         
27621         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27622         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27623         this.footerEl.hide();
27624         
27625         this.setThumbBoxSize();
27626         
27627         this.bind();
27628         
27629         this.resize();
27630         
27631         this.fireEvent('initial', this);
27632     },
27633
27634     bind : function()
27635     {
27636         var _this = this;
27637         
27638         window.addEventListener("resize", function() { _this.resize(); } );
27639         
27640         this.bodyEl.on('click', this.beforeSelectFile, this);
27641         
27642         if(Roo.isTouch){
27643             this.bodyEl.on('touchstart', this.onTouchStart, this);
27644             this.bodyEl.on('touchmove', this.onTouchMove, this);
27645             this.bodyEl.on('touchend', this.onTouchEnd, this);
27646         }
27647         
27648         if(!Roo.isTouch){
27649             this.bodyEl.on('mousedown', this.onMouseDown, this);
27650             this.bodyEl.on('mousemove', this.onMouseMove, this);
27651             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27652             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27653             Roo.get(document).on('mouseup', this.onMouseUp, this);
27654         }
27655         
27656         this.selectorEl.on('change', this.onFileSelected, this);
27657     },
27658     
27659     reset : function()
27660     {    
27661         this.scale = 0;
27662         this.baseScale = 1;
27663         this.rotate = 0;
27664         this.baseRotate = 1;
27665         this.dragable = false;
27666         this.pinching = false;
27667         this.mouseX = 0;
27668         this.mouseY = 0;
27669         this.cropData = false;
27670         this.notifyEl.dom.innerHTML = this.emptyText;
27671         
27672         this.selectorEl.dom.value = '';
27673         
27674     },
27675     
27676     resize : function()
27677     {
27678         if(this.fireEvent('resize', this) != false){
27679             this.setThumbBoxPosition();
27680             this.setCanvasPosition();
27681         }
27682     },
27683     
27684     onFooterButtonClick : function(e, el, o, type)
27685     {
27686         switch (type) {
27687             case 'rotate-left' :
27688                 this.onRotateLeft(e);
27689                 break;
27690             case 'rotate-right' :
27691                 this.onRotateRight(e);
27692                 break;
27693             case 'picture' :
27694                 this.beforeSelectFile(e);
27695                 break;
27696             case 'trash' :
27697                 this.trash(e);
27698                 break;
27699             case 'crop' :
27700                 this.crop(e);
27701                 break;
27702             case 'download' :
27703                 this.download(e);
27704                 break;
27705             default :
27706                 break;
27707         }
27708         
27709         this.fireEvent('footerbuttonclick', this, type);
27710     },
27711     
27712     beforeSelectFile : function(e)
27713     {
27714         e.preventDefault();
27715         
27716         if(this.fireEvent('beforeselectfile', this) != false){
27717             this.selectorEl.dom.click();
27718         }
27719     },
27720     
27721     onFileSelected : function(e)
27722     {
27723         e.preventDefault();
27724         
27725         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27726             return;
27727         }
27728         
27729         var file = this.selectorEl.dom.files[0];
27730         
27731         if(this.fireEvent('inspect', this, file) != false){
27732             this.prepare(file);
27733         }
27734         
27735     },
27736     
27737     trash : function(e)
27738     {
27739         this.fireEvent('trash', this);
27740     },
27741     
27742     download : function(e)
27743     {
27744         this.fireEvent('download', this);
27745     },
27746     
27747     loadCanvas : function(src)
27748     {   
27749         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27750             
27751             this.reset();
27752             
27753             this.imageEl = document.createElement('img');
27754             
27755             var _this = this;
27756             
27757             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27758             
27759             this.imageEl.src = src;
27760         }
27761     },
27762     
27763     onLoadCanvas : function()
27764     {   
27765         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27766         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27767         
27768         this.bodyEl.un('click', this.beforeSelectFile, this);
27769         
27770         this.notifyEl.hide();
27771         this.thumbEl.show();
27772         this.footerEl.show();
27773         
27774         this.baseRotateLevel();
27775         
27776         if(this.isDocument){
27777             this.setThumbBoxSize();
27778         }
27779         
27780         this.setThumbBoxPosition();
27781         
27782         this.baseScaleLevel();
27783         
27784         this.draw();
27785         
27786         this.resize();
27787         
27788         this.canvasLoaded = true;
27789         
27790         if(this.loadMask){
27791             this.maskEl.unmask();
27792         }
27793         
27794     },
27795     
27796     setCanvasPosition : function()
27797     {   
27798         if(!this.canvasEl){
27799             return;
27800         }
27801         
27802         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27803         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27804         
27805         this.previewEl.setLeft(pw);
27806         this.previewEl.setTop(ph);
27807         
27808     },
27809     
27810     onMouseDown : function(e)
27811     {   
27812         e.stopEvent();
27813         
27814         this.dragable = true;
27815         this.pinching = false;
27816         
27817         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27818             this.dragable = false;
27819             return;
27820         }
27821         
27822         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27823         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27824         
27825     },
27826     
27827     onMouseMove : function(e)
27828     {   
27829         e.stopEvent();
27830         
27831         if(!this.canvasLoaded){
27832             return;
27833         }
27834         
27835         if (!this.dragable){
27836             return;
27837         }
27838         
27839         var minX = Math.ceil(this.thumbEl.getLeft(true));
27840         var minY = Math.ceil(this.thumbEl.getTop(true));
27841         
27842         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27843         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27844         
27845         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27846         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27847         
27848         x = x - this.mouseX;
27849         y = y - this.mouseY;
27850         
27851         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27852         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27853         
27854         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27855         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27856         
27857         this.previewEl.setLeft(bgX);
27858         this.previewEl.setTop(bgY);
27859         
27860         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27861         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27862     },
27863     
27864     onMouseUp : function(e)
27865     {   
27866         e.stopEvent();
27867         
27868         this.dragable = false;
27869     },
27870     
27871     onMouseWheel : function(e)
27872     {   
27873         e.stopEvent();
27874         
27875         this.startScale = this.scale;
27876         
27877         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27878         
27879         if(!this.zoomable()){
27880             this.scale = this.startScale;
27881             return;
27882         }
27883         
27884         this.draw();
27885         
27886         return;
27887     },
27888     
27889     zoomable : function()
27890     {
27891         var minScale = this.thumbEl.getWidth() / this.minWidth;
27892         
27893         if(this.minWidth < this.minHeight){
27894             minScale = this.thumbEl.getHeight() / this.minHeight;
27895         }
27896         
27897         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27898         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27899         
27900         if(
27901                 this.isDocument &&
27902                 (this.rotate == 0 || this.rotate == 180) && 
27903                 (
27904                     width > this.imageEl.OriginWidth || 
27905                     height > this.imageEl.OriginHeight ||
27906                     (width < this.minWidth && height < this.minHeight)
27907                 )
27908         ){
27909             return false;
27910         }
27911         
27912         if(
27913                 this.isDocument &&
27914                 (this.rotate == 90 || this.rotate == 270) && 
27915                 (
27916                     width > this.imageEl.OriginWidth || 
27917                     height > this.imageEl.OriginHeight ||
27918                     (width < this.minHeight && height < this.minWidth)
27919                 )
27920         ){
27921             return false;
27922         }
27923         
27924         if(
27925                 !this.isDocument &&
27926                 (this.rotate == 0 || this.rotate == 180) && 
27927                 (
27928                     width < this.minWidth || 
27929                     width > this.imageEl.OriginWidth || 
27930                     height < this.minHeight || 
27931                     height > this.imageEl.OriginHeight
27932                 )
27933         ){
27934             return false;
27935         }
27936         
27937         if(
27938                 !this.isDocument &&
27939                 (this.rotate == 90 || this.rotate == 270) && 
27940                 (
27941                     width < this.minHeight || 
27942                     width > this.imageEl.OriginWidth || 
27943                     height < this.minWidth || 
27944                     height > this.imageEl.OriginHeight
27945                 )
27946         ){
27947             return false;
27948         }
27949         
27950         return true;
27951         
27952     },
27953     
27954     onRotateLeft : function(e)
27955     {   
27956         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27957             
27958             var minScale = this.thumbEl.getWidth() / this.minWidth;
27959             
27960             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27961             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27962             
27963             this.startScale = this.scale;
27964             
27965             while (this.getScaleLevel() < minScale){
27966             
27967                 this.scale = this.scale + 1;
27968                 
27969                 if(!this.zoomable()){
27970                     break;
27971                 }
27972                 
27973                 if(
27974                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27975                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27976                 ){
27977                     continue;
27978                 }
27979                 
27980                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27981
27982                 this.draw();
27983                 
27984                 return;
27985             }
27986             
27987             this.scale = this.startScale;
27988             
27989             this.onRotateFail();
27990             
27991             return false;
27992         }
27993         
27994         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27995
27996         if(this.isDocument){
27997             this.setThumbBoxSize();
27998             this.setThumbBoxPosition();
27999             this.setCanvasPosition();
28000         }
28001         
28002         this.draw();
28003         
28004         this.fireEvent('rotate', this, 'left');
28005         
28006     },
28007     
28008     onRotateRight : function(e)
28009     {
28010         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28011             
28012             var minScale = this.thumbEl.getWidth() / this.minWidth;
28013         
28014             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28015             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28016             
28017             this.startScale = this.scale;
28018             
28019             while (this.getScaleLevel() < minScale){
28020             
28021                 this.scale = this.scale + 1;
28022                 
28023                 if(!this.zoomable()){
28024                     break;
28025                 }
28026                 
28027                 if(
28028                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28029                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28030                 ){
28031                     continue;
28032                 }
28033                 
28034                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28035
28036                 this.draw();
28037                 
28038                 return;
28039             }
28040             
28041             this.scale = this.startScale;
28042             
28043             this.onRotateFail();
28044             
28045             return false;
28046         }
28047         
28048         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28049
28050         if(this.isDocument){
28051             this.setThumbBoxSize();
28052             this.setThumbBoxPosition();
28053             this.setCanvasPosition();
28054         }
28055         
28056         this.draw();
28057         
28058         this.fireEvent('rotate', this, 'right');
28059     },
28060     
28061     onRotateFail : function()
28062     {
28063         this.errorEl.show(true);
28064         
28065         var _this = this;
28066         
28067         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28068     },
28069     
28070     draw : function()
28071     {
28072         this.previewEl.dom.innerHTML = '';
28073         
28074         var canvasEl = document.createElement("canvas");
28075         
28076         var contextEl = canvasEl.getContext("2d");
28077         
28078         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28079         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28080         var center = this.imageEl.OriginWidth / 2;
28081         
28082         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28083             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28084             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28085             center = this.imageEl.OriginHeight / 2;
28086         }
28087         
28088         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28089         
28090         contextEl.translate(center, center);
28091         contextEl.rotate(this.rotate * Math.PI / 180);
28092
28093         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28094         
28095         this.canvasEl = document.createElement("canvas");
28096         
28097         this.contextEl = this.canvasEl.getContext("2d");
28098         
28099         switch (this.rotate) {
28100             case 0 :
28101                 
28102                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28103                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28104                 
28105                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28106                 
28107                 break;
28108             case 90 : 
28109                 
28110                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28111                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28112                 
28113                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28114                     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);
28115                     break;
28116                 }
28117                 
28118                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28119                 
28120                 break;
28121             case 180 :
28122                 
28123                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28124                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28125                 
28126                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28127                     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);
28128                     break;
28129                 }
28130                 
28131                 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);
28132                 
28133                 break;
28134             case 270 :
28135                 
28136                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28137                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28138         
28139                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28140                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28141                     break;
28142                 }
28143                 
28144                 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);
28145                 
28146                 break;
28147             default : 
28148                 break;
28149         }
28150         
28151         this.previewEl.appendChild(this.canvasEl);
28152         
28153         this.setCanvasPosition();
28154     },
28155     
28156     crop : function()
28157     {
28158         if(!this.canvasLoaded){
28159             return;
28160         }
28161         
28162         var imageCanvas = document.createElement("canvas");
28163         
28164         var imageContext = imageCanvas.getContext("2d");
28165         
28166         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28167         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28168         
28169         var center = imageCanvas.width / 2;
28170         
28171         imageContext.translate(center, center);
28172         
28173         imageContext.rotate(this.rotate * Math.PI / 180);
28174         
28175         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28176         
28177         var canvas = document.createElement("canvas");
28178         
28179         var context = canvas.getContext("2d");
28180                 
28181         canvas.width = this.minWidth;
28182         canvas.height = this.minHeight;
28183
28184         switch (this.rotate) {
28185             case 0 :
28186                 
28187                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28188                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28189                 
28190                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28191                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28192                 
28193                 var targetWidth = this.minWidth - 2 * x;
28194                 var targetHeight = this.minHeight - 2 * y;
28195                 
28196                 var scale = 1;
28197                 
28198                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28199                     scale = targetWidth / width;
28200                 }
28201                 
28202                 if(x > 0 && y == 0){
28203                     scale = targetHeight / height;
28204                 }
28205                 
28206                 if(x > 0 && y > 0){
28207                     scale = targetWidth / width;
28208                     
28209                     if(width < height){
28210                         scale = targetHeight / height;
28211                     }
28212                 }
28213                 
28214                 context.scale(scale, scale);
28215                 
28216                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28217                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28218
28219                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28220                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28221
28222                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28223                 
28224                 break;
28225             case 90 : 
28226                 
28227                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28228                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28229                 
28230                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28231                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28232                 
28233                 var targetWidth = this.minWidth - 2 * x;
28234                 var targetHeight = this.minHeight - 2 * y;
28235                 
28236                 var scale = 1;
28237                 
28238                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28239                     scale = targetWidth / width;
28240                 }
28241                 
28242                 if(x > 0 && y == 0){
28243                     scale = targetHeight / height;
28244                 }
28245                 
28246                 if(x > 0 && y > 0){
28247                     scale = targetWidth / width;
28248                     
28249                     if(width < height){
28250                         scale = targetHeight / height;
28251                     }
28252                 }
28253                 
28254                 context.scale(scale, scale);
28255                 
28256                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28257                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28258
28259                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28260                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28261                 
28262                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28263                 
28264                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28265                 
28266                 break;
28267             case 180 :
28268                 
28269                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28270                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28271                 
28272                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28273                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28274                 
28275                 var targetWidth = this.minWidth - 2 * x;
28276                 var targetHeight = this.minHeight - 2 * y;
28277                 
28278                 var scale = 1;
28279                 
28280                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28281                     scale = targetWidth / width;
28282                 }
28283                 
28284                 if(x > 0 && y == 0){
28285                     scale = targetHeight / height;
28286                 }
28287                 
28288                 if(x > 0 && y > 0){
28289                     scale = targetWidth / width;
28290                     
28291                     if(width < height){
28292                         scale = targetHeight / height;
28293                     }
28294                 }
28295                 
28296                 context.scale(scale, scale);
28297                 
28298                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28299                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28300
28301                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28302                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28303
28304                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28305                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28306                 
28307                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28308                 
28309                 break;
28310             case 270 :
28311                 
28312                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28313                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28314                 
28315                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28316                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28317                 
28318                 var targetWidth = this.minWidth - 2 * x;
28319                 var targetHeight = this.minHeight - 2 * y;
28320                 
28321                 var scale = 1;
28322                 
28323                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28324                     scale = targetWidth / width;
28325                 }
28326                 
28327                 if(x > 0 && y == 0){
28328                     scale = targetHeight / height;
28329                 }
28330                 
28331                 if(x > 0 && y > 0){
28332                     scale = targetWidth / width;
28333                     
28334                     if(width < height){
28335                         scale = targetHeight / height;
28336                     }
28337                 }
28338                 
28339                 context.scale(scale, scale);
28340                 
28341                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28342                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28343
28344                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28345                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28346                 
28347                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28348                 
28349                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28350                 
28351                 break;
28352             default : 
28353                 break;
28354         }
28355         
28356         this.cropData = canvas.toDataURL(this.cropType);
28357         
28358         if(this.fireEvent('crop', this, this.cropData) !== false){
28359             this.process(this.file, this.cropData);
28360         }
28361         
28362         return;
28363         
28364     },
28365     
28366     setThumbBoxSize : function()
28367     {
28368         var width, height;
28369         
28370         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28371             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28372             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28373             
28374             this.minWidth = width;
28375             this.minHeight = height;
28376             
28377             if(this.rotate == 90 || this.rotate == 270){
28378                 this.minWidth = height;
28379                 this.minHeight = width;
28380             }
28381         }
28382         
28383         height = 300;
28384         width = Math.ceil(this.minWidth * height / this.minHeight);
28385         
28386         if(this.minWidth > this.minHeight){
28387             width = 300;
28388             height = Math.ceil(this.minHeight * width / this.minWidth);
28389         }
28390         
28391         this.thumbEl.setStyle({
28392             width : width + 'px',
28393             height : height + 'px'
28394         });
28395
28396         return;
28397             
28398     },
28399     
28400     setThumbBoxPosition : function()
28401     {
28402         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28403         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28404         
28405         this.thumbEl.setLeft(x);
28406         this.thumbEl.setTop(y);
28407         
28408     },
28409     
28410     baseRotateLevel : function()
28411     {
28412         this.baseRotate = 1;
28413         
28414         if(
28415                 typeof(this.exif) != 'undefined' &&
28416                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28417                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28418         ){
28419             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28420         }
28421         
28422         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28423         
28424     },
28425     
28426     baseScaleLevel : function()
28427     {
28428         var width, height;
28429         
28430         if(this.isDocument){
28431             
28432             if(this.baseRotate == 6 || this.baseRotate == 8){
28433             
28434                 height = this.thumbEl.getHeight();
28435                 this.baseScale = height / this.imageEl.OriginWidth;
28436
28437                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28438                     width = this.thumbEl.getWidth();
28439                     this.baseScale = width / this.imageEl.OriginHeight;
28440                 }
28441
28442                 return;
28443             }
28444
28445             height = this.thumbEl.getHeight();
28446             this.baseScale = height / this.imageEl.OriginHeight;
28447
28448             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28449                 width = this.thumbEl.getWidth();
28450                 this.baseScale = width / this.imageEl.OriginWidth;
28451             }
28452
28453             return;
28454         }
28455         
28456         if(this.baseRotate == 6 || this.baseRotate == 8){
28457             
28458             width = this.thumbEl.getHeight();
28459             this.baseScale = width / this.imageEl.OriginHeight;
28460             
28461             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28462                 height = this.thumbEl.getWidth();
28463                 this.baseScale = height / this.imageEl.OriginHeight;
28464             }
28465             
28466             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28467                 height = this.thumbEl.getWidth();
28468                 this.baseScale = height / this.imageEl.OriginHeight;
28469                 
28470                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28471                     width = this.thumbEl.getHeight();
28472                     this.baseScale = width / this.imageEl.OriginWidth;
28473                 }
28474             }
28475             
28476             return;
28477         }
28478         
28479         width = this.thumbEl.getWidth();
28480         this.baseScale = width / this.imageEl.OriginWidth;
28481         
28482         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28483             height = this.thumbEl.getHeight();
28484             this.baseScale = height / this.imageEl.OriginHeight;
28485         }
28486         
28487         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28488             
28489             height = this.thumbEl.getHeight();
28490             this.baseScale = height / this.imageEl.OriginHeight;
28491             
28492             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28493                 width = this.thumbEl.getWidth();
28494                 this.baseScale = width / this.imageEl.OriginWidth;
28495             }
28496             
28497         }
28498         
28499         return;
28500     },
28501     
28502     getScaleLevel : function()
28503     {
28504         return this.baseScale * Math.pow(1.1, this.scale);
28505     },
28506     
28507     onTouchStart : function(e)
28508     {
28509         if(!this.canvasLoaded){
28510             this.beforeSelectFile(e);
28511             return;
28512         }
28513         
28514         var touches = e.browserEvent.touches;
28515         
28516         if(!touches){
28517             return;
28518         }
28519         
28520         if(touches.length == 1){
28521             this.onMouseDown(e);
28522             return;
28523         }
28524         
28525         if(touches.length != 2){
28526             return;
28527         }
28528         
28529         var coords = [];
28530         
28531         for(var i = 0, finger; finger = touches[i]; i++){
28532             coords.push(finger.pageX, finger.pageY);
28533         }
28534         
28535         var x = Math.pow(coords[0] - coords[2], 2);
28536         var y = Math.pow(coords[1] - coords[3], 2);
28537         
28538         this.startDistance = Math.sqrt(x + y);
28539         
28540         this.startScale = this.scale;
28541         
28542         this.pinching = true;
28543         this.dragable = false;
28544         
28545     },
28546     
28547     onTouchMove : function(e)
28548     {
28549         if(!this.pinching && !this.dragable){
28550             return;
28551         }
28552         
28553         var touches = e.browserEvent.touches;
28554         
28555         if(!touches){
28556             return;
28557         }
28558         
28559         if(this.dragable){
28560             this.onMouseMove(e);
28561             return;
28562         }
28563         
28564         var coords = [];
28565         
28566         for(var i = 0, finger; finger = touches[i]; i++){
28567             coords.push(finger.pageX, finger.pageY);
28568         }
28569         
28570         var x = Math.pow(coords[0] - coords[2], 2);
28571         var y = Math.pow(coords[1] - coords[3], 2);
28572         
28573         this.endDistance = Math.sqrt(x + y);
28574         
28575         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28576         
28577         if(!this.zoomable()){
28578             this.scale = this.startScale;
28579             return;
28580         }
28581         
28582         this.draw();
28583         
28584     },
28585     
28586     onTouchEnd : function(e)
28587     {
28588         this.pinching = false;
28589         this.dragable = false;
28590         
28591     },
28592     
28593     process : function(file, crop)
28594     {
28595         if(this.loadMask){
28596             this.maskEl.mask(this.loadingText);
28597         }
28598         
28599         this.xhr = new XMLHttpRequest();
28600         
28601         file.xhr = this.xhr;
28602
28603         this.xhr.open(this.method, this.url, true);
28604         
28605         var headers = {
28606             "Accept": "application/json",
28607             "Cache-Control": "no-cache",
28608             "X-Requested-With": "XMLHttpRequest"
28609         };
28610         
28611         for (var headerName in headers) {
28612             var headerValue = headers[headerName];
28613             if (headerValue) {
28614                 this.xhr.setRequestHeader(headerName, headerValue);
28615             }
28616         }
28617         
28618         var _this = this;
28619         
28620         this.xhr.onload = function()
28621         {
28622             _this.xhrOnLoad(_this.xhr);
28623         }
28624         
28625         this.xhr.onerror = function()
28626         {
28627             _this.xhrOnError(_this.xhr);
28628         }
28629         
28630         var formData = new FormData();
28631
28632         formData.append('returnHTML', 'NO');
28633         
28634         if(crop){
28635             formData.append('crop', crop);
28636         }
28637         
28638         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28639             formData.append(this.paramName, file, file.name);
28640         }
28641         
28642         if(typeof(file.filename) != 'undefined'){
28643             formData.append('filename', file.filename);
28644         }
28645         
28646         if(typeof(file.mimetype) != 'undefined'){
28647             formData.append('mimetype', file.mimetype);
28648         }
28649         
28650         if(this.fireEvent('arrange', this, formData) != false){
28651             this.xhr.send(formData);
28652         };
28653     },
28654     
28655     xhrOnLoad : function(xhr)
28656     {
28657         if(this.loadMask){
28658             this.maskEl.unmask();
28659         }
28660         
28661         if (xhr.readyState !== 4) {
28662             this.fireEvent('exception', this, xhr);
28663             return;
28664         }
28665
28666         var response = Roo.decode(xhr.responseText);
28667         
28668         if(!response.success){
28669             this.fireEvent('exception', this, xhr);
28670             return;
28671         }
28672         
28673         var response = Roo.decode(xhr.responseText);
28674         
28675         this.fireEvent('upload', this, response);
28676         
28677     },
28678     
28679     xhrOnError : function()
28680     {
28681         if(this.loadMask){
28682             this.maskEl.unmask();
28683         }
28684         
28685         Roo.log('xhr on error');
28686         
28687         var response = Roo.decode(xhr.responseText);
28688           
28689         Roo.log(response);
28690         
28691     },
28692     
28693     prepare : function(file)
28694     {   
28695         if(this.loadMask){
28696             this.maskEl.mask(this.loadingText);
28697         }
28698         
28699         this.file = false;
28700         this.exif = {};
28701         
28702         if(typeof(file) === 'string'){
28703             this.loadCanvas(file);
28704             return;
28705         }
28706         
28707         if(!file || !this.urlAPI){
28708             return;
28709         }
28710         
28711         this.file = file;
28712         this.cropType = file.type;
28713         
28714         var _this = this;
28715         
28716         if(this.fireEvent('prepare', this, this.file) != false){
28717             
28718             var reader = new FileReader();
28719             
28720             reader.onload = function (e) {
28721                 if (e.target.error) {
28722                     Roo.log(e.target.error);
28723                     return;
28724                 }
28725                 
28726                 var buffer = e.target.result,
28727                     dataView = new DataView(buffer),
28728                     offset = 2,
28729                     maxOffset = dataView.byteLength - 4,
28730                     markerBytes,
28731                     markerLength;
28732                 
28733                 if (dataView.getUint16(0) === 0xffd8) {
28734                     while (offset < maxOffset) {
28735                         markerBytes = dataView.getUint16(offset);
28736                         
28737                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28738                             markerLength = dataView.getUint16(offset + 2) + 2;
28739                             if (offset + markerLength > dataView.byteLength) {
28740                                 Roo.log('Invalid meta data: Invalid segment size.');
28741                                 break;
28742                             }
28743                             
28744                             if(markerBytes == 0xffe1){
28745                                 _this.parseExifData(
28746                                     dataView,
28747                                     offset,
28748                                     markerLength
28749                                 );
28750                             }
28751                             
28752                             offset += markerLength;
28753                             
28754                             continue;
28755                         }
28756                         
28757                         break;
28758                     }
28759                     
28760                 }
28761                 
28762                 var url = _this.urlAPI.createObjectURL(_this.file);
28763                 
28764                 _this.loadCanvas(url);
28765                 
28766                 return;
28767             }
28768             
28769             reader.readAsArrayBuffer(this.file);
28770             
28771         }
28772         
28773     },
28774     
28775     parseExifData : function(dataView, offset, length)
28776     {
28777         var tiffOffset = offset + 10,
28778             littleEndian,
28779             dirOffset;
28780     
28781         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28782             // No Exif data, might be XMP data instead
28783             return;
28784         }
28785         
28786         // Check for the ASCII code for "Exif" (0x45786966):
28787         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28788             // No Exif data, might be XMP data instead
28789             return;
28790         }
28791         if (tiffOffset + 8 > dataView.byteLength) {
28792             Roo.log('Invalid Exif data: Invalid segment size.');
28793             return;
28794         }
28795         // Check for the two null bytes:
28796         if (dataView.getUint16(offset + 8) !== 0x0000) {
28797             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28798             return;
28799         }
28800         // Check the byte alignment:
28801         switch (dataView.getUint16(tiffOffset)) {
28802         case 0x4949:
28803             littleEndian = true;
28804             break;
28805         case 0x4D4D:
28806             littleEndian = false;
28807             break;
28808         default:
28809             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28810             return;
28811         }
28812         // Check for the TIFF tag marker (0x002A):
28813         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28814             Roo.log('Invalid Exif data: Missing TIFF marker.');
28815             return;
28816         }
28817         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28818         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28819         
28820         this.parseExifTags(
28821             dataView,
28822             tiffOffset,
28823             tiffOffset + dirOffset,
28824             littleEndian
28825         );
28826     },
28827     
28828     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28829     {
28830         var tagsNumber,
28831             dirEndOffset,
28832             i;
28833         if (dirOffset + 6 > dataView.byteLength) {
28834             Roo.log('Invalid Exif data: Invalid directory offset.');
28835             return;
28836         }
28837         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28838         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28839         if (dirEndOffset + 4 > dataView.byteLength) {
28840             Roo.log('Invalid Exif data: Invalid directory size.');
28841             return;
28842         }
28843         for (i = 0; i < tagsNumber; i += 1) {
28844             this.parseExifTag(
28845                 dataView,
28846                 tiffOffset,
28847                 dirOffset + 2 + 12 * i, // tag offset
28848                 littleEndian
28849             );
28850         }
28851         // Return the offset to the next directory:
28852         return dataView.getUint32(dirEndOffset, littleEndian);
28853     },
28854     
28855     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28856     {
28857         var tag = dataView.getUint16(offset, littleEndian);
28858         
28859         this.exif[tag] = this.getExifValue(
28860             dataView,
28861             tiffOffset,
28862             offset,
28863             dataView.getUint16(offset + 2, littleEndian), // tag type
28864             dataView.getUint32(offset + 4, littleEndian), // tag length
28865             littleEndian
28866         );
28867     },
28868     
28869     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28870     {
28871         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28872             tagSize,
28873             dataOffset,
28874             values,
28875             i,
28876             str,
28877             c;
28878     
28879         if (!tagType) {
28880             Roo.log('Invalid Exif data: Invalid tag type.');
28881             return;
28882         }
28883         
28884         tagSize = tagType.size * length;
28885         // Determine if the value is contained in the dataOffset bytes,
28886         // or if the value at the dataOffset is a pointer to the actual data:
28887         dataOffset = tagSize > 4 ?
28888                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28889         if (dataOffset + tagSize > dataView.byteLength) {
28890             Roo.log('Invalid Exif data: Invalid data offset.');
28891             return;
28892         }
28893         if (length === 1) {
28894             return tagType.getValue(dataView, dataOffset, littleEndian);
28895         }
28896         values = [];
28897         for (i = 0; i < length; i += 1) {
28898             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28899         }
28900         
28901         if (tagType.ascii) {
28902             str = '';
28903             // Concatenate the chars:
28904             for (i = 0; i < values.length; i += 1) {
28905                 c = values[i];
28906                 // Ignore the terminating NULL byte(s):
28907                 if (c === '\u0000') {
28908                     break;
28909                 }
28910                 str += c;
28911             }
28912             return str;
28913         }
28914         return values;
28915     }
28916     
28917 });
28918
28919 Roo.apply(Roo.bootstrap.UploadCropbox, {
28920     tags : {
28921         'Orientation': 0x0112
28922     },
28923     
28924     Orientation: {
28925             1: 0, //'top-left',
28926 //            2: 'top-right',
28927             3: 180, //'bottom-right',
28928 //            4: 'bottom-left',
28929 //            5: 'left-top',
28930             6: 90, //'right-top',
28931 //            7: 'right-bottom',
28932             8: 270 //'left-bottom'
28933     },
28934     
28935     exifTagTypes : {
28936         // byte, 8-bit unsigned int:
28937         1: {
28938             getValue: function (dataView, dataOffset) {
28939                 return dataView.getUint8(dataOffset);
28940             },
28941             size: 1
28942         },
28943         // ascii, 8-bit byte:
28944         2: {
28945             getValue: function (dataView, dataOffset) {
28946                 return String.fromCharCode(dataView.getUint8(dataOffset));
28947             },
28948             size: 1,
28949             ascii: true
28950         },
28951         // short, 16 bit int:
28952         3: {
28953             getValue: function (dataView, dataOffset, littleEndian) {
28954                 return dataView.getUint16(dataOffset, littleEndian);
28955             },
28956             size: 2
28957         },
28958         // long, 32 bit int:
28959         4: {
28960             getValue: function (dataView, dataOffset, littleEndian) {
28961                 return dataView.getUint32(dataOffset, littleEndian);
28962             },
28963             size: 4
28964         },
28965         // rational = two long values, first is numerator, second is denominator:
28966         5: {
28967             getValue: function (dataView, dataOffset, littleEndian) {
28968                 return dataView.getUint32(dataOffset, littleEndian) /
28969                     dataView.getUint32(dataOffset + 4, littleEndian);
28970             },
28971             size: 8
28972         },
28973         // slong, 32 bit signed int:
28974         9: {
28975             getValue: function (dataView, dataOffset, littleEndian) {
28976                 return dataView.getInt32(dataOffset, littleEndian);
28977             },
28978             size: 4
28979         },
28980         // srational, two slongs, first is numerator, second is denominator:
28981         10: {
28982             getValue: function (dataView, dataOffset, littleEndian) {
28983                 return dataView.getInt32(dataOffset, littleEndian) /
28984                     dataView.getInt32(dataOffset + 4, littleEndian);
28985             },
28986             size: 8
28987         }
28988     },
28989     
28990     footer : {
28991         STANDARD : [
28992             {
28993                 tag : 'div',
28994                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28995                 action : 'rotate-left',
28996                 cn : [
28997                     {
28998                         tag : 'button',
28999                         cls : 'btn btn-default',
29000                         html : '<i class="fa fa-undo"></i>'
29001                     }
29002                 ]
29003             },
29004             {
29005                 tag : 'div',
29006                 cls : 'btn-group roo-upload-cropbox-picture',
29007                 action : 'picture',
29008                 cn : [
29009                     {
29010                         tag : 'button',
29011                         cls : 'btn btn-default',
29012                         html : '<i class="fa fa-picture-o"></i>'
29013                     }
29014                 ]
29015             },
29016             {
29017                 tag : 'div',
29018                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29019                 action : 'rotate-right',
29020                 cn : [
29021                     {
29022                         tag : 'button',
29023                         cls : 'btn btn-default',
29024                         html : '<i class="fa fa-repeat"></i>'
29025                     }
29026                 ]
29027             }
29028         ],
29029         DOCUMENT : [
29030             {
29031                 tag : 'div',
29032                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29033                 action : 'rotate-left',
29034                 cn : [
29035                     {
29036                         tag : 'button',
29037                         cls : 'btn btn-default',
29038                         html : '<i class="fa fa-undo"></i>'
29039                     }
29040                 ]
29041             },
29042             {
29043                 tag : 'div',
29044                 cls : 'btn-group roo-upload-cropbox-download',
29045                 action : 'download',
29046                 cn : [
29047                     {
29048                         tag : 'button',
29049                         cls : 'btn btn-default',
29050                         html : '<i class="fa fa-download"></i>'
29051                     }
29052                 ]
29053             },
29054             {
29055                 tag : 'div',
29056                 cls : 'btn-group roo-upload-cropbox-crop',
29057                 action : 'crop',
29058                 cn : [
29059                     {
29060                         tag : 'button',
29061                         cls : 'btn btn-default',
29062                         html : '<i class="fa fa-crop"></i>'
29063                     }
29064                 ]
29065             },
29066             {
29067                 tag : 'div',
29068                 cls : 'btn-group roo-upload-cropbox-trash',
29069                 action : 'trash',
29070                 cn : [
29071                     {
29072                         tag : 'button',
29073                         cls : 'btn btn-default',
29074                         html : '<i class="fa fa-trash"></i>'
29075                     }
29076                 ]
29077             },
29078             {
29079                 tag : 'div',
29080                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29081                 action : 'rotate-right',
29082                 cn : [
29083                     {
29084                         tag : 'button',
29085                         cls : 'btn btn-default',
29086                         html : '<i class="fa fa-repeat"></i>'
29087                     }
29088                 ]
29089             }
29090         ],
29091         ROTATOR : [
29092             {
29093                 tag : 'div',
29094                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29095                 action : 'rotate-left',
29096                 cn : [
29097                     {
29098                         tag : 'button',
29099                         cls : 'btn btn-default',
29100                         html : '<i class="fa fa-undo"></i>'
29101                     }
29102                 ]
29103             },
29104             {
29105                 tag : 'div',
29106                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29107                 action : 'rotate-right',
29108                 cn : [
29109                     {
29110                         tag : 'button',
29111                         cls : 'btn btn-default',
29112                         html : '<i class="fa fa-repeat"></i>'
29113                     }
29114                 ]
29115             }
29116         ]
29117     }
29118 });
29119
29120 /*
29121 * Licence: LGPL
29122 */
29123
29124 /**
29125  * @class Roo.bootstrap.DocumentManager
29126  * @extends Roo.bootstrap.Component
29127  * Bootstrap DocumentManager class
29128  * @cfg {String} paramName default 'imageUpload'
29129  * @cfg {String} toolTipName default 'filename'
29130  * @cfg {String} method default POST
29131  * @cfg {String} url action url
29132  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29133  * @cfg {Boolean} multiple multiple upload default true
29134  * @cfg {Number} thumbSize default 300
29135  * @cfg {String} fieldLabel
29136  * @cfg {Number} labelWidth default 4
29137  * @cfg {String} labelAlign (left|top) default left
29138  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29139 * @cfg {Number} labellg set the width of label (1-12)
29140  * @cfg {Number} labelmd set the width of label (1-12)
29141  * @cfg {Number} labelsm set the width of label (1-12)
29142  * @cfg {Number} labelxs set the width of label (1-12)
29143  * 
29144  * @constructor
29145  * Create a new DocumentManager
29146  * @param {Object} config The config object
29147  */
29148
29149 Roo.bootstrap.DocumentManager = function(config){
29150     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29151     
29152     this.files = [];
29153     this.delegates = [];
29154     
29155     this.addEvents({
29156         /**
29157          * @event initial
29158          * Fire when initial the DocumentManager
29159          * @param {Roo.bootstrap.DocumentManager} this
29160          */
29161         "initial" : true,
29162         /**
29163          * @event inspect
29164          * inspect selected file
29165          * @param {Roo.bootstrap.DocumentManager} this
29166          * @param {File} file
29167          */
29168         "inspect" : true,
29169         /**
29170          * @event exception
29171          * Fire when xhr load exception
29172          * @param {Roo.bootstrap.DocumentManager} this
29173          * @param {XMLHttpRequest} xhr
29174          */
29175         "exception" : true,
29176         /**
29177          * @event afterupload
29178          * Fire when xhr load exception
29179          * @param {Roo.bootstrap.DocumentManager} this
29180          * @param {XMLHttpRequest} xhr
29181          */
29182         "afterupload" : true,
29183         /**
29184          * @event prepare
29185          * prepare the form data
29186          * @param {Roo.bootstrap.DocumentManager} this
29187          * @param {Object} formData
29188          */
29189         "prepare" : true,
29190         /**
29191          * @event remove
29192          * Fire when remove the file
29193          * @param {Roo.bootstrap.DocumentManager} this
29194          * @param {Object} file
29195          */
29196         "remove" : true,
29197         /**
29198          * @event refresh
29199          * Fire after refresh the file
29200          * @param {Roo.bootstrap.DocumentManager} this
29201          */
29202         "refresh" : true,
29203         /**
29204          * @event click
29205          * Fire after click the image
29206          * @param {Roo.bootstrap.DocumentManager} this
29207          * @param {Object} file
29208          */
29209         "click" : true,
29210         /**
29211          * @event edit
29212          * Fire when upload a image and editable set to true
29213          * @param {Roo.bootstrap.DocumentManager} this
29214          * @param {Object} file
29215          */
29216         "edit" : true,
29217         /**
29218          * @event beforeselectfile
29219          * Fire before select file
29220          * @param {Roo.bootstrap.DocumentManager} this
29221          */
29222         "beforeselectfile" : true,
29223         /**
29224          * @event process
29225          * Fire before process file
29226          * @param {Roo.bootstrap.DocumentManager} this
29227          * @param {Object} file
29228          */
29229         "process" : true,
29230         /**
29231          * @event previewrendered
29232          * Fire when preview rendered
29233          * @param {Roo.bootstrap.DocumentManager} this
29234          * @param {Object} file
29235          */
29236         "previewrendered" : true,
29237         /**
29238          */
29239         "previewResize" : true
29240         
29241     });
29242 };
29243
29244 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29245     
29246     boxes : 0,
29247     inputName : '',
29248     thumbSize : 300,
29249     multiple : true,
29250     files : false,
29251     method : 'POST',
29252     url : '',
29253     paramName : 'imageUpload',
29254     toolTipName : 'filename',
29255     fieldLabel : '',
29256     labelWidth : 4,
29257     labelAlign : 'left',
29258     editable : true,
29259     delegates : false,
29260     xhr : false, 
29261     
29262     labellg : 0,
29263     labelmd : 0,
29264     labelsm : 0,
29265     labelxs : 0,
29266     
29267     getAutoCreate : function()
29268     {   
29269         var managerWidget = {
29270             tag : 'div',
29271             cls : 'roo-document-manager',
29272             cn : [
29273                 {
29274                     tag : 'input',
29275                     cls : 'roo-document-manager-selector',
29276                     type : 'file'
29277                 },
29278                 {
29279                     tag : 'div',
29280                     cls : 'roo-document-manager-uploader',
29281                     cn : [
29282                         {
29283                             tag : 'div',
29284                             cls : 'roo-document-manager-upload-btn',
29285                             html : '<i class="fa fa-plus"></i>'
29286                         }
29287                     ]
29288                     
29289                 }
29290             ]
29291         };
29292         
29293         var content = [
29294             {
29295                 tag : 'div',
29296                 cls : 'column col-md-12',
29297                 cn : managerWidget
29298             }
29299         ];
29300         
29301         if(this.fieldLabel.length){
29302             
29303             content = [
29304                 {
29305                     tag : 'div',
29306                     cls : 'column col-md-12',
29307                     html : this.fieldLabel
29308                 },
29309                 {
29310                     tag : 'div',
29311                     cls : 'column col-md-12',
29312                     cn : managerWidget
29313                 }
29314             ];
29315
29316             if(this.labelAlign == 'left'){
29317                 content = [
29318                     {
29319                         tag : 'div',
29320                         cls : 'column',
29321                         html : this.fieldLabel
29322                     },
29323                     {
29324                         tag : 'div',
29325                         cls : 'column',
29326                         cn : managerWidget
29327                     }
29328                 ];
29329                 
29330                 if(this.labelWidth > 12){
29331                     content[0].style = "width: " + this.labelWidth + 'px';
29332                 }
29333
29334                 if(this.labelWidth < 13 && this.labelmd == 0){
29335                     this.labelmd = this.labelWidth;
29336                 }
29337
29338                 if(this.labellg > 0){
29339                     content[0].cls += ' col-lg-' + this.labellg;
29340                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29341                 }
29342
29343                 if(this.labelmd > 0){
29344                     content[0].cls += ' col-md-' + this.labelmd;
29345                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29346                 }
29347
29348                 if(this.labelsm > 0){
29349                     content[0].cls += ' col-sm-' + this.labelsm;
29350                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29351                 }
29352
29353                 if(this.labelxs > 0){
29354                     content[0].cls += ' col-xs-' + this.labelxs;
29355                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29356                 }
29357                 
29358             }
29359         }
29360         
29361         var cfg = {
29362             tag : 'div',
29363             cls : 'row clearfix',
29364             cn : content
29365         };
29366         
29367         return cfg;
29368         
29369     },
29370     
29371     initEvents : function()
29372     {
29373         this.managerEl = this.el.select('.roo-document-manager', true).first();
29374         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29375         
29376         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29377         this.selectorEl.hide();
29378         
29379         if(this.multiple){
29380             this.selectorEl.attr('multiple', 'multiple');
29381         }
29382         
29383         this.selectorEl.on('change', this.onFileSelected, this);
29384         
29385         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29386         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29387         
29388         this.uploader.on('click', this.onUploaderClick, this);
29389         
29390         this.renderProgressDialog();
29391         
29392         var _this = this;
29393         
29394         window.addEventListener("resize", function() { _this.refresh(); } );
29395         
29396         this.fireEvent('initial', this);
29397     },
29398     
29399     renderProgressDialog : function()
29400     {
29401         var _this = this;
29402         
29403         this.progressDialog = new Roo.bootstrap.Modal({
29404             cls : 'roo-document-manager-progress-dialog',
29405             allow_close : false,
29406             animate : false,
29407             title : '',
29408             buttons : [
29409                 {
29410                     name  :'cancel',
29411                     weight : 'danger',
29412                     html : 'Cancel'
29413                 }
29414             ], 
29415             listeners : { 
29416                 btnclick : function() {
29417                     _this.uploadCancel();
29418                     this.hide();
29419                 }
29420             }
29421         });
29422          
29423         this.progressDialog.render(Roo.get(document.body));
29424          
29425         this.progress = new Roo.bootstrap.Progress({
29426             cls : 'roo-document-manager-progress',
29427             active : true,
29428             striped : true
29429         });
29430         
29431         this.progress.render(this.progressDialog.getChildContainer());
29432         
29433         this.progressBar = new Roo.bootstrap.ProgressBar({
29434             cls : 'roo-document-manager-progress-bar',
29435             aria_valuenow : 0,
29436             aria_valuemin : 0,
29437             aria_valuemax : 12,
29438             panel : 'success'
29439         });
29440         
29441         this.progressBar.render(this.progress.getChildContainer());
29442     },
29443     
29444     onUploaderClick : function(e)
29445     {
29446         e.preventDefault();
29447      
29448         if(this.fireEvent('beforeselectfile', this) != false){
29449             this.selectorEl.dom.click();
29450         }
29451         
29452     },
29453     
29454     onFileSelected : function(e)
29455     {
29456         e.preventDefault();
29457         
29458         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29459             return;
29460         }
29461         
29462         Roo.each(this.selectorEl.dom.files, function(file){
29463             if(this.fireEvent('inspect', this, file) != false){
29464                 this.files.push(file);
29465             }
29466         }, this);
29467         
29468         this.queue();
29469         
29470     },
29471     
29472     queue : function()
29473     {
29474         this.selectorEl.dom.value = '';
29475         
29476         if(!this.files || !this.files.length){
29477             return;
29478         }
29479         
29480         if(this.boxes > 0 && this.files.length > this.boxes){
29481             this.files = this.files.slice(0, this.boxes);
29482         }
29483         
29484         this.uploader.show();
29485         
29486         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29487             this.uploader.hide();
29488         }
29489         
29490         var _this = this;
29491         
29492         var files = [];
29493         
29494         var docs = [];
29495         
29496         Roo.each(this.files, function(file){
29497             
29498             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29499                 var f = this.renderPreview(file);
29500                 files.push(f);
29501                 return;
29502             }
29503             
29504             if(file.type.indexOf('image') != -1){
29505                 this.delegates.push(
29506                     (function(){
29507                         _this.process(file);
29508                     }).createDelegate(this)
29509                 );
29510         
29511                 return;
29512             }
29513             
29514             docs.push(
29515                 (function(){
29516                     _this.process(file);
29517                 }).createDelegate(this)
29518             );
29519             
29520         }, this);
29521         
29522         this.files = files;
29523         
29524         this.delegates = this.delegates.concat(docs);
29525         
29526         if(!this.delegates.length){
29527             this.refresh();
29528             return;
29529         }
29530         
29531         this.progressBar.aria_valuemax = this.delegates.length;
29532         
29533         this.arrange();
29534         
29535         return;
29536     },
29537     
29538     arrange : function()
29539     {
29540         if(!this.delegates.length){
29541             this.progressDialog.hide();
29542             this.refresh();
29543             return;
29544         }
29545         
29546         var delegate = this.delegates.shift();
29547         
29548         this.progressDialog.show();
29549         
29550         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29551         
29552         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29553         
29554         delegate();
29555     },
29556     
29557     refresh : function()
29558     {
29559         this.uploader.show();
29560         
29561         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29562             this.uploader.hide();
29563         }
29564         
29565         Roo.isTouch ? this.closable(false) : this.closable(true);
29566         
29567         this.fireEvent('refresh', this);
29568     },
29569     
29570     onRemove : function(e, el, o)
29571     {
29572         e.preventDefault();
29573         
29574         this.fireEvent('remove', this, o);
29575         
29576     },
29577     
29578     remove : function(o)
29579     {
29580         var files = [];
29581         
29582         Roo.each(this.files, function(file){
29583             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29584                 files.push(file);
29585                 return;
29586             }
29587
29588             o.target.remove();
29589
29590         }, this);
29591         
29592         this.files = files;
29593         
29594         this.refresh();
29595     },
29596     
29597     clear : function()
29598     {
29599         Roo.each(this.files, function(file){
29600             if(!file.target){
29601                 return;
29602             }
29603             
29604             file.target.remove();
29605
29606         }, this);
29607         
29608         this.files = [];
29609         
29610         this.refresh();
29611     },
29612     
29613     onClick : function(e, el, o)
29614     {
29615         e.preventDefault();
29616         
29617         this.fireEvent('click', this, o);
29618         
29619     },
29620     
29621     closable : function(closable)
29622     {
29623         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29624             
29625             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29626             
29627             if(closable){
29628                 el.show();
29629                 return;
29630             }
29631             
29632             el.hide();
29633             
29634         }, this);
29635     },
29636     
29637     xhrOnLoad : function(xhr)
29638     {
29639         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29640             el.remove();
29641         }, this);
29642         
29643         if (xhr.readyState !== 4) {
29644             this.arrange();
29645             this.fireEvent('exception', this, xhr);
29646             return;
29647         }
29648
29649         var response = Roo.decode(xhr.responseText);
29650         
29651         if(!response.success){
29652             this.arrange();
29653             this.fireEvent('exception', this, xhr);
29654             return;
29655         }
29656         
29657         var file = this.renderPreview(response.data);
29658         
29659         this.files.push(file);
29660         
29661         this.arrange();
29662         
29663         this.fireEvent('afterupload', this, xhr);
29664         
29665     },
29666     
29667     xhrOnError : function(xhr)
29668     {
29669         Roo.log('xhr on error');
29670         
29671         var response = Roo.decode(xhr.responseText);
29672           
29673         Roo.log(response);
29674         
29675         this.arrange();
29676     },
29677     
29678     process : function(file)
29679     {
29680         if(this.fireEvent('process', this, file) !== false){
29681             if(this.editable && file.type.indexOf('image') != -1){
29682                 this.fireEvent('edit', this, file);
29683                 return;
29684             }
29685
29686             this.uploadStart(file, false);
29687
29688             return;
29689         }
29690         
29691     },
29692     
29693     uploadStart : function(file, crop)
29694     {
29695         this.xhr = new XMLHttpRequest();
29696         
29697         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29698             this.arrange();
29699             return;
29700         }
29701         
29702         file.xhr = this.xhr;
29703             
29704         this.managerEl.createChild({
29705             tag : 'div',
29706             cls : 'roo-document-manager-loading',
29707             cn : [
29708                 {
29709                     tag : 'div',
29710                     tooltip : file.name,
29711                     cls : 'roo-document-manager-thumb',
29712                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29713                 }
29714             ]
29715
29716         });
29717
29718         this.xhr.open(this.method, this.url, true);
29719         
29720         var headers = {
29721             "Accept": "application/json",
29722             "Cache-Control": "no-cache",
29723             "X-Requested-With": "XMLHttpRequest"
29724         };
29725         
29726         for (var headerName in headers) {
29727             var headerValue = headers[headerName];
29728             if (headerValue) {
29729                 this.xhr.setRequestHeader(headerName, headerValue);
29730             }
29731         }
29732         
29733         var _this = this;
29734         
29735         this.xhr.onload = function()
29736         {
29737             _this.xhrOnLoad(_this.xhr);
29738         }
29739         
29740         this.xhr.onerror = function()
29741         {
29742             _this.xhrOnError(_this.xhr);
29743         }
29744         
29745         var formData = new FormData();
29746
29747         formData.append('returnHTML', 'NO');
29748         
29749         if(crop){
29750             formData.append('crop', crop);
29751         }
29752         
29753         formData.append(this.paramName, file, file.name);
29754         
29755         var options = {
29756             file : file, 
29757             manually : false
29758         };
29759         
29760         if(this.fireEvent('prepare', this, formData, options) != false){
29761             
29762             if(options.manually){
29763                 return;
29764             }
29765             
29766             this.xhr.send(formData);
29767             return;
29768         };
29769         
29770         this.uploadCancel();
29771     },
29772     
29773     uploadCancel : function()
29774     {
29775         if (this.xhr) {
29776             this.xhr.abort();
29777         }
29778         
29779         this.delegates = [];
29780         
29781         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29782             el.remove();
29783         }, this);
29784         
29785         this.arrange();
29786     },
29787     
29788     renderPreview : function(file)
29789     {
29790         if(typeof(file.target) != 'undefined' && file.target){
29791             return file;
29792         }
29793         
29794         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29795         
29796         var previewEl = this.managerEl.createChild({
29797             tag : 'div',
29798             cls : 'roo-document-manager-preview',
29799             cn : [
29800                 {
29801                     tag : 'div',
29802                     tooltip : file[this.toolTipName],
29803                     cls : 'roo-document-manager-thumb',
29804                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29805                 },
29806                 {
29807                     tag : 'button',
29808                     cls : 'close',
29809                     html : '<i class="fa fa-times-circle"></i>'
29810                 }
29811             ]
29812         });
29813
29814         var close = previewEl.select('button.close', true).first();
29815
29816         close.on('click', this.onRemove, this, file);
29817
29818         file.target = previewEl;
29819
29820         var image = previewEl.select('img', true).first();
29821         
29822         var _this = this;
29823         
29824         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29825         
29826         image.on('click', this.onClick, this, file);
29827         
29828         this.fireEvent('previewrendered', this, file);
29829         
29830         return file;
29831         
29832     },
29833     
29834     onPreviewLoad : function(file, image)
29835     {
29836         if(typeof(file.target) == 'undefined' || !file.target){
29837             return;
29838         }
29839         
29840         var width = image.dom.naturalWidth || image.dom.width;
29841         var height = image.dom.naturalHeight || image.dom.height;
29842         
29843         if(!this.previewResize) {
29844             return;
29845         }
29846         
29847         if(width > height){
29848             file.target.addClass('wide');
29849             return;
29850         }
29851         
29852         file.target.addClass('tall');
29853         return;
29854         
29855     },
29856     
29857     uploadFromSource : function(file, crop)
29858     {
29859         this.xhr = new XMLHttpRequest();
29860         
29861         this.managerEl.createChild({
29862             tag : 'div',
29863             cls : 'roo-document-manager-loading',
29864             cn : [
29865                 {
29866                     tag : 'div',
29867                     tooltip : file.name,
29868                     cls : 'roo-document-manager-thumb',
29869                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29870                 }
29871             ]
29872
29873         });
29874
29875         this.xhr.open(this.method, this.url, true);
29876         
29877         var headers = {
29878             "Accept": "application/json",
29879             "Cache-Control": "no-cache",
29880             "X-Requested-With": "XMLHttpRequest"
29881         };
29882         
29883         for (var headerName in headers) {
29884             var headerValue = headers[headerName];
29885             if (headerValue) {
29886                 this.xhr.setRequestHeader(headerName, headerValue);
29887             }
29888         }
29889         
29890         var _this = this;
29891         
29892         this.xhr.onload = function()
29893         {
29894             _this.xhrOnLoad(_this.xhr);
29895         }
29896         
29897         this.xhr.onerror = function()
29898         {
29899             _this.xhrOnError(_this.xhr);
29900         }
29901         
29902         var formData = new FormData();
29903
29904         formData.append('returnHTML', 'NO');
29905         
29906         formData.append('crop', crop);
29907         
29908         if(typeof(file.filename) != 'undefined'){
29909             formData.append('filename', file.filename);
29910         }
29911         
29912         if(typeof(file.mimetype) != 'undefined'){
29913             formData.append('mimetype', file.mimetype);
29914         }
29915         
29916         Roo.log(formData);
29917         
29918         if(this.fireEvent('prepare', this, formData) != false){
29919             this.xhr.send(formData);
29920         };
29921     }
29922 });
29923
29924 /*
29925 * Licence: LGPL
29926 */
29927
29928 /**
29929  * @class Roo.bootstrap.DocumentViewer
29930  * @extends Roo.bootstrap.Component
29931  * Bootstrap DocumentViewer class
29932  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29933  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29934  * 
29935  * @constructor
29936  * Create a new DocumentViewer
29937  * @param {Object} config The config object
29938  */
29939
29940 Roo.bootstrap.DocumentViewer = function(config){
29941     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29942     
29943     this.addEvents({
29944         /**
29945          * @event initial
29946          * Fire after initEvent
29947          * @param {Roo.bootstrap.DocumentViewer} this
29948          */
29949         "initial" : true,
29950         /**
29951          * @event click
29952          * Fire after click
29953          * @param {Roo.bootstrap.DocumentViewer} this
29954          */
29955         "click" : true,
29956         /**
29957          * @event download
29958          * Fire after download button
29959          * @param {Roo.bootstrap.DocumentViewer} this
29960          */
29961         "download" : true,
29962         /**
29963          * @event trash
29964          * Fire after trash button
29965          * @param {Roo.bootstrap.DocumentViewer} this
29966          */
29967         "trash" : true
29968         
29969     });
29970 };
29971
29972 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29973     
29974     showDownload : true,
29975     
29976     showTrash : true,
29977     
29978     getAutoCreate : function()
29979     {
29980         var cfg = {
29981             tag : 'div',
29982             cls : 'roo-document-viewer',
29983             cn : [
29984                 {
29985                     tag : 'div',
29986                     cls : 'roo-document-viewer-body',
29987                     cn : [
29988                         {
29989                             tag : 'div',
29990                             cls : 'roo-document-viewer-thumb',
29991                             cn : [
29992                                 {
29993                                     tag : 'img',
29994                                     cls : 'roo-document-viewer-image'
29995                                 }
29996                             ]
29997                         }
29998                     ]
29999                 },
30000                 {
30001                     tag : 'div',
30002                     cls : 'roo-document-viewer-footer',
30003                     cn : {
30004                         tag : 'div',
30005                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30006                         cn : [
30007                             {
30008                                 tag : 'div',
30009                                 cls : 'btn-group roo-document-viewer-download',
30010                                 cn : [
30011                                     {
30012                                         tag : 'button',
30013                                         cls : 'btn btn-default',
30014                                         html : '<i class="fa fa-download"></i>'
30015                                     }
30016                                 ]
30017                             },
30018                             {
30019                                 tag : 'div',
30020                                 cls : 'btn-group roo-document-viewer-trash',
30021                                 cn : [
30022                                     {
30023                                         tag : 'button',
30024                                         cls : 'btn btn-default',
30025                                         html : '<i class="fa fa-trash"></i>'
30026                                     }
30027                                 ]
30028                             }
30029                         ]
30030                     }
30031                 }
30032             ]
30033         };
30034         
30035         return cfg;
30036     },
30037     
30038     initEvents : function()
30039     {
30040         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30041         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30042         
30043         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30044         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30045         
30046         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30047         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30048         
30049         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30050         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30051         
30052         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30053         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30054         
30055         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30056         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30057         
30058         this.bodyEl.on('click', this.onClick, this);
30059         this.downloadBtn.on('click', this.onDownload, this);
30060         this.trashBtn.on('click', this.onTrash, this);
30061         
30062         this.downloadBtn.hide();
30063         this.trashBtn.hide();
30064         
30065         if(this.showDownload){
30066             this.downloadBtn.show();
30067         }
30068         
30069         if(this.showTrash){
30070             this.trashBtn.show();
30071         }
30072         
30073         if(!this.showDownload && !this.showTrash) {
30074             this.footerEl.hide();
30075         }
30076         
30077     },
30078     
30079     initial : function()
30080     {
30081         this.fireEvent('initial', this);
30082         
30083     },
30084     
30085     onClick : function(e)
30086     {
30087         e.preventDefault();
30088         
30089         this.fireEvent('click', this);
30090     },
30091     
30092     onDownload : function(e)
30093     {
30094         e.preventDefault();
30095         
30096         this.fireEvent('download', this);
30097     },
30098     
30099     onTrash : function(e)
30100     {
30101         e.preventDefault();
30102         
30103         this.fireEvent('trash', this);
30104     }
30105     
30106 });
30107 /*
30108  * - LGPL
30109  *
30110  * nav progress bar
30111  * 
30112  */
30113
30114 /**
30115  * @class Roo.bootstrap.NavProgressBar
30116  * @extends Roo.bootstrap.Component
30117  * Bootstrap NavProgressBar class
30118  * 
30119  * @constructor
30120  * Create a new nav progress bar
30121  * @param {Object} config The config object
30122  */
30123
30124 Roo.bootstrap.NavProgressBar = function(config){
30125     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30126
30127     this.bullets = this.bullets || [];
30128    
30129 //    Roo.bootstrap.NavProgressBar.register(this);
30130      this.addEvents({
30131         /**
30132              * @event changed
30133              * Fires when the active item changes
30134              * @param {Roo.bootstrap.NavProgressBar} this
30135              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30136              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30137          */
30138         'changed': true
30139      });
30140     
30141 };
30142
30143 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30144     
30145     bullets : [],
30146     barItems : [],
30147     
30148     getAutoCreate : function()
30149     {
30150         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30151         
30152         cfg = {
30153             tag : 'div',
30154             cls : 'roo-navigation-bar-group',
30155             cn : [
30156                 {
30157                     tag : 'div',
30158                     cls : 'roo-navigation-top-bar'
30159                 },
30160                 {
30161                     tag : 'div',
30162                     cls : 'roo-navigation-bullets-bar',
30163                     cn : [
30164                         {
30165                             tag : 'ul',
30166                             cls : 'roo-navigation-bar'
30167                         }
30168                     ]
30169                 },
30170                 
30171                 {
30172                     tag : 'div',
30173                     cls : 'roo-navigation-bottom-bar'
30174                 }
30175             ]
30176             
30177         };
30178         
30179         return cfg;
30180         
30181     },
30182     
30183     initEvents: function() 
30184     {
30185         
30186     },
30187     
30188     onRender : function(ct, position) 
30189     {
30190         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30191         
30192         if(this.bullets.length){
30193             Roo.each(this.bullets, function(b){
30194                this.addItem(b);
30195             }, this);
30196         }
30197         
30198         this.format();
30199         
30200     },
30201     
30202     addItem : function(cfg)
30203     {
30204         var item = new Roo.bootstrap.NavProgressItem(cfg);
30205         
30206         item.parentId = this.id;
30207         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30208         
30209         if(cfg.html){
30210             var top = new Roo.bootstrap.Element({
30211                 tag : 'div',
30212                 cls : 'roo-navigation-bar-text'
30213             });
30214             
30215             var bottom = new Roo.bootstrap.Element({
30216                 tag : 'div',
30217                 cls : 'roo-navigation-bar-text'
30218             });
30219             
30220             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30221             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30222             
30223             var topText = new Roo.bootstrap.Element({
30224                 tag : 'span',
30225                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30226             });
30227             
30228             var bottomText = new Roo.bootstrap.Element({
30229                 tag : 'span',
30230                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30231             });
30232             
30233             topText.onRender(top.el, null);
30234             bottomText.onRender(bottom.el, null);
30235             
30236             item.topEl = top;
30237             item.bottomEl = bottom;
30238         }
30239         
30240         this.barItems.push(item);
30241         
30242         return item;
30243     },
30244     
30245     getActive : function()
30246     {
30247         var active = false;
30248         
30249         Roo.each(this.barItems, function(v){
30250             
30251             if (!v.isActive()) {
30252                 return;
30253             }
30254             
30255             active = v;
30256             return false;
30257             
30258         });
30259         
30260         return active;
30261     },
30262     
30263     setActiveItem : function(item)
30264     {
30265         var prev = false;
30266         
30267         Roo.each(this.barItems, function(v){
30268             if (v.rid == item.rid) {
30269                 return ;
30270             }
30271             
30272             if (v.isActive()) {
30273                 v.setActive(false);
30274                 prev = v;
30275             }
30276         });
30277
30278         item.setActive(true);
30279         
30280         this.fireEvent('changed', this, item, prev);
30281     },
30282     
30283     getBarItem: function(rid)
30284     {
30285         var ret = false;
30286         
30287         Roo.each(this.barItems, function(e) {
30288             if (e.rid != rid) {
30289                 return;
30290             }
30291             
30292             ret =  e;
30293             return false;
30294         });
30295         
30296         return ret;
30297     },
30298     
30299     indexOfItem : function(item)
30300     {
30301         var index = false;
30302         
30303         Roo.each(this.barItems, function(v, i){
30304             
30305             if (v.rid != item.rid) {
30306                 return;
30307             }
30308             
30309             index = i;
30310             return false
30311         });
30312         
30313         return index;
30314     },
30315     
30316     setActiveNext : function()
30317     {
30318         var i = this.indexOfItem(this.getActive());
30319         
30320         if (i > this.barItems.length) {
30321             return;
30322         }
30323         
30324         this.setActiveItem(this.barItems[i+1]);
30325     },
30326     
30327     setActivePrev : function()
30328     {
30329         var i = this.indexOfItem(this.getActive());
30330         
30331         if (i  < 1) {
30332             return;
30333         }
30334         
30335         this.setActiveItem(this.barItems[i-1]);
30336     },
30337     
30338     format : function()
30339     {
30340         if(!this.barItems.length){
30341             return;
30342         }
30343      
30344         var width = 100 / this.barItems.length;
30345         
30346         Roo.each(this.barItems, function(i){
30347             i.el.setStyle('width', width + '%');
30348             i.topEl.el.setStyle('width', width + '%');
30349             i.bottomEl.el.setStyle('width', width + '%');
30350         }, this);
30351         
30352     }
30353     
30354 });
30355 /*
30356  * - LGPL
30357  *
30358  * Nav Progress Item
30359  * 
30360  */
30361
30362 /**
30363  * @class Roo.bootstrap.NavProgressItem
30364  * @extends Roo.bootstrap.Component
30365  * Bootstrap NavProgressItem class
30366  * @cfg {String} rid the reference id
30367  * @cfg {Boolean} active (true|false) Is item active default false
30368  * @cfg {Boolean} disabled (true|false) Is item active default false
30369  * @cfg {String} html
30370  * @cfg {String} position (top|bottom) text position default bottom
30371  * @cfg {String} icon show icon instead of number
30372  * 
30373  * @constructor
30374  * Create a new NavProgressItem
30375  * @param {Object} config The config object
30376  */
30377 Roo.bootstrap.NavProgressItem = function(config){
30378     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30379     this.addEvents({
30380         // raw events
30381         /**
30382          * @event click
30383          * The raw click event for the entire grid.
30384          * @param {Roo.bootstrap.NavProgressItem} this
30385          * @param {Roo.EventObject} e
30386          */
30387         "click" : true
30388     });
30389    
30390 };
30391
30392 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30393     
30394     rid : '',
30395     active : false,
30396     disabled : false,
30397     html : '',
30398     position : 'bottom',
30399     icon : false,
30400     
30401     getAutoCreate : function()
30402     {
30403         var iconCls = 'roo-navigation-bar-item-icon';
30404         
30405         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30406         
30407         var cfg = {
30408             tag: 'li',
30409             cls: 'roo-navigation-bar-item',
30410             cn : [
30411                 {
30412                     tag : 'i',
30413                     cls : iconCls
30414                 }
30415             ]
30416         };
30417         
30418         if(this.active){
30419             cfg.cls += ' active';
30420         }
30421         if(this.disabled){
30422             cfg.cls += ' disabled';
30423         }
30424         
30425         return cfg;
30426     },
30427     
30428     disable : function()
30429     {
30430         this.setDisabled(true);
30431     },
30432     
30433     enable : function()
30434     {
30435         this.setDisabled(false);
30436     },
30437     
30438     initEvents: function() 
30439     {
30440         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30441         
30442         this.iconEl.on('click', this.onClick, this);
30443     },
30444     
30445     onClick : function(e)
30446     {
30447         e.preventDefault();
30448         
30449         if(this.disabled){
30450             return;
30451         }
30452         
30453         if(this.fireEvent('click', this, e) === false){
30454             return;
30455         };
30456         
30457         this.parent().setActiveItem(this);
30458     },
30459     
30460     isActive: function () 
30461     {
30462         return this.active;
30463     },
30464     
30465     setActive : function(state)
30466     {
30467         if(this.active == state){
30468             return;
30469         }
30470         
30471         this.active = state;
30472         
30473         if (state) {
30474             this.el.addClass('active');
30475             return;
30476         }
30477         
30478         this.el.removeClass('active');
30479         
30480         return;
30481     },
30482     
30483     setDisabled : function(state)
30484     {
30485         if(this.disabled == state){
30486             return;
30487         }
30488         
30489         this.disabled = state;
30490         
30491         if (state) {
30492             this.el.addClass('disabled');
30493             return;
30494         }
30495         
30496         this.el.removeClass('disabled');
30497     },
30498     
30499     tooltipEl : function()
30500     {
30501         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30502     }
30503 });
30504  
30505
30506  /*
30507  * - LGPL
30508  *
30509  * FieldLabel
30510  * 
30511  */
30512
30513 /**
30514  * @class Roo.bootstrap.FieldLabel
30515  * @extends Roo.bootstrap.Component
30516  * Bootstrap FieldLabel class
30517  * @cfg {String} html contents of the element
30518  * @cfg {String} tag tag of the element default label
30519  * @cfg {String} cls class of the element
30520  * @cfg {String} target label target 
30521  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30522  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30523  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30524  * @cfg {String} iconTooltip default "This field is required"
30525  * @cfg {String} indicatorpos (left|right) default left
30526  * 
30527  * @constructor
30528  * Create a new FieldLabel
30529  * @param {Object} config The config object
30530  */
30531
30532 Roo.bootstrap.FieldLabel = function(config){
30533     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30534     
30535     this.addEvents({
30536             /**
30537              * @event invalid
30538              * Fires after the field has been marked as invalid.
30539              * @param {Roo.form.FieldLabel} this
30540              * @param {String} msg The validation message
30541              */
30542             invalid : true,
30543             /**
30544              * @event valid
30545              * Fires after the field has been validated with no errors.
30546              * @param {Roo.form.FieldLabel} this
30547              */
30548             valid : true
30549         });
30550 };
30551
30552 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30553     
30554     tag: 'label',
30555     cls: '',
30556     html: '',
30557     target: '',
30558     allowBlank : true,
30559     invalidClass : 'has-warning',
30560     validClass : 'has-success',
30561     iconTooltip : 'This field is required',
30562     indicatorpos : 'left',
30563     
30564     getAutoCreate : function(){
30565         
30566         var cls = "";
30567         if (!this.allowBlank) {
30568             cls  = "visible";
30569         }
30570         
30571         var cfg = {
30572             tag : this.tag,
30573             cls : 'roo-bootstrap-field-label ' + this.cls,
30574             for : this.target,
30575             cn : [
30576                 {
30577                     tag : 'i',
30578                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30579                     tooltip : this.iconTooltip
30580                 },
30581                 {
30582                     tag : 'span',
30583                     html : this.html
30584                 }
30585             ] 
30586         };
30587         
30588         if(this.indicatorpos == 'right'){
30589             var cfg = {
30590                 tag : this.tag,
30591                 cls : 'roo-bootstrap-field-label ' + this.cls,
30592                 for : this.target,
30593                 cn : [
30594                     {
30595                         tag : 'span',
30596                         html : this.html
30597                     },
30598                     {
30599                         tag : 'i',
30600                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30601                         tooltip : this.iconTooltip
30602                     }
30603                 ] 
30604             };
30605         }
30606         
30607         return cfg;
30608     },
30609     
30610     initEvents: function() 
30611     {
30612         Roo.bootstrap.Element.superclass.initEvents.call(this);
30613         
30614         this.indicator = this.indicatorEl();
30615         
30616         if(this.indicator){
30617             this.indicator.removeClass('visible');
30618             this.indicator.addClass('invisible');
30619         }
30620         
30621         Roo.bootstrap.FieldLabel.register(this);
30622     },
30623     
30624     indicatorEl : function()
30625     {
30626         var indicator = this.el.select('i.roo-required-indicator',true).first();
30627         
30628         if(!indicator){
30629             return false;
30630         }
30631         
30632         return indicator;
30633         
30634     },
30635     
30636     /**
30637      * Mark this field as valid
30638      */
30639     markValid : function()
30640     {
30641         if(this.indicator){
30642             this.indicator.removeClass('visible');
30643             this.indicator.addClass('invisible');
30644         }
30645         if (Roo.bootstrap.version == 3) {
30646             this.el.removeClass(this.invalidClass);
30647             this.el.addClass(this.validClass);
30648         } else {
30649             this.el.removeClass('is-invalid');
30650             this.el.addClass('is-valid');
30651         }
30652         
30653         
30654         this.fireEvent('valid', this);
30655     },
30656     
30657     /**
30658      * Mark this field as invalid
30659      * @param {String} msg The validation message
30660      */
30661     markInvalid : function(msg)
30662     {
30663         if(this.indicator){
30664             this.indicator.removeClass('invisible');
30665             this.indicator.addClass('visible');
30666         }
30667           if (Roo.bootstrap.version == 3) {
30668             this.el.removeClass(this.validClass);
30669             this.el.addClass(this.invalidClass);
30670         } else {
30671             this.el.removeClass('is-valid');
30672             this.el.addClass('is-invalid');
30673         }
30674         
30675         
30676         this.fireEvent('invalid', this, msg);
30677     }
30678     
30679    
30680 });
30681
30682 Roo.apply(Roo.bootstrap.FieldLabel, {
30683     
30684     groups: {},
30685     
30686      /**
30687     * register a FieldLabel Group
30688     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30689     */
30690     register : function(label)
30691     {
30692         if(this.groups.hasOwnProperty(label.target)){
30693             return;
30694         }
30695      
30696         this.groups[label.target] = label;
30697         
30698     },
30699     /**
30700     * fetch a FieldLabel Group based on the target
30701     * @param {string} target
30702     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30703     */
30704     get: function(target) {
30705         if (typeof(this.groups[target]) == 'undefined') {
30706             return false;
30707         }
30708         
30709         return this.groups[target] ;
30710     }
30711 });
30712
30713  
30714
30715  /*
30716  * - LGPL
30717  *
30718  * page DateSplitField.
30719  * 
30720  */
30721
30722
30723 /**
30724  * @class Roo.bootstrap.DateSplitField
30725  * @extends Roo.bootstrap.Component
30726  * Bootstrap DateSplitField class
30727  * @cfg {string} fieldLabel - the label associated
30728  * @cfg {Number} labelWidth set the width of label (0-12)
30729  * @cfg {String} labelAlign (top|left)
30730  * @cfg {Boolean} dayAllowBlank (true|false) default false
30731  * @cfg {Boolean} monthAllowBlank (true|false) default false
30732  * @cfg {Boolean} yearAllowBlank (true|false) default false
30733  * @cfg {string} dayPlaceholder 
30734  * @cfg {string} monthPlaceholder
30735  * @cfg {string} yearPlaceholder
30736  * @cfg {string} dayFormat default 'd'
30737  * @cfg {string} monthFormat default 'm'
30738  * @cfg {string} yearFormat default 'Y'
30739  * @cfg {Number} labellg set the width of label (1-12)
30740  * @cfg {Number} labelmd set the width of label (1-12)
30741  * @cfg {Number} labelsm set the width of label (1-12)
30742  * @cfg {Number} labelxs set the width of label (1-12)
30743
30744  *     
30745  * @constructor
30746  * Create a new DateSplitField
30747  * @param {Object} config The config object
30748  */
30749
30750 Roo.bootstrap.DateSplitField = function(config){
30751     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30752     
30753     this.addEvents({
30754         // raw events
30755          /**
30756          * @event years
30757          * getting the data of years
30758          * @param {Roo.bootstrap.DateSplitField} this
30759          * @param {Object} years
30760          */
30761         "years" : true,
30762         /**
30763          * @event days
30764          * getting the data of days
30765          * @param {Roo.bootstrap.DateSplitField} this
30766          * @param {Object} days
30767          */
30768         "days" : true,
30769         /**
30770          * @event invalid
30771          * Fires after the field has been marked as invalid.
30772          * @param {Roo.form.Field} this
30773          * @param {String} msg The validation message
30774          */
30775         invalid : true,
30776        /**
30777          * @event valid
30778          * Fires after the field has been validated with no errors.
30779          * @param {Roo.form.Field} this
30780          */
30781         valid : true
30782     });
30783 };
30784
30785 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30786     
30787     fieldLabel : '',
30788     labelAlign : 'top',
30789     labelWidth : 3,
30790     dayAllowBlank : false,
30791     monthAllowBlank : false,
30792     yearAllowBlank : false,
30793     dayPlaceholder : '',
30794     monthPlaceholder : '',
30795     yearPlaceholder : '',
30796     dayFormat : 'd',
30797     monthFormat : 'm',
30798     yearFormat : 'Y',
30799     isFormField : true,
30800     labellg : 0,
30801     labelmd : 0,
30802     labelsm : 0,
30803     labelxs : 0,
30804     
30805     getAutoCreate : function()
30806     {
30807         var cfg = {
30808             tag : 'div',
30809             cls : 'row roo-date-split-field-group',
30810             cn : [
30811                 {
30812                     tag : 'input',
30813                     type : 'hidden',
30814                     cls : 'form-hidden-field roo-date-split-field-group-value',
30815                     name : this.name
30816                 }
30817             ]
30818         };
30819         
30820         var labelCls = 'col-md-12';
30821         var contentCls = 'col-md-4';
30822         
30823         if(this.fieldLabel){
30824             
30825             var label = {
30826                 tag : 'div',
30827                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30828                 cn : [
30829                     {
30830                         tag : 'label',
30831                         html : this.fieldLabel
30832                     }
30833                 ]
30834             };
30835             
30836             if(this.labelAlign == 'left'){
30837             
30838                 if(this.labelWidth > 12){
30839                     label.style = "width: " + this.labelWidth + 'px';
30840                 }
30841
30842                 if(this.labelWidth < 13 && this.labelmd == 0){
30843                     this.labelmd = this.labelWidth;
30844                 }
30845
30846                 if(this.labellg > 0){
30847                     labelCls = ' col-lg-' + this.labellg;
30848                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30849                 }
30850
30851                 if(this.labelmd > 0){
30852                     labelCls = ' col-md-' + this.labelmd;
30853                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30854                 }
30855
30856                 if(this.labelsm > 0){
30857                     labelCls = ' col-sm-' + this.labelsm;
30858                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30859                 }
30860
30861                 if(this.labelxs > 0){
30862                     labelCls = ' col-xs-' + this.labelxs;
30863                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30864                 }
30865             }
30866             
30867             label.cls += ' ' + labelCls;
30868             
30869             cfg.cn.push(label);
30870         }
30871         
30872         Roo.each(['day', 'month', 'year'], function(t){
30873             cfg.cn.push({
30874                 tag : 'div',
30875                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30876             });
30877         }, this);
30878         
30879         return cfg;
30880     },
30881     
30882     inputEl: function ()
30883     {
30884         return this.el.select('.roo-date-split-field-group-value', true).first();
30885     },
30886     
30887     onRender : function(ct, position) 
30888     {
30889         var _this = this;
30890         
30891         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30892         
30893         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30894         
30895         this.dayField = new Roo.bootstrap.ComboBox({
30896             allowBlank : this.dayAllowBlank,
30897             alwaysQuery : true,
30898             displayField : 'value',
30899             editable : false,
30900             fieldLabel : '',
30901             forceSelection : true,
30902             mode : 'local',
30903             placeholder : this.dayPlaceholder,
30904             selectOnFocus : true,
30905             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30906             triggerAction : 'all',
30907             typeAhead : true,
30908             valueField : 'value',
30909             store : new Roo.data.SimpleStore({
30910                 data : (function() {    
30911                     var days = [];
30912                     _this.fireEvent('days', _this, days);
30913                     return days;
30914                 })(),
30915                 fields : [ 'value' ]
30916             }),
30917             listeners : {
30918                 select : function (_self, record, index)
30919                 {
30920                     _this.setValue(_this.getValue());
30921                 }
30922             }
30923         });
30924
30925         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30926         
30927         this.monthField = new Roo.bootstrap.MonthField({
30928             after : '<i class=\"fa fa-calendar\"></i>',
30929             allowBlank : this.monthAllowBlank,
30930             placeholder : this.monthPlaceholder,
30931             readOnly : true,
30932             listeners : {
30933                 render : function (_self)
30934                 {
30935                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30936                         e.preventDefault();
30937                         _self.focus();
30938                     });
30939                 },
30940                 select : function (_self, oldvalue, newvalue)
30941                 {
30942                     _this.setValue(_this.getValue());
30943                 }
30944             }
30945         });
30946         
30947         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30948         
30949         this.yearField = new Roo.bootstrap.ComboBox({
30950             allowBlank : this.yearAllowBlank,
30951             alwaysQuery : true,
30952             displayField : 'value',
30953             editable : false,
30954             fieldLabel : '',
30955             forceSelection : true,
30956             mode : 'local',
30957             placeholder : this.yearPlaceholder,
30958             selectOnFocus : true,
30959             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30960             triggerAction : 'all',
30961             typeAhead : true,
30962             valueField : 'value',
30963             store : new Roo.data.SimpleStore({
30964                 data : (function() {
30965                     var years = [];
30966                     _this.fireEvent('years', _this, years);
30967                     return years;
30968                 })(),
30969                 fields : [ 'value' ]
30970             }),
30971             listeners : {
30972                 select : function (_self, record, index)
30973                 {
30974                     _this.setValue(_this.getValue());
30975                 }
30976             }
30977         });
30978
30979         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30980     },
30981     
30982     setValue : function(v, format)
30983     {
30984         this.inputEl.dom.value = v;
30985         
30986         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30987         
30988         var d = Date.parseDate(v, f);
30989         
30990         if(!d){
30991             this.validate();
30992             return;
30993         }
30994         
30995         this.setDay(d.format(this.dayFormat));
30996         this.setMonth(d.format(this.monthFormat));
30997         this.setYear(d.format(this.yearFormat));
30998         
30999         this.validate();
31000         
31001         return;
31002     },
31003     
31004     setDay : function(v)
31005     {
31006         this.dayField.setValue(v);
31007         this.inputEl.dom.value = this.getValue();
31008         this.validate();
31009         return;
31010     },
31011     
31012     setMonth : function(v)
31013     {
31014         this.monthField.setValue(v, true);
31015         this.inputEl.dom.value = this.getValue();
31016         this.validate();
31017         return;
31018     },
31019     
31020     setYear : function(v)
31021     {
31022         this.yearField.setValue(v);
31023         this.inputEl.dom.value = this.getValue();
31024         this.validate();
31025         return;
31026     },
31027     
31028     getDay : function()
31029     {
31030         return this.dayField.getValue();
31031     },
31032     
31033     getMonth : function()
31034     {
31035         return this.monthField.getValue();
31036     },
31037     
31038     getYear : function()
31039     {
31040         return this.yearField.getValue();
31041     },
31042     
31043     getValue : function()
31044     {
31045         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31046         
31047         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31048         
31049         return date;
31050     },
31051     
31052     reset : function()
31053     {
31054         this.setDay('');
31055         this.setMonth('');
31056         this.setYear('');
31057         this.inputEl.dom.value = '';
31058         this.validate();
31059         return;
31060     },
31061     
31062     validate : function()
31063     {
31064         var d = this.dayField.validate();
31065         var m = this.monthField.validate();
31066         var y = this.yearField.validate();
31067         
31068         var valid = true;
31069         
31070         if(
31071                 (!this.dayAllowBlank && !d) ||
31072                 (!this.monthAllowBlank && !m) ||
31073                 (!this.yearAllowBlank && !y)
31074         ){
31075             valid = false;
31076         }
31077         
31078         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31079             return valid;
31080         }
31081         
31082         if(valid){
31083             this.markValid();
31084             return valid;
31085         }
31086         
31087         this.markInvalid();
31088         
31089         return valid;
31090     },
31091     
31092     markValid : function()
31093     {
31094         
31095         var label = this.el.select('label', true).first();
31096         var icon = this.el.select('i.fa-star', true).first();
31097
31098         if(label && icon){
31099             icon.remove();
31100         }
31101         
31102         this.fireEvent('valid', this);
31103     },
31104     
31105      /**
31106      * Mark this field as invalid
31107      * @param {String} msg The validation message
31108      */
31109     markInvalid : function(msg)
31110     {
31111         
31112         var label = this.el.select('label', true).first();
31113         var icon = this.el.select('i.fa-star', true).first();
31114
31115         if(label && !icon){
31116             this.el.select('.roo-date-split-field-label', true).createChild({
31117                 tag : 'i',
31118                 cls : 'text-danger fa fa-lg fa-star',
31119                 tooltip : 'This field is required',
31120                 style : 'margin-right:5px;'
31121             }, label, true);
31122         }
31123         
31124         this.fireEvent('invalid', this, msg);
31125     },
31126     
31127     clearInvalid : function()
31128     {
31129         var label = this.el.select('label', true).first();
31130         var icon = this.el.select('i.fa-star', true).first();
31131
31132         if(label && icon){
31133             icon.remove();
31134         }
31135         
31136         this.fireEvent('valid', this);
31137     },
31138     
31139     getName: function()
31140     {
31141         return this.name;
31142     }
31143     
31144 });
31145
31146  /**
31147  *
31148  * This is based on 
31149  * http://masonry.desandro.com
31150  *
31151  * The idea is to render all the bricks based on vertical width...
31152  *
31153  * The original code extends 'outlayer' - we might need to use that....
31154  * 
31155  */
31156
31157
31158 /**
31159  * @class Roo.bootstrap.LayoutMasonry
31160  * @extends Roo.bootstrap.Component
31161  * Bootstrap Layout Masonry class
31162  * 
31163  * @constructor
31164  * Create a new Element
31165  * @param {Object} config The config object
31166  */
31167
31168 Roo.bootstrap.LayoutMasonry = function(config){
31169     
31170     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31171     
31172     this.bricks = [];
31173     
31174     Roo.bootstrap.LayoutMasonry.register(this);
31175     
31176     this.addEvents({
31177         // raw events
31178         /**
31179          * @event layout
31180          * Fire after layout the items
31181          * @param {Roo.bootstrap.LayoutMasonry} this
31182          * @param {Roo.EventObject} e
31183          */
31184         "layout" : true
31185     });
31186     
31187 };
31188
31189 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31190     
31191     /**
31192      * @cfg {Boolean} isLayoutInstant = no animation?
31193      */   
31194     isLayoutInstant : false, // needed?
31195    
31196     /**
31197      * @cfg {Number} boxWidth  width of the columns
31198      */   
31199     boxWidth : 450,
31200     
31201       /**
31202      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31203      */   
31204     boxHeight : 0,
31205     
31206     /**
31207      * @cfg {Number} padWidth padding below box..
31208      */   
31209     padWidth : 10, 
31210     
31211     /**
31212      * @cfg {Number} gutter gutter width..
31213      */   
31214     gutter : 10,
31215     
31216      /**
31217      * @cfg {Number} maxCols maximum number of columns
31218      */   
31219     
31220     maxCols: 0,
31221     
31222     /**
31223      * @cfg {Boolean} isAutoInitial defalut true
31224      */   
31225     isAutoInitial : true, 
31226     
31227     containerWidth: 0,
31228     
31229     /**
31230      * @cfg {Boolean} isHorizontal defalut false
31231      */   
31232     isHorizontal : false, 
31233
31234     currentSize : null,
31235     
31236     tag: 'div',
31237     
31238     cls: '',
31239     
31240     bricks: null, //CompositeElement
31241     
31242     cols : 1,
31243     
31244     _isLayoutInited : false,
31245     
31246 //    isAlternative : false, // only use for vertical layout...
31247     
31248     /**
31249      * @cfg {Number} alternativePadWidth padding below box..
31250      */   
31251     alternativePadWidth : 50,
31252     
31253     selectedBrick : [],
31254     
31255     getAutoCreate : function(){
31256         
31257         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31258         
31259         var cfg = {
31260             tag: this.tag,
31261             cls: 'blog-masonary-wrapper ' + this.cls,
31262             cn : {
31263                 cls : 'mas-boxes masonary'
31264             }
31265         };
31266         
31267         return cfg;
31268     },
31269     
31270     getChildContainer: function( )
31271     {
31272         if (this.boxesEl) {
31273             return this.boxesEl;
31274         }
31275         
31276         this.boxesEl = this.el.select('.mas-boxes').first();
31277         
31278         return this.boxesEl;
31279     },
31280     
31281     
31282     initEvents : function()
31283     {
31284         var _this = this;
31285         
31286         if(this.isAutoInitial){
31287             Roo.log('hook children rendered');
31288             this.on('childrenrendered', function() {
31289                 Roo.log('children rendered');
31290                 _this.initial();
31291             } ,this);
31292         }
31293     },
31294     
31295     initial : function()
31296     {
31297         this.selectedBrick = [];
31298         
31299         this.currentSize = this.el.getBox(true);
31300         
31301         Roo.EventManager.onWindowResize(this.resize, this); 
31302
31303         if(!this.isAutoInitial){
31304             this.layout();
31305             return;
31306         }
31307         
31308         this.layout();
31309         
31310         return;
31311         //this.layout.defer(500,this);
31312         
31313     },
31314     
31315     resize : function()
31316     {
31317         var cs = this.el.getBox(true);
31318         
31319         if (
31320                 this.currentSize.width == cs.width && 
31321                 this.currentSize.x == cs.x && 
31322                 this.currentSize.height == cs.height && 
31323                 this.currentSize.y == cs.y 
31324         ) {
31325             Roo.log("no change in with or X or Y");
31326             return;
31327         }
31328         
31329         this.currentSize = cs;
31330         
31331         this.layout();
31332         
31333     },
31334     
31335     layout : function()
31336     {   
31337         this._resetLayout();
31338         
31339         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31340         
31341         this.layoutItems( isInstant );
31342       
31343         this._isLayoutInited = true;
31344         
31345         this.fireEvent('layout', this);
31346         
31347     },
31348     
31349     _resetLayout : function()
31350     {
31351         if(this.isHorizontal){
31352             this.horizontalMeasureColumns();
31353             return;
31354         }
31355         
31356         this.verticalMeasureColumns();
31357         
31358     },
31359     
31360     verticalMeasureColumns : function()
31361     {
31362         this.getContainerWidth();
31363         
31364 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31365 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31366 //            return;
31367 //        }
31368         
31369         var boxWidth = this.boxWidth + this.padWidth;
31370         
31371         if(this.containerWidth < this.boxWidth){
31372             boxWidth = this.containerWidth
31373         }
31374         
31375         var containerWidth = this.containerWidth;
31376         
31377         var cols = Math.floor(containerWidth / boxWidth);
31378         
31379         this.cols = Math.max( cols, 1 );
31380         
31381         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31382         
31383         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31384         
31385         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31386         
31387         this.colWidth = boxWidth + avail - this.padWidth;
31388         
31389         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31390         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31391     },
31392     
31393     horizontalMeasureColumns : function()
31394     {
31395         this.getContainerWidth();
31396         
31397         var boxWidth = this.boxWidth;
31398         
31399         if(this.containerWidth < boxWidth){
31400             boxWidth = this.containerWidth;
31401         }
31402         
31403         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31404         
31405         this.el.setHeight(boxWidth);
31406         
31407     },
31408     
31409     getContainerWidth : function()
31410     {
31411         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31412     },
31413     
31414     layoutItems : function( isInstant )
31415     {
31416         Roo.log(this.bricks);
31417         
31418         var items = Roo.apply([], this.bricks);
31419         
31420         if(this.isHorizontal){
31421             this._horizontalLayoutItems( items , isInstant );
31422             return;
31423         }
31424         
31425 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31426 //            this._verticalAlternativeLayoutItems( items , isInstant );
31427 //            return;
31428 //        }
31429         
31430         this._verticalLayoutItems( items , isInstant );
31431         
31432     },
31433     
31434     _verticalLayoutItems : function ( items , isInstant)
31435     {
31436         if ( !items || !items.length ) {
31437             return;
31438         }
31439         
31440         var standard = [
31441             ['xs', 'xs', 'xs', 'tall'],
31442             ['xs', 'xs', 'tall'],
31443             ['xs', 'xs', 'sm'],
31444             ['xs', 'xs', 'xs'],
31445             ['xs', 'tall'],
31446             ['xs', 'sm'],
31447             ['xs', 'xs'],
31448             ['xs'],
31449             
31450             ['sm', 'xs', 'xs'],
31451             ['sm', 'xs'],
31452             ['sm'],
31453             
31454             ['tall', 'xs', 'xs', 'xs'],
31455             ['tall', 'xs', 'xs'],
31456             ['tall', 'xs'],
31457             ['tall']
31458             
31459         ];
31460         
31461         var queue = [];
31462         
31463         var boxes = [];
31464         
31465         var box = [];
31466         
31467         Roo.each(items, function(item, k){
31468             
31469             switch (item.size) {
31470                 // these layouts take up a full box,
31471                 case 'md' :
31472                 case 'md-left' :
31473                 case 'md-right' :
31474                 case 'wide' :
31475                     
31476                     if(box.length){
31477                         boxes.push(box);
31478                         box = [];
31479                     }
31480                     
31481                     boxes.push([item]);
31482                     
31483                     break;
31484                     
31485                 case 'xs' :
31486                 case 'sm' :
31487                 case 'tall' :
31488                     
31489                     box.push(item);
31490                     
31491                     break;
31492                 default :
31493                     break;
31494                     
31495             }
31496             
31497         }, this);
31498         
31499         if(box.length){
31500             boxes.push(box);
31501             box = [];
31502         }
31503         
31504         var filterPattern = function(box, length)
31505         {
31506             if(!box.length){
31507                 return;
31508             }
31509             
31510             var match = false;
31511             
31512             var pattern = box.slice(0, length);
31513             
31514             var format = [];
31515             
31516             Roo.each(pattern, function(i){
31517                 format.push(i.size);
31518             }, this);
31519             
31520             Roo.each(standard, function(s){
31521                 
31522                 if(String(s) != String(format)){
31523                     return;
31524                 }
31525                 
31526                 match = true;
31527                 return false;
31528                 
31529             }, this);
31530             
31531             if(!match && length == 1){
31532                 return;
31533             }
31534             
31535             if(!match){
31536                 filterPattern(box, length - 1);
31537                 return;
31538             }
31539                 
31540             queue.push(pattern);
31541
31542             box = box.slice(length, box.length);
31543
31544             filterPattern(box, 4);
31545
31546             return;
31547             
31548         }
31549         
31550         Roo.each(boxes, function(box, k){
31551             
31552             if(!box.length){
31553                 return;
31554             }
31555             
31556             if(box.length == 1){
31557                 queue.push(box);
31558                 return;
31559             }
31560             
31561             filterPattern(box, 4);
31562             
31563         }, this);
31564         
31565         this._processVerticalLayoutQueue( queue, isInstant );
31566         
31567     },
31568     
31569 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31570 //    {
31571 //        if ( !items || !items.length ) {
31572 //            return;
31573 //        }
31574 //
31575 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31576 //        
31577 //    },
31578     
31579     _horizontalLayoutItems : function ( items , isInstant)
31580     {
31581         if ( !items || !items.length || items.length < 3) {
31582             return;
31583         }
31584         
31585         items.reverse();
31586         
31587         var eItems = items.slice(0, 3);
31588         
31589         items = items.slice(3, items.length);
31590         
31591         var standard = [
31592             ['xs', 'xs', 'xs', 'wide'],
31593             ['xs', 'xs', 'wide'],
31594             ['xs', 'xs', 'sm'],
31595             ['xs', 'xs', 'xs'],
31596             ['xs', 'wide'],
31597             ['xs', 'sm'],
31598             ['xs', 'xs'],
31599             ['xs'],
31600             
31601             ['sm', 'xs', 'xs'],
31602             ['sm', 'xs'],
31603             ['sm'],
31604             
31605             ['wide', 'xs', 'xs', 'xs'],
31606             ['wide', 'xs', 'xs'],
31607             ['wide', 'xs'],
31608             ['wide'],
31609             
31610             ['wide-thin']
31611         ];
31612         
31613         var queue = [];
31614         
31615         var boxes = [];
31616         
31617         var box = [];
31618         
31619         Roo.each(items, function(item, k){
31620             
31621             switch (item.size) {
31622                 case 'md' :
31623                 case 'md-left' :
31624                 case 'md-right' :
31625                 case 'tall' :
31626                     
31627                     if(box.length){
31628                         boxes.push(box);
31629                         box = [];
31630                     }
31631                     
31632                     boxes.push([item]);
31633                     
31634                     break;
31635                     
31636                 case 'xs' :
31637                 case 'sm' :
31638                 case 'wide' :
31639                 case 'wide-thin' :
31640                     
31641                     box.push(item);
31642                     
31643                     break;
31644                 default :
31645                     break;
31646                     
31647             }
31648             
31649         }, this);
31650         
31651         if(box.length){
31652             boxes.push(box);
31653             box = [];
31654         }
31655         
31656         var filterPattern = function(box, length)
31657         {
31658             if(!box.length){
31659                 return;
31660             }
31661             
31662             var match = false;
31663             
31664             var pattern = box.slice(0, length);
31665             
31666             var format = [];
31667             
31668             Roo.each(pattern, function(i){
31669                 format.push(i.size);
31670             }, this);
31671             
31672             Roo.each(standard, function(s){
31673                 
31674                 if(String(s) != String(format)){
31675                     return;
31676                 }
31677                 
31678                 match = true;
31679                 return false;
31680                 
31681             }, this);
31682             
31683             if(!match && length == 1){
31684                 return;
31685             }
31686             
31687             if(!match){
31688                 filterPattern(box, length - 1);
31689                 return;
31690             }
31691                 
31692             queue.push(pattern);
31693
31694             box = box.slice(length, box.length);
31695
31696             filterPattern(box, 4);
31697
31698             return;
31699             
31700         }
31701         
31702         Roo.each(boxes, function(box, k){
31703             
31704             if(!box.length){
31705                 return;
31706             }
31707             
31708             if(box.length == 1){
31709                 queue.push(box);
31710                 return;
31711             }
31712             
31713             filterPattern(box, 4);
31714             
31715         }, this);
31716         
31717         
31718         var prune = [];
31719         
31720         var pos = this.el.getBox(true);
31721         
31722         var minX = pos.x;
31723         
31724         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31725         
31726         var hit_end = false;
31727         
31728         Roo.each(queue, function(box){
31729             
31730             if(hit_end){
31731                 
31732                 Roo.each(box, function(b){
31733                 
31734                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31735                     b.el.hide();
31736
31737                 }, this);
31738
31739                 return;
31740             }
31741             
31742             var mx = 0;
31743             
31744             Roo.each(box, function(b){
31745                 
31746                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31747                 b.el.show();
31748
31749                 mx = Math.max(mx, b.x);
31750                 
31751             }, this);
31752             
31753             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31754             
31755             if(maxX < minX){
31756                 
31757                 Roo.each(box, function(b){
31758                 
31759                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31760                     b.el.hide();
31761                     
31762                 }, this);
31763                 
31764                 hit_end = true;
31765                 
31766                 return;
31767             }
31768             
31769             prune.push(box);
31770             
31771         }, this);
31772         
31773         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31774     },
31775     
31776     /** Sets position of item in DOM
31777     * @param {Element} item
31778     * @param {Number} x - horizontal position
31779     * @param {Number} y - vertical position
31780     * @param {Boolean} isInstant - disables transitions
31781     */
31782     _processVerticalLayoutQueue : function( queue, isInstant )
31783     {
31784         var pos = this.el.getBox(true);
31785         var x = pos.x;
31786         var y = pos.y;
31787         var maxY = [];
31788         
31789         for (var i = 0; i < this.cols; i++){
31790             maxY[i] = pos.y;
31791         }
31792         
31793         Roo.each(queue, function(box, k){
31794             
31795             var col = k % this.cols;
31796             
31797             Roo.each(box, function(b,kk){
31798                 
31799                 b.el.position('absolute');
31800                 
31801                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31802                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31803                 
31804                 if(b.size == 'md-left' || b.size == 'md-right'){
31805                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31806                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31807                 }
31808                 
31809                 b.el.setWidth(width);
31810                 b.el.setHeight(height);
31811                 // iframe?
31812                 b.el.select('iframe',true).setSize(width,height);
31813                 
31814             }, this);
31815             
31816             for (var i = 0; i < this.cols; i++){
31817                 
31818                 if(maxY[i] < maxY[col]){
31819                     col = i;
31820                     continue;
31821                 }
31822                 
31823                 col = Math.min(col, i);
31824                 
31825             }
31826             
31827             x = pos.x + col * (this.colWidth + this.padWidth);
31828             
31829             y = maxY[col];
31830             
31831             var positions = [];
31832             
31833             switch (box.length){
31834                 case 1 :
31835                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31836                     break;
31837                 case 2 :
31838                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31839                     break;
31840                 case 3 :
31841                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31842                     break;
31843                 case 4 :
31844                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31845                     break;
31846                 default :
31847                     break;
31848             }
31849             
31850             Roo.each(box, function(b,kk){
31851                 
31852                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31853                 
31854                 var sz = b.el.getSize();
31855                 
31856                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31857                 
31858             }, this);
31859             
31860         }, this);
31861         
31862         var mY = 0;
31863         
31864         for (var i = 0; i < this.cols; i++){
31865             mY = Math.max(mY, maxY[i]);
31866         }
31867         
31868         this.el.setHeight(mY - pos.y);
31869         
31870     },
31871     
31872 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31873 //    {
31874 //        var pos = this.el.getBox(true);
31875 //        var x = pos.x;
31876 //        var y = pos.y;
31877 //        var maxX = pos.right;
31878 //        
31879 //        var maxHeight = 0;
31880 //        
31881 //        Roo.each(items, function(item, k){
31882 //            
31883 //            var c = k % 2;
31884 //            
31885 //            item.el.position('absolute');
31886 //                
31887 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31888 //
31889 //            item.el.setWidth(width);
31890 //
31891 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31892 //
31893 //            item.el.setHeight(height);
31894 //            
31895 //            if(c == 0){
31896 //                item.el.setXY([x, y], isInstant ? false : true);
31897 //            } else {
31898 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31899 //            }
31900 //            
31901 //            y = y + height + this.alternativePadWidth;
31902 //            
31903 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31904 //            
31905 //        }, this);
31906 //        
31907 //        this.el.setHeight(maxHeight);
31908 //        
31909 //    },
31910     
31911     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31912     {
31913         var pos = this.el.getBox(true);
31914         
31915         var minX = pos.x;
31916         var minY = pos.y;
31917         
31918         var maxX = pos.right;
31919         
31920         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31921         
31922         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31923         
31924         Roo.each(queue, function(box, k){
31925             
31926             Roo.each(box, function(b, kk){
31927                 
31928                 b.el.position('absolute');
31929                 
31930                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31931                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31932                 
31933                 if(b.size == 'md-left' || b.size == 'md-right'){
31934                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31935                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31936                 }
31937                 
31938                 b.el.setWidth(width);
31939                 b.el.setHeight(height);
31940                 
31941             }, this);
31942             
31943             if(!box.length){
31944                 return;
31945             }
31946             
31947             var positions = [];
31948             
31949             switch (box.length){
31950                 case 1 :
31951                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31952                     break;
31953                 case 2 :
31954                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31955                     break;
31956                 case 3 :
31957                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31958                     break;
31959                 case 4 :
31960                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31961                     break;
31962                 default :
31963                     break;
31964             }
31965             
31966             Roo.each(box, function(b,kk){
31967                 
31968                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31969                 
31970                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31971                 
31972             }, this);
31973             
31974         }, this);
31975         
31976     },
31977     
31978     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31979     {
31980         Roo.each(eItems, function(b,k){
31981             
31982             b.size = (k == 0) ? 'sm' : 'xs';
31983             b.x = (k == 0) ? 2 : 1;
31984             b.y = (k == 0) ? 2 : 1;
31985             
31986             b.el.position('absolute');
31987             
31988             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31989                 
31990             b.el.setWidth(width);
31991             
31992             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31993             
31994             b.el.setHeight(height);
31995             
31996         }, this);
31997
31998         var positions = [];
31999         
32000         positions.push({
32001             x : maxX - this.unitWidth * 2 - this.gutter,
32002             y : minY
32003         });
32004         
32005         positions.push({
32006             x : maxX - this.unitWidth,
32007             y : minY + (this.unitWidth + this.gutter) * 2
32008         });
32009         
32010         positions.push({
32011             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32012             y : minY
32013         });
32014         
32015         Roo.each(eItems, function(b,k){
32016             
32017             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32018
32019         }, this);
32020         
32021     },
32022     
32023     getVerticalOneBoxColPositions : function(x, y, box)
32024     {
32025         var pos = [];
32026         
32027         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32028         
32029         if(box[0].size == 'md-left'){
32030             rand = 0;
32031         }
32032         
32033         if(box[0].size == 'md-right'){
32034             rand = 1;
32035         }
32036         
32037         pos.push({
32038             x : x + (this.unitWidth + this.gutter) * rand,
32039             y : y
32040         });
32041         
32042         return pos;
32043     },
32044     
32045     getVerticalTwoBoxColPositions : function(x, y, box)
32046     {
32047         var pos = [];
32048         
32049         if(box[0].size == 'xs'){
32050             
32051             pos.push({
32052                 x : x,
32053                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32054             });
32055
32056             pos.push({
32057                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32058                 y : y
32059             });
32060             
32061             return pos;
32062             
32063         }
32064         
32065         pos.push({
32066             x : x,
32067             y : y
32068         });
32069
32070         pos.push({
32071             x : x + (this.unitWidth + this.gutter) * 2,
32072             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32073         });
32074         
32075         return pos;
32076         
32077     },
32078     
32079     getVerticalThreeBoxColPositions : function(x, y, box)
32080     {
32081         var pos = [];
32082         
32083         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32084             
32085             pos.push({
32086                 x : x,
32087                 y : y
32088             });
32089
32090             pos.push({
32091                 x : x + (this.unitWidth + this.gutter) * 1,
32092                 y : y
32093             });
32094             
32095             pos.push({
32096                 x : x + (this.unitWidth + this.gutter) * 2,
32097                 y : y
32098             });
32099             
32100             return pos;
32101             
32102         }
32103         
32104         if(box[0].size == 'xs' && box[1].size == 'xs'){
32105             
32106             pos.push({
32107                 x : x,
32108                 y : y
32109             });
32110
32111             pos.push({
32112                 x : x,
32113                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32114             });
32115             
32116             pos.push({
32117                 x : x + (this.unitWidth + this.gutter) * 1,
32118                 y : y
32119             });
32120             
32121             return pos;
32122             
32123         }
32124         
32125         pos.push({
32126             x : x,
32127             y : y
32128         });
32129
32130         pos.push({
32131             x : x + (this.unitWidth + this.gutter) * 2,
32132             y : y
32133         });
32134
32135         pos.push({
32136             x : x + (this.unitWidth + this.gutter) * 2,
32137             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32138         });
32139             
32140         return pos;
32141         
32142     },
32143     
32144     getVerticalFourBoxColPositions : function(x, y, box)
32145     {
32146         var pos = [];
32147         
32148         if(box[0].size == 'xs'){
32149             
32150             pos.push({
32151                 x : x,
32152                 y : y
32153             });
32154
32155             pos.push({
32156                 x : x,
32157                 y : y + (this.unitHeight + this.gutter) * 1
32158             });
32159             
32160             pos.push({
32161                 x : x,
32162                 y : y + (this.unitHeight + this.gutter) * 2
32163             });
32164             
32165             pos.push({
32166                 x : x + (this.unitWidth + this.gutter) * 1,
32167                 y : y
32168             });
32169             
32170             return pos;
32171             
32172         }
32173         
32174         pos.push({
32175             x : x,
32176             y : y
32177         });
32178
32179         pos.push({
32180             x : x + (this.unitWidth + this.gutter) * 2,
32181             y : y
32182         });
32183
32184         pos.push({
32185             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32186             y : y + (this.unitHeight + this.gutter) * 1
32187         });
32188
32189         pos.push({
32190             x : x + (this.unitWidth + this.gutter) * 2,
32191             y : y + (this.unitWidth + this.gutter) * 2
32192         });
32193
32194         return pos;
32195         
32196     },
32197     
32198     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32199     {
32200         var pos = [];
32201         
32202         if(box[0].size == 'md-left'){
32203             pos.push({
32204                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32205                 y : minY
32206             });
32207             
32208             return pos;
32209         }
32210         
32211         if(box[0].size == 'md-right'){
32212             pos.push({
32213                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32214                 y : minY + (this.unitWidth + this.gutter) * 1
32215             });
32216             
32217             return pos;
32218         }
32219         
32220         var rand = Math.floor(Math.random() * (4 - box[0].y));
32221         
32222         pos.push({
32223             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32224             y : minY + (this.unitWidth + this.gutter) * rand
32225         });
32226         
32227         return pos;
32228         
32229     },
32230     
32231     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32232     {
32233         var pos = [];
32234         
32235         if(box[0].size == 'xs'){
32236             
32237             pos.push({
32238                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32239                 y : minY
32240             });
32241
32242             pos.push({
32243                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32244                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32245             });
32246             
32247             return pos;
32248             
32249         }
32250         
32251         pos.push({
32252             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32253             y : minY
32254         });
32255
32256         pos.push({
32257             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32258             y : minY + (this.unitWidth + this.gutter) * 2
32259         });
32260         
32261         return pos;
32262         
32263     },
32264     
32265     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32266     {
32267         var pos = [];
32268         
32269         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32270             
32271             pos.push({
32272                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32273                 y : minY
32274             });
32275
32276             pos.push({
32277                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32278                 y : minY + (this.unitWidth + this.gutter) * 1
32279             });
32280             
32281             pos.push({
32282                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32283                 y : minY + (this.unitWidth + this.gutter) * 2
32284             });
32285             
32286             return pos;
32287             
32288         }
32289         
32290         if(box[0].size == 'xs' && box[1].size == 'xs'){
32291             
32292             pos.push({
32293                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32294                 y : minY
32295             });
32296
32297             pos.push({
32298                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32299                 y : minY
32300             });
32301             
32302             pos.push({
32303                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32304                 y : minY + (this.unitWidth + this.gutter) * 1
32305             });
32306             
32307             return pos;
32308             
32309         }
32310         
32311         pos.push({
32312             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32313             y : minY
32314         });
32315
32316         pos.push({
32317             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32318             y : minY + (this.unitWidth + this.gutter) * 2
32319         });
32320
32321         pos.push({
32322             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32323             y : minY + (this.unitWidth + this.gutter) * 2
32324         });
32325             
32326         return pos;
32327         
32328     },
32329     
32330     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32331     {
32332         var pos = [];
32333         
32334         if(box[0].size == 'xs'){
32335             
32336             pos.push({
32337                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32338                 y : minY
32339             });
32340
32341             pos.push({
32342                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32343                 y : minY
32344             });
32345             
32346             pos.push({
32347                 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),
32348                 y : minY
32349             });
32350             
32351             pos.push({
32352                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32353                 y : minY + (this.unitWidth + this.gutter) * 1
32354             });
32355             
32356             return pos;
32357             
32358         }
32359         
32360         pos.push({
32361             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32362             y : minY
32363         });
32364         
32365         pos.push({
32366             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32367             y : minY + (this.unitWidth + this.gutter) * 2
32368         });
32369         
32370         pos.push({
32371             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32372             y : minY + (this.unitWidth + this.gutter) * 2
32373         });
32374         
32375         pos.push({
32376             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),
32377             y : minY + (this.unitWidth + this.gutter) * 2
32378         });
32379
32380         return pos;
32381         
32382     },
32383     
32384     /**
32385     * remove a Masonry Brick
32386     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32387     */
32388     removeBrick : function(brick_id)
32389     {
32390         if (!brick_id) {
32391             return;
32392         }
32393         
32394         for (var i = 0; i<this.bricks.length; i++) {
32395             if (this.bricks[i].id == brick_id) {
32396                 this.bricks.splice(i,1);
32397                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32398                 this.initial();
32399             }
32400         }
32401     },
32402     
32403     /**
32404     * adds a Masonry Brick
32405     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32406     */
32407     addBrick : function(cfg)
32408     {
32409         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32410         //this.register(cn);
32411         cn.parentId = this.id;
32412         cn.render(this.el);
32413         return cn;
32414     },
32415     
32416     /**
32417     * register a Masonry Brick
32418     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32419     */
32420     
32421     register : function(brick)
32422     {
32423         this.bricks.push(brick);
32424         brick.masonryId = this.id;
32425     },
32426     
32427     /**
32428     * clear all the Masonry Brick
32429     */
32430     clearAll : function()
32431     {
32432         this.bricks = [];
32433         //this.getChildContainer().dom.innerHTML = "";
32434         this.el.dom.innerHTML = '';
32435     },
32436     
32437     getSelected : function()
32438     {
32439         if (!this.selectedBrick) {
32440             return false;
32441         }
32442         
32443         return this.selectedBrick;
32444     }
32445 });
32446
32447 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32448     
32449     groups: {},
32450      /**
32451     * register a Masonry Layout
32452     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32453     */
32454     
32455     register : function(layout)
32456     {
32457         this.groups[layout.id] = layout;
32458     },
32459     /**
32460     * fetch a  Masonry Layout based on the masonry layout ID
32461     * @param {string} the masonry layout to add
32462     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32463     */
32464     
32465     get: function(layout_id) {
32466         if (typeof(this.groups[layout_id]) == 'undefined') {
32467             return false;
32468         }
32469         return this.groups[layout_id] ;
32470     }
32471     
32472     
32473     
32474 });
32475
32476  
32477
32478  /**
32479  *
32480  * This is based on 
32481  * http://masonry.desandro.com
32482  *
32483  * The idea is to render all the bricks based on vertical width...
32484  *
32485  * The original code extends 'outlayer' - we might need to use that....
32486  * 
32487  */
32488
32489
32490 /**
32491  * @class Roo.bootstrap.LayoutMasonryAuto
32492  * @extends Roo.bootstrap.Component
32493  * Bootstrap Layout Masonry class
32494  * 
32495  * @constructor
32496  * Create a new Element
32497  * @param {Object} config The config object
32498  */
32499
32500 Roo.bootstrap.LayoutMasonryAuto = function(config){
32501     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32502 };
32503
32504 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32505     
32506       /**
32507      * @cfg {Boolean} isFitWidth  - resize the width..
32508      */   
32509     isFitWidth : false,  // options..
32510     /**
32511      * @cfg {Boolean} isOriginLeft = left align?
32512      */   
32513     isOriginLeft : true,
32514     /**
32515      * @cfg {Boolean} isOriginTop = top align?
32516      */   
32517     isOriginTop : false,
32518     /**
32519      * @cfg {Boolean} isLayoutInstant = no animation?
32520      */   
32521     isLayoutInstant : false, // needed?
32522     /**
32523      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32524      */   
32525     isResizingContainer : true,
32526     /**
32527      * @cfg {Number} columnWidth  width of the columns 
32528      */   
32529     
32530     columnWidth : 0,
32531     
32532     /**
32533      * @cfg {Number} maxCols maximum number of columns
32534      */   
32535     
32536     maxCols: 0,
32537     /**
32538      * @cfg {Number} padHeight padding below box..
32539      */   
32540     
32541     padHeight : 10, 
32542     
32543     /**
32544      * @cfg {Boolean} isAutoInitial defalut true
32545      */   
32546     
32547     isAutoInitial : true, 
32548     
32549     // private?
32550     gutter : 0,
32551     
32552     containerWidth: 0,
32553     initialColumnWidth : 0,
32554     currentSize : null,
32555     
32556     colYs : null, // array.
32557     maxY : 0,
32558     padWidth: 10,
32559     
32560     
32561     tag: 'div',
32562     cls: '',
32563     bricks: null, //CompositeElement
32564     cols : 0, // array?
32565     // element : null, // wrapped now this.el
32566     _isLayoutInited : null, 
32567     
32568     
32569     getAutoCreate : function(){
32570         
32571         var cfg = {
32572             tag: this.tag,
32573             cls: 'blog-masonary-wrapper ' + this.cls,
32574             cn : {
32575                 cls : 'mas-boxes masonary'
32576             }
32577         };
32578         
32579         return cfg;
32580     },
32581     
32582     getChildContainer: function( )
32583     {
32584         if (this.boxesEl) {
32585             return this.boxesEl;
32586         }
32587         
32588         this.boxesEl = this.el.select('.mas-boxes').first();
32589         
32590         return this.boxesEl;
32591     },
32592     
32593     
32594     initEvents : function()
32595     {
32596         var _this = this;
32597         
32598         if(this.isAutoInitial){
32599             Roo.log('hook children rendered');
32600             this.on('childrenrendered', function() {
32601                 Roo.log('children rendered');
32602                 _this.initial();
32603             } ,this);
32604         }
32605         
32606     },
32607     
32608     initial : function()
32609     {
32610         this.reloadItems();
32611
32612         this.currentSize = this.el.getBox(true);
32613
32614         /// was window resize... - let's see if this works..
32615         Roo.EventManager.onWindowResize(this.resize, this); 
32616
32617         if(!this.isAutoInitial){
32618             this.layout();
32619             return;
32620         }
32621         
32622         this.layout.defer(500,this);
32623     },
32624     
32625     reloadItems: function()
32626     {
32627         this.bricks = this.el.select('.masonry-brick', true);
32628         
32629         this.bricks.each(function(b) {
32630             //Roo.log(b.getSize());
32631             if (!b.attr('originalwidth')) {
32632                 b.attr('originalwidth',  b.getSize().width);
32633             }
32634             
32635         });
32636         
32637         Roo.log(this.bricks.elements.length);
32638     },
32639     
32640     resize : function()
32641     {
32642         Roo.log('resize');
32643         var cs = this.el.getBox(true);
32644         
32645         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32646             Roo.log("no change in with or X");
32647             return;
32648         }
32649         this.currentSize = cs;
32650         this.layout();
32651     },
32652     
32653     layout : function()
32654     {
32655          Roo.log('layout');
32656         this._resetLayout();
32657         //this._manageStamps();
32658       
32659         // don't animate first layout
32660         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32661         this.layoutItems( isInstant );
32662       
32663         // flag for initalized
32664         this._isLayoutInited = true;
32665     },
32666     
32667     layoutItems : function( isInstant )
32668     {
32669         //var items = this._getItemsForLayout( this.items );
32670         // original code supports filtering layout items.. we just ignore it..
32671         
32672         this._layoutItems( this.bricks , isInstant );
32673       
32674         this._postLayout();
32675     },
32676     _layoutItems : function ( items , isInstant)
32677     {
32678        //this.fireEvent( 'layout', this, items );
32679     
32680
32681         if ( !items || !items.elements.length ) {
32682           // no items, emit event with empty array
32683             return;
32684         }
32685
32686         var queue = [];
32687         items.each(function(item) {
32688             Roo.log("layout item");
32689             Roo.log(item);
32690             // get x/y object from method
32691             var position = this._getItemLayoutPosition( item );
32692             // enqueue
32693             position.item = item;
32694             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32695             queue.push( position );
32696         }, this);
32697       
32698         this._processLayoutQueue( queue );
32699     },
32700     /** Sets position of item in DOM
32701     * @param {Element} item
32702     * @param {Number} x - horizontal position
32703     * @param {Number} y - vertical position
32704     * @param {Boolean} isInstant - disables transitions
32705     */
32706     _processLayoutQueue : function( queue )
32707     {
32708         for ( var i=0, len = queue.length; i < len; i++ ) {
32709             var obj = queue[i];
32710             obj.item.position('absolute');
32711             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32712         }
32713     },
32714       
32715     
32716     /**
32717     * Any logic you want to do after each layout,
32718     * i.e. size the container
32719     */
32720     _postLayout : function()
32721     {
32722         this.resizeContainer();
32723     },
32724     
32725     resizeContainer : function()
32726     {
32727         if ( !this.isResizingContainer ) {
32728             return;
32729         }
32730         var size = this._getContainerSize();
32731         if ( size ) {
32732             this.el.setSize(size.width,size.height);
32733             this.boxesEl.setSize(size.width,size.height);
32734         }
32735     },
32736     
32737     
32738     
32739     _resetLayout : function()
32740     {
32741         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32742         this.colWidth = this.el.getWidth();
32743         //this.gutter = this.el.getWidth(); 
32744         
32745         this.measureColumns();
32746
32747         // reset column Y
32748         var i = this.cols;
32749         this.colYs = [];
32750         while (i--) {
32751             this.colYs.push( 0 );
32752         }
32753     
32754         this.maxY = 0;
32755     },
32756
32757     measureColumns : function()
32758     {
32759         this.getContainerWidth();
32760       // if columnWidth is 0, default to outerWidth of first item
32761         if ( !this.columnWidth ) {
32762             var firstItem = this.bricks.first();
32763             Roo.log(firstItem);
32764             this.columnWidth  = this.containerWidth;
32765             if (firstItem && firstItem.attr('originalwidth') ) {
32766                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32767             }
32768             // columnWidth fall back to item of first element
32769             Roo.log("set column width?");
32770                         this.initialColumnWidth = this.columnWidth  ;
32771
32772             // if first elem has no width, default to size of container
32773             
32774         }
32775         
32776         
32777         if (this.initialColumnWidth) {
32778             this.columnWidth = this.initialColumnWidth;
32779         }
32780         
32781         
32782             
32783         // column width is fixed at the top - however if container width get's smaller we should
32784         // reduce it...
32785         
32786         // this bit calcs how man columns..
32787             
32788         var columnWidth = this.columnWidth += this.gutter;
32789       
32790         // calculate columns
32791         var containerWidth = this.containerWidth + this.gutter;
32792         
32793         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32794         // fix rounding errors, typically with gutters
32795         var excess = columnWidth - containerWidth % columnWidth;
32796         
32797         
32798         // if overshoot is less than a pixel, round up, otherwise floor it
32799         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32800         cols = Math[ mathMethod ]( cols );
32801         this.cols = Math.max( cols, 1 );
32802         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32803         
32804          // padding positioning..
32805         var totalColWidth = this.cols * this.columnWidth;
32806         var padavail = this.containerWidth - totalColWidth;
32807         // so for 2 columns - we need 3 'pads'
32808         
32809         var padNeeded = (1+this.cols) * this.padWidth;
32810         
32811         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32812         
32813         this.columnWidth += padExtra
32814         //this.padWidth = Math.floor(padavail /  ( this.cols));
32815         
32816         // adjust colum width so that padding is fixed??
32817         
32818         // we have 3 columns ... total = width * 3
32819         // we have X left over... that should be used by 
32820         
32821         //if (this.expandC) {
32822             
32823         //}
32824         
32825         
32826         
32827     },
32828     
32829     getContainerWidth : function()
32830     {
32831        /* // container is parent if fit width
32832         var container = this.isFitWidth ? this.element.parentNode : this.element;
32833         // check that this.size and size are there
32834         // IE8 triggers resize on body size change, so they might not be
32835         
32836         var size = getSize( container );  //FIXME
32837         this.containerWidth = size && size.innerWidth; //FIXME
32838         */
32839          
32840         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32841         
32842     },
32843     
32844     _getItemLayoutPosition : function( item )  // what is item?
32845     {
32846         // we resize the item to our columnWidth..
32847       
32848         item.setWidth(this.columnWidth);
32849         item.autoBoxAdjust  = false;
32850         
32851         var sz = item.getSize();
32852  
32853         // how many columns does this brick span
32854         var remainder = this.containerWidth % this.columnWidth;
32855         
32856         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32857         // round if off by 1 pixel, otherwise use ceil
32858         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32859         colSpan = Math.min( colSpan, this.cols );
32860         
32861         // normally this should be '1' as we dont' currently allow multi width columns..
32862         
32863         var colGroup = this._getColGroup( colSpan );
32864         // get the minimum Y value from the columns
32865         var minimumY = Math.min.apply( Math, colGroup );
32866         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32867         
32868         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32869          
32870         // position the brick
32871         var position = {
32872             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32873             y: this.currentSize.y + minimumY + this.padHeight
32874         };
32875         
32876         Roo.log(position);
32877         // apply setHeight to necessary columns
32878         var setHeight = minimumY + sz.height + this.padHeight;
32879         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32880         
32881         var setSpan = this.cols + 1 - colGroup.length;
32882         for ( var i = 0; i < setSpan; i++ ) {
32883           this.colYs[ shortColIndex + i ] = setHeight ;
32884         }
32885       
32886         return position;
32887     },
32888     
32889     /**
32890      * @param {Number} colSpan - number of columns the element spans
32891      * @returns {Array} colGroup
32892      */
32893     _getColGroup : function( colSpan )
32894     {
32895         if ( colSpan < 2 ) {
32896           // if brick spans only one column, use all the column Ys
32897           return this.colYs;
32898         }
32899       
32900         var colGroup = [];
32901         // how many different places could this brick fit horizontally
32902         var groupCount = this.cols + 1 - colSpan;
32903         // for each group potential horizontal position
32904         for ( var i = 0; i < groupCount; i++ ) {
32905           // make an array of colY values for that one group
32906           var groupColYs = this.colYs.slice( i, i + colSpan );
32907           // and get the max value of the array
32908           colGroup[i] = Math.max.apply( Math, groupColYs );
32909         }
32910         return colGroup;
32911     },
32912     /*
32913     _manageStamp : function( stamp )
32914     {
32915         var stampSize =  stamp.getSize();
32916         var offset = stamp.getBox();
32917         // get the columns that this stamp affects
32918         var firstX = this.isOriginLeft ? offset.x : offset.right;
32919         var lastX = firstX + stampSize.width;
32920         var firstCol = Math.floor( firstX / this.columnWidth );
32921         firstCol = Math.max( 0, firstCol );
32922         
32923         var lastCol = Math.floor( lastX / this.columnWidth );
32924         // lastCol should not go over if multiple of columnWidth #425
32925         lastCol -= lastX % this.columnWidth ? 0 : 1;
32926         lastCol = Math.min( this.cols - 1, lastCol );
32927         
32928         // set colYs to bottom of the stamp
32929         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32930             stampSize.height;
32931             
32932         for ( var i = firstCol; i <= lastCol; i++ ) {
32933           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32934         }
32935     },
32936     */
32937     
32938     _getContainerSize : function()
32939     {
32940         this.maxY = Math.max.apply( Math, this.colYs );
32941         var size = {
32942             height: this.maxY
32943         };
32944       
32945         if ( this.isFitWidth ) {
32946             size.width = this._getContainerFitWidth();
32947         }
32948       
32949         return size;
32950     },
32951     
32952     _getContainerFitWidth : function()
32953     {
32954         var unusedCols = 0;
32955         // count unused columns
32956         var i = this.cols;
32957         while ( --i ) {
32958           if ( this.colYs[i] !== 0 ) {
32959             break;
32960           }
32961           unusedCols++;
32962         }
32963         // fit container to columns that have been used
32964         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32965     },
32966     
32967     needsResizeLayout : function()
32968     {
32969         var previousWidth = this.containerWidth;
32970         this.getContainerWidth();
32971         return previousWidth !== this.containerWidth;
32972     }
32973  
32974 });
32975
32976  
32977
32978  /*
32979  * - LGPL
32980  *
32981  * element
32982  * 
32983  */
32984
32985 /**
32986  * @class Roo.bootstrap.MasonryBrick
32987  * @extends Roo.bootstrap.Component
32988  * Bootstrap MasonryBrick class
32989  * 
32990  * @constructor
32991  * Create a new MasonryBrick
32992  * @param {Object} config The config object
32993  */
32994
32995 Roo.bootstrap.MasonryBrick = function(config){
32996     
32997     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32998     
32999     Roo.bootstrap.MasonryBrick.register(this);
33000     
33001     this.addEvents({
33002         // raw events
33003         /**
33004          * @event click
33005          * When a MasonryBrick is clcik
33006          * @param {Roo.bootstrap.MasonryBrick} this
33007          * @param {Roo.EventObject} e
33008          */
33009         "click" : true
33010     });
33011 };
33012
33013 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33014     
33015     /**
33016      * @cfg {String} title
33017      */   
33018     title : '',
33019     /**
33020      * @cfg {String} html
33021      */   
33022     html : '',
33023     /**
33024      * @cfg {String} bgimage
33025      */   
33026     bgimage : '',
33027     /**
33028      * @cfg {String} videourl
33029      */   
33030     videourl : '',
33031     /**
33032      * @cfg {String} cls
33033      */   
33034     cls : '',
33035     /**
33036      * @cfg {String} href
33037      */   
33038     href : '',
33039     /**
33040      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33041      */   
33042     size : 'xs',
33043     
33044     /**
33045      * @cfg {String} placetitle (center|bottom)
33046      */   
33047     placetitle : '',
33048     
33049     /**
33050      * @cfg {Boolean} isFitContainer defalut true
33051      */   
33052     isFitContainer : true, 
33053     
33054     /**
33055      * @cfg {Boolean} preventDefault defalut false
33056      */   
33057     preventDefault : false, 
33058     
33059     /**
33060      * @cfg {Boolean} inverse defalut false
33061      */   
33062     maskInverse : false, 
33063     
33064     getAutoCreate : function()
33065     {
33066         if(!this.isFitContainer){
33067             return this.getSplitAutoCreate();
33068         }
33069         
33070         var cls = 'masonry-brick masonry-brick-full';
33071         
33072         if(this.href.length){
33073             cls += ' masonry-brick-link';
33074         }
33075         
33076         if(this.bgimage.length){
33077             cls += ' masonry-brick-image';
33078         }
33079         
33080         if(this.maskInverse){
33081             cls += ' mask-inverse';
33082         }
33083         
33084         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33085             cls += ' enable-mask';
33086         }
33087         
33088         if(this.size){
33089             cls += ' masonry-' + this.size + '-brick';
33090         }
33091         
33092         if(this.placetitle.length){
33093             
33094             switch (this.placetitle) {
33095                 case 'center' :
33096                     cls += ' masonry-center-title';
33097                     break;
33098                 case 'bottom' :
33099                     cls += ' masonry-bottom-title';
33100                     break;
33101                 default:
33102                     break;
33103             }
33104             
33105         } else {
33106             if(!this.html.length && !this.bgimage.length){
33107                 cls += ' masonry-center-title';
33108             }
33109
33110             if(!this.html.length && this.bgimage.length){
33111                 cls += ' masonry-bottom-title';
33112             }
33113         }
33114         
33115         if(this.cls){
33116             cls += ' ' + this.cls;
33117         }
33118         
33119         var cfg = {
33120             tag: (this.href.length) ? 'a' : 'div',
33121             cls: cls,
33122             cn: [
33123                 {
33124                     tag: 'div',
33125                     cls: 'masonry-brick-mask'
33126                 },
33127                 {
33128                     tag: 'div',
33129                     cls: 'masonry-brick-paragraph',
33130                     cn: []
33131                 }
33132             ]
33133         };
33134         
33135         if(this.href.length){
33136             cfg.href = this.href;
33137         }
33138         
33139         var cn = cfg.cn[1].cn;
33140         
33141         if(this.title.length){
33142             cn.push({
33143                 tag: 'h4',
33144                 cls: 'masonry-brick-title',
33145                 html: this.title
33146             });
33147         }
33148         
33149         if(this.html.length){
33150             cn.push({
33151                 tag: 'p',
33152                 cls: 'masonry-brick-text',
33153                 html: this.html
33154             });
33155         }
33156         
33157         if (!this.title.length && !this.html.length) {
33158             cfg.cn[1].cls += ' hide';
33159         }
33160         
33161         if(this.bgimage.length){
33162             cfg.cn.push({
33163                 tag: 'img',
33164                 cls: 'masonry-brick-image-view',
33165                 src: this.bgimage
33166             });
33167         }
33168         
33169         if(this.videourl.length){
33170             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33171             // youtube support only?
33172             cfg.cn.push({
33173                 tag: 'iframe',
33174                 cls: 'masonry-brick-image-view',
33175                 src: vurl,
33176                 frameborder : 0,
33177                 allowfullscreen : true
33178             });
33179         }
33180         
33181         return cfg;
33182         
33183     },
33184     
33185     getSplitAutoCreate : function()
33186     {
33187         var cls = 'masonry-brick masonry-brick-split';
33188         
33189         if(this.href.length){
33190             cls += ' masonry-brick-link';
33191         }
33192         
33193         if(this.bgimage.length){
33194             cls += ' masonry-brick-image';
33195         }
33196         
33197         if(this.size){
33198             cls += ' masonry-' + this.size + '-brick';
33199         }
33200         
33201         switch (this.placetitle) {
33202             case 'center' :
33203                 cls += ' masonry-center-title';
33204                 break;
33205             case 'bottom' :
33206                 cls += ' masonry-bottom-title';
33207                 break;
33208             default:
33209                 if(!this.bgimage.length){
33210                     cls += ' masonry-center-title';
33211                 }
33212
33213                 if(this.bgimage.length){
33214                     cls += ' masonry-bottom-title';
33215                 }
33216                 break;
33217         }
33218         
33219         if(this.cls){
33220             cls += ' ' + this.cls;
33221         }
33222         
33223         var cfg = {
33224             tag: (this.href.length) ? 'a' : 'div',
33225             cls: cls,
33226             cn: [
33227                 {
33228                     tag: 'div',
33229                     cls: 'masonry-brick-split-head',
33230                     cn: [
33231                         {
33232                             tag: 'div',
33233                             cls: 'masonry-brick-paragraph',
33234                             cn: []
33235                         }
33236                     ]
33237                 },
33238                 {
33239                     tag: 'div',
33240                     cls: 'masonry-brick-split-body',
33241                     cn: []
33242                 }
33243             ]
33244         };
33245         
33246         if(this.href.length){
33247             cfg.href = this.href;
33248         }
33249         
33250         if(this.title.length){
33251             cfg.cn[0].cn[0].cn.push({
33252                 tag: 'h4',
33253                 cls: 'masonry-brick-title',
33254                 html: this.title
33255             });
33256         }
33257         
33258         if(this.html.length){
33259             cfg.cn[1].cn.push({
33260                 tag: 'p',
33261                 cls: 'masonry-brick-text',
33262                 html: this.html
33263             });
33264         }
33265
33266         if(this.bgimage.length){
33267             cfg.cn[0].cn.push({
33268                 tag: 'img',
33269                 cls: 'masonry-brick-image-view',
33270                 src: this.bgimage
33271             });
33272         }
33273         
33274         if(this.videourl.length){
33275             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33276             // youtube support only?
33277             cfg.cn[0].cn.cn.push({
33278                 tag: 'iframe',
33279                 cls: 'masonry-brick-image-view',
33280                 src: vurl,
33281                 frameborder : 0,
33282                 allowfullscreen : true
33283             });
33284         }
33285         
33286         return cfg;
33287     },
33288     
33289     initEvents: function() 
33290     {
33291         switch (this.size) {
33292             case 'xs' :
33293                 this.x = 1;
33294                 this.y = 1;
33295                 break;
33296             case 'sm' :
33297                 this.x = 2;
33298                 this.y = 2;
33299                 break;
33300             case 'md' :
33301             case 'md-left' :
33302             case 'md-right' :
33303                 this.x = 3;
33304                 this.y = 3;
33305                 break;
33306             case 'tall' :
33307                 this.x = 2;
33308                 this.y = 3;
33309                 break;
33310             case 'wide' :
33311                 this.x = 3;
33312                 this.y = 2;
33313                 break;
33314             case 'wide-thin' :
33315                 this.x = 3;
33316                 this.y = 1;
33317                 break;
33318                         
33319             default :
33320                 break;
33321         }
33322         
33323         if(Roo.isTouch){
33324             this.el.on('touchstart', this.onTouchStart, this);
33325             this.el.on('touchmove', this.onTouchMove, this);
33326             this.el.on('touchend', this.onTouchEnd, this);
33327             this.el.on('contextmenu', this.onContextMenu, this);
33328         } else {
33329             this.el.on('mouseenter'  ,this.enter, this);
33330             this.el.on('mouseleave', this.leave, this);
33331             this.el.on('click', this.onClick, this);
33332         }
33333         
33334         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33335             this.parent().bricks.push(this);   
33336         }
33337         
33338     },
33339     
33340     onClick: function(e, el)
33341     {
33342         var time = this.endTimer - this.startTimer;
33343         // Roo.log(e.preventDefault());
33344         if(Roo.isTouch){
33345             if(time > 1000){
33346                 e.preventDefault();
33347                 return;
33348             }
33349         }
33350         
33351         if(!this.preventDefault){
33352             return;
33353         }
33354         
33355         e.preventDefault();
33356         
33357         if (this.activeClass != '') {
33358             this.selectBrick();
33359         }
33360         
33361         this.fireEvent('click', this, e);
33362     },
33363     
33364     enter: function(e, el)
33365     {
33366         e.preventDefault();
33367         
33368         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33369             return;
33370         }
33371         
33372         if(this.bgimage.length && this.html.length){
33373             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33374         }
33375     },
33376     
33377     leave: function(e, el)
33378     {
33379         e.preventDefault();
33380         
33381         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33382             return;
33383         }
33384         
33385         if(this.bgimage.length && this.html.length){
33386             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33387         }
33388     },
33389     
33390     onTouchStart: function(e, el)
33391     {
33392 //        e.preventDefault();
33393         
33394         this.touchmoved = false;
33395         
33396         if(!this.isFitContainer){
33397             return;
33398         }
33399         
33400         if(!this.bgimage.length || !this.html.length){
33401             return;
33402         }
33403         
33404         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33405         
33406         this.timer = new Date().getTime();
33407         
33408     },
33409     
33410     onTouchMove: function(e, el)
33411     {
33412         this.touchmoved = true;
33413     },
33414     
33415     onContextMenu : function(e,el)
33416     {
33417         e.preventDefault();
33418         e.stopPropagation();
33419         return false;
33420     },
33421     
33422     onTouchEnd: function(e, el)
33423     {
33424 //        e.preventDefault();
33425         
33426         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33427         
33428             this.leave(e,el);
33429             
33430             return;
33431         }
33432         
33433         if(!this.bgimage.length || !this.html.length){
33434             
33435             if(this.href.length){
33436                 window.location.href = this.href;
33437             }
33438             
33439             return;
33440         }
33441         
33442         if(!this.isFitContainer){
33443             return;
33444         }
33445         
33446         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33447         
33448         window.location.href = this.href;
33449     },
33450     
33451     //selection on single brick only
33452     selectBrick : function() {
33453         
33454         if (!this.parentId) {
33455             return;
33456         }
33457         
33458         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33459         var index = m.selectedBrick.indexOf(this.id);
33460         
33461         if ( index > -1) {
33462             m.selectedBrick.splice(index,1);
33463             this.el.removeClass(this.activeClass);
33464             return;
33465         }
33466         
33467         for(var i = 0; i < m.selectedBrick.length; i++) {
33468             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33469             b.el.removeClass(b.activeClass);
33470         }
33471         
33472         m.selectedBrick = [];
33473         
33474         m.selectedBrick.push(this.id);
33475         this.el.addClass(this.activeClass);
33476         return;
33477     },
33478     
33479     isSelected : function(){
33480         return this.el.hasClass(this.activeClass);
33481         
33482     }
33483 });
33484
33485 Roo.apply(Roo.bootstrap.MasonryBrick, {
33486     
33487     //groups: {},
33488     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33489      /**
33490     * register a Masonry Brick
33491     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33492     */
33493     
33494     register : function(brick)
33495     {
33496         //this.groups[brick.id] = brick;
33497         this.groups.add(brick.id, brick);
33498     },
33499     /**
33500     * fetch a  masonry brick based on the masonry brick ID
33501     * @param {string} the masonry brick to add
33502     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33503     */
33504     
33505     get: function(brick_id) 
33506     {
33507         // if (typeof(this.groups[brick_id]) == 'undefined') {
33508         //     return false;
33509         // }
33510         // return this.groups[brick_id] ;
33511         
33512         if(this.groups.key(brick_id)) {
33513             return this.groups.key(brick_id);
33514         }
33515         
33516         return false;
33517     }
33518     
33519     
33520     
33521 });
33522
33523  /*
33524  * - LGPL
33525  *
33526  * element
33527  * 
33528  */
33529
33530 /**
33531  * @class Roo.bootstrap.Brick
33532  * @extends Roo.bootstrap.Component
33533  * Bootstrap Brick class
33534  * 
33535  * @constructor
33536  * Create a new Brick
33537  * @param {Object} config The config object
33538  */
33539
33540 Roo.bootstrap.Brick = function(config){
33541     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33542     
33543     this.addEvents({
33544         // raw events
33545         /**
33546          * @event click
33547          * When a Brick is click
33548          * @param {Roo.bootstrap.Brick} this
33549          * @param {Roo.EventObject} e
33550          */
33551         "click" : true
33552     });
33553 };
33554
33555 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33556     
33557     /**
33558      * @cfg {String} title
33559      */   
33560     title : '',
33561     /**
33562      * @cfg {String} html
33563      */   
33564     html : '',
33565     /**
33566      * @cfg {String} bgimage
33567      */   
33568     bgimage : '',
33569     /**
33570      * @cfg {String} cls
33571      */   
33572     cls : '',
33573     /**
33574      * @cfg {String} href
33575      */   
33576     href : '',
33577     /**
33578      * @cfg {String} video
33579      */   
33580     video : '',
33581     /**
33582      * @cfg {Boolean} square
33583      */   
33584     square : true,
33585     
33586     getAutoCreate : function()
33587     {
33588         var cls = 'roo-brick';
33589         
33590         if(this.href.length){
33591             cls += ' roo-brick-link';
33592         }
33593         
33594         if(this.bgimage.length){
33595             cls += ' roo-brick-image';
33596         }
33597         
33598         if(!this.html.length && !this.bgimage.length){
33599             cls += ' roo-brick-center-title';
33600         }
33601         
33602         if(!this.html.length && this.bgimage.length){
33603             cls += ' roo-brick-bottom-title';
33604         }
33605         
33606         if(this.cls){
33607             cls += ' ' + this.cls;
33608         }
33609         
33610         var cfg = {
33611             tag: (this.href.length) ? 'a' : 'div',
33612             cls: cls,
33613             cn: [
33614                 {
33615                     tag: 'div',
33616                     cls: 'roo-brick-paragraph',
33617                     cn: []
33618                 }
33619             ]
33620         };
33621         
33622         if(this.href.length){
33623             cfg.href = this.href;
33624         }
33625         
33626         var cn = cfg.cn[0].cn;
33627         
33628         if(this.title.length){
33629             cn.push({
33630                 tag: 'h4',
33631                 cls: 'roo-brick-title',
33632                 html: this.title
33633             });
33634         }
33635         
33636         if(this.html.length){
33637             cn.push({
33638                 tag: 'p',
33639                 cls: 'roo-brick-text',
33640                 html: this.html
33641             });
33642         } else {
33643             cn.cls += ' hide';
33644         }
33645         
33646         if(this.bgimage.length){
33647             cfg.cn.push({
33648                 tag: 'img',
33649                 cls: 'roo-brick-image-view',
33650                 src: this.bgimage
33651             });
33652         }
33653         
33654         return cfg;
33655     },
33656     
33657     initEvents: function() 
33658     {
33659         if(this.title.length || this.html.length){
33660             this.el.on('mouseenter'  ,this.enter, this);
33661             this.el.on('mouseleave', this.leave, this);
33662         }
33663         
33664         Roo.EventManager.onWindowResize(this.resize, this); 
33665         
33666         if(this.bgimage.length){
33667             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33668             this.imageEl.on('load', this.onImageLoad, this);
33669             return;
33670         }
33671         
33672         this.resize();
33673     },
33674     
33675     onImageLoad : function()
33676     {
33677         this.resize();
33678     },
33679     
33680     resize : function()
33681     {
33682         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33683         
33684         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33685         
33686         if(this.bgimage.length){
33687             var image = this.el.select('.roo-brick-image-view', true).first();
33688             
33689             image.setWidth(paragraph.getWidth());
33690             
33691             if(this.square){
33692                 image.setHeight(paragraph.getWidth());
33693             }
33694             
33695             this.el.setHeight(image.getHeight());
33696             paragraph.setHeight(image.getHeight());
33697             
33698         }
33699         
33700     },
33701     
33702     enter: function(e, el)
33703     {
33704         e.preventDefault();
33705         
33706         if(this.bgimage.length){
33707             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33708             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33709         }
33710     },
33711     
33712     leave: function(e, el)
33713     {
33714         e.preventDefault();
33715         
33716         if(this.bgimage.length){
33717             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33718             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33719         }
33720     }
33721     
33722 });
33723
33724  
33725
33726  /*
33727  * - LGPL
33728  *
33729  * Number field 
33730  */
33731
33732 /**
33733  * @class Roo.bootstrap.NumberField
33734  * @extends Roo.bootstrap.Input
33735  * Bootstrap NumberField class
33736  * 
33737  * 
33738  * 
33739  * 
33740  * @constructor
33741  * Create a new NumberField
33742  * @param {Object} config The config object
33743  */
33744
33745 Roo.bootstrap.NumberField = function(config){
33746     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33747 };
33748
33749 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33750     
33751     /**
33752      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33753      */
33754     allowDecimals : true,
33755     /**
33756      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33757      */
33758     decimalSeparator : ".",
33759     /**
33760      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33761      */
33762     decimalPrecision : 2,
33763     /**
33764      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33765      */
33766     allowNegative : true,
33767     
33768     /**
33769      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33770      */
33771     allowZero: true,
33772     /**
33773      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33774      */
33775     minValue : Number.NEGATIVE_INFINITY,
33776     /**
33777      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33778      */
33779     maxValue : Number.MAX_VALUE,
33780     /**
33781      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33782      */
33783     minText : "The minimum value for this field is {0}",
33784     /**
33785      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33786      */
33787     maxText : "The maximum value for this field is {0}",
33788     /**
33789      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33790      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33791      */
33792     nanText : "{0} is not a valid number",
33793     /**
33794      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33795      */
33796     thousandsDelimiter : false,
33797     /**
33798      * @cfg {String} valueAlign alignment of value
33799      */
33800     valueAlign : "left",
33801
33802     getAutoCreate : function()
33803     {
33804         var hiddenInput = {
33805             tag: 'input',
33806             type: 'hidden',
33807             id: Roo.id(),
33808             cls: 'hidden-number-input'
33809         };
33810         
33811         if (this.name) {
33812             hiddenInput.name = this.name;
33813         }
33814         
33815         this.name = '';
33816         
33817         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33818         
33819         this.name = hiddenInput.name;
33820         
33821         if(cfg.cn.length > 0) {
33822             cfg.cn.push(hiddenInput);
33823         }
33824         
33825         return cfg;
33826     },
33827
33828     // private
33829     initEvents : function()
33830     {   
33831         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33832         
33833         var allowed = "0123456789";
33834         
33835         if(this.allowDecimals){
33836             allowed += this.decimalSeparator;
33837         }
33838         
33839         if(this.allowNegative){
33840             allowed += "-";
33841         }
33842         
33843         if(this.thousandsDelimiter) {
33844             allowed += ",";
33845         }
33846         
33847         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33848         
33849         var keyPress = function(e){
33850             
33851             var k = e.getKey();
33852             
33853             var c = e.getCharCode();
33854             
33855             if(
33856                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33857                     allowed.indexOf(String.fromCharCode(c)) === -1
33858             ){
33859                 e.stopEvent();
33860                 return;
33861             }
33862             
33863             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33864                 return;
33865             }
33866             
33867             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33868                 e.stopEvent();
33869             }
33870         };
33871         
33872         this.el.on("keypress", keyPress, this);
33873     },
33874     
33875     validateValue : function(value)
33876     {
33877         
33878         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33879             return false;
33880         }
33881         
33882         var num = this.parseValue(value);
33883         
33884         if(isNaN(num)){
33885             this.markInvalid(String.format(this.nanText, value));
33886             return false;
33887         }
33888         
33889         if(num < this.minValue){
33890             this.markInvalid(String.format(this.minText, this.minValue));
33891             return false;
33892         }
33893         
33894         if(num > this.maxValue){
33895             this.markInvalid(String.format(this.maxText, this.maxValue));
33896             return false;
33897         }
33898         
33899         return true;
33900     },
33901
33902     getValue : function()
33903     {
33904         var v = this.hiddenEl().getValue();
33905         
33906         return this.fixPrecision(this.parseValue(v));
33907     },
33908
33909     parseValue : function(value)
33910     {
33911         if(this.thousandsDelimiter) {
33912             value += "";
33913             r = new RegExp(",", "g");
33914             value = value.replace(r, "");
33915         }
33916         
33917         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33918         return isNaN(value) ? '' : value;
33919     },
33920
33921     fixPrecision : function(value)
33922     {
33923         if(this.thousandsDelimiter) {
33924             value += "";
33925             r = new RegExp(",", "g");
33926             value = value.replace(r, "");
33927         }
33928         
33929         var nan = isNaN(value);
33930         
33931         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33932             return nan ? '' : value;
33933         }
33934         return parseFloat(value).toFixed(this.decimalPrecision);
33935     },
33936
33937     setValue : function(v)
33938     {
33939         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33940         
33941         this.value = v;
33942         
33943         if(this.rendered){
33944             
33945             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33946             
33947             this.inputEl().dom.value = (v == '') ? '' :
33948                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33949             
33950             if(!this.allowZero && v === '0') {
33951                 this.hiddenEl().dom.value = '';
33952                 this.inputEl().dom.value = '';
33953             }
33954             
33955             this.validate();
33956         }
33957     },
33958
33959     decimalPrecisionFcn : function(v)
33960     {
33961         return Math.floor(v);
33962     },
33963
33964     beforeBlur : function()
33965     {
33966         var v = this.parseValue(this.getRawValue());
33967         
33968         if(v || v === 0 || v === ''){
33969             this.setValue(v);
33970         }
33971     },
33972     
33973     hiddenEl : function()
33974     {
33975         return this.el.select('input.hidden-number-input',true).first();
33976     }
33977     
33978 });
33979
33980  
33981
33982 /*
33983 * Licence: LGPL
33984 */
33985
33986 /**
33987  * @class Roo.bootstrap.DocumentSlider
33988  * @extends Roo.bootstrap.Component
33989  * Bootstrap DocumentSlider class
33990  * 
33991  * @constructor
33992  * Create a new DocumentViewer
33993  * @param {Object} config The config object
33994  */
33995
33996 Roo.bootstrap.DocumentSlider = function(config){
33997     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33998     
33999     this.files = [];
34000     
34001     this.addEvents({
34002         /**
34003          * @event initial
34004          * Fire after initEvent
34005          * @param {Roo.bootstrap.DocumentSlider} this
34006          */
34007         "initial" : true,
34008         /**
34009          * @event update
34010          * Fire after update
34011          * @param {Roo.bootstrap.DocumentSlider} this
34012          */
34013         "update" : true,
34014         /**
34015          * @event click
34016          * Fire after click
34017          * @param {Roo.bootstrap.DocumentSlider} this
34018          */
34019         "click" : true
34020     });
34021 };
34022
34023 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34024     
34025     files : false,
34026     
34027     indicator : 0,
34028     
34029     getAutoCreate : function()
34030     {
34031         var cfg = {
34032             tag : 'div',
34033             cls : 'roo-document-slider',
34034             cn : [
34035                 {
34036                     tag : 'div',
34037                     cls : 'roo-document-slider-header',
34038                     cn : [
34039                         {
34040                             tag : 'div',
34041                             cls : 'roo-document-slider-header-title'
34042                         }
34043                     ]
34044                 },
34045                 {
34046                     tag : 'div',
34047                     cls : 'roo-document-slider-body',
34048                     cn : [
34049                         {
34050                             tag : 'div',
34051                             cls : 'roo-document-slider-prev',
34052                             cn : [
34053                                 {
34054                                     tag : 'i',
34055                                     cls : 'fa fa-chevron-left'
34056                                 }
34057                             ]
34058                         },
34059                         {
34060                             tag : 'div',
34061                             cls : 'roo-document-slider-thumb',
34062                             cn : [
34063                                 {
34064                                     tag : 'img',
34065                                     cls : 'roo-document-slider-image'
34066                                 }
34067                             ]
34068                         },
34069                         {
34070                             tag : 'div',
34071                             cls : 'roo-document-slider-next',
34072                             cn : [
34073                                 {
34074                                     tag : 'i',
34075                                     cls : 'fa fa-chevron-right'
34076                                 }
34077                             ]
34078                         }
34079                     ]
34080                 }
34081             ]
34082         };
34083         
34084         return cfg;
34085     },
34086     
34087     initEvents : function()
34088     {
34089         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34090         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34091         
34092         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34093         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34094         
34095         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34096         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34097         
34098         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34099         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34100         
34101         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34102         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34103         
34104         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34105         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34106         
34107         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34108         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34109         
34110         this.thumbEl.on('click', this.onClick, this);
34111         
34112         this.prevIndicator.on('click', this.prev, this);
34113         
34114         this.nextIndicator.on('click', this.next, this);
34115         
34116     },
34117     
34118     initial : function()
34119     {
34120         if(this.files.length){
34121             this.indicator = 1;
34122             this.update()
34123         }
34124         
34125         this.fireEvent('initial', this);
34126     },
34127     
34128     update : function()
34129     {
34130         this.imageEl.attr('src', this.files[this.indicator - 1]);
34131         
34132         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34133         
34134         this.prevIndicator.show();
34135         
34136         if(this.indicator == 1){
34137             this.prevIndicator.hide();
34138         }
34139         
34140         this.nextIndicator.show();
34141         
34142         if(this.indicator == this.files.length){
34143             this.nextIndicator.hide();
34144         }
34145         
34146         this.thumbEl.scrollTo('top');
34147         
34148         this.fireEvent('update', this);
34149     },
34150     
34151     onClick : function(e)
34152     {
34153         e.preventDefault();
34154         
34155         this.fireEvent('click', this);
34156     },
34157     
34158     prev : function(e)
34159     {
34160         e.preventDefault();
34161         
34162         this.indicator = Math.max(1, this.indicator - 1);
34163         
34164         this.update();
34165     },
34166     
34167     next : function(e)
34168     {
34169         e.preventDefault();
34170         
34171         this.indicator = Math.min(this.files.length, this.indicator + 1);
34172         
34173         this.update();
34174     }
34175 });
34176 /*
34177  * - LGPL
34178  *
34179  * RadioSet
34180  *
34181  *
34182  */
34183
34184 /**
34185  * @class Roo.bootstrap.RadioSet
34186  * @extends Roo.bootstrap.Input
34187  * Bootstrap RadioSet class
34188  * @cfg {String} indicatorpos (left|right) default left
34189  * @cfg {Boolean} inline (true|false) inline the element (default true)
34190  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34191  * @constructor
34192  * Create a new RadioSet
34193  * @param {Object} config The config object
34194  */
34195
34196 Roo.bootstrap.RadioSet = function(config){
34197     
34198     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34199     
34200     this.radioes = [];
34201     
34202     Roo.bootstrap.RadioSet.register(this);
34203     
34204     this.addEvents({
34205         /**
34206         * @event check
34207         * Fires when the element is checked or unchecked.
34208         * @param {Roo.bootstrap.RadioSet} this This radio
34209         * @param {Roo.bootstrap.Radio} item The checked item
34210         */
34211        check : true,
34212        /**
34213         * @event click
34214         * Fires when the element is click.
34215         * @param {Roo.bootstrap.RadioSet} this This radio set
34216         * @param {Roo.bootstrap.Radio} item The checked item
34217         * @param {Roo.EventObject} e The event object
34218         */
34219        click : true
34220     });
34221     
34222 };
34223
34224 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34225
34226     radioes : false,
34227     
34228     inline : true,
34229     
34230     weight : '',
34231     
34232     indicatorpos : 'left',
34233     
34234     getAutoCreate : function()
34235     {
34236         var label = {
34237             tag : 'label',
34238             cls : 'roo-radio-set-label',
34239             cn : [
34240                 {
34241                     tag : 'span',
34242                     html : this.fieldLabel
34243                 }
34244             ]
34245         };
34246         if (Roo.bootstrap.version == 3) {
34247             
34248             
34249             if(this.indicatorpos == 'left'){
34250                 label.cn.unshift({
34251                     tag : 'i',
34252                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34253                     tooltip : 'This field is required'
34254                 });
34255             } else {
34256                 label.cn.push({
34257                     tag : 'i',
34258                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34259                     tooltip : 'This field is required'
34260                 });
34261             }
34262         }
34263         var items = {
34264             tag : 'div',
34265             cls : 'roo-radio-set-items'
34266         };
34267         
34268         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34269         
34270         if (align === 'left' && this.fieldLabel.length) {
34271             
34272             items = {
34273                 cls : "roo-radio-set-right", 
34274                 cn: [
34275                     items
34276                 ]
34277             };
34278             
34279             if(this.labelWidth > 12){
34280                 label.style = "width: " + this.labelWidth + 'px';
34281             }
34282             
34283             if(this.labelWidth < 13 && this.labelmd == 0){
34284                 this.labelmd = this.labelWidth;
34285             }
34286             
34287             if(this.labellg > 0){
34288                 label.cls += ' col-lg-' + this.labellg;
34289                 items.cls += ' col-lg-' + (12 - this.labellg);
34290             }
34291             
34292             if(this.labelmd > 0){
34293                 label.cls += ' col-md-' + this.labelmd;
34294                 items.cls += ' col-md-' + (12 - this.labelmd);
34295             }
34296             
34297             if(this.labelsm > 0){
34298                 label.cls += ' col-sm-' + this.labelsm;
34299                 items.cls += ' col-sm-' + (12 - this.labelsm);
34300             }
34301             
34302             if(this.labelxs > 0){
34303                 label.cls += ' col-xs-' + this.labelxs;
34304                 items.cls += ' col-xs-' + (12 - this.labelxs);
34305             }
34306         }
34307         
34308         var cfg = {
34309             tag : 'div',
34310             cls : 'roo-radio-set',
34311             cn : [
34312                 {
34313                     tag : 'input',
34314                     cls : 'roo-radio-set-input',
34315                     type : 'hidden',
34316                     name : this.name,
34317                     value : this.value ? this.value :  ''
34318                 },
34319                 label,
34320                 items
34321             ]
34322         };
34323         
34324         if(this.weight.length){
34325             cfg.cls += ' roo-radio-' + this.weight;
34326         }
34327         
34328         if(this.inline) {
34329             cfg.cls += ' roo-radio-set-inline';
34330         }
34331         
34332         var settings=this;
34333         ['xs','sm','md','lg'].map(function(size){
34334             if (settings[size]) {
34335                 cfg.cls += ' col-' + size + '-' + settings[size];
34336             }
34337         });
34338         
34339         return cfg;
34340         
34341     },
34342
34343     initEvents : function()
34344     {
34345         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34346         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34347         
34348         if(!this.fieldLabel.length){
34349             this.labelEl.hide();
34350         }
34351         
34352         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34353         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34354         
34355         this.indicator = this.indicatorEl();
34356         
34357         if(this.indicator){
34358             this.indicator.addClass('invisible');
34359         }
34360         
34361         this.originalValue = this.getValue();
34362         
34363     },
34364     
34365     inputEl: function ()
34366     {
34367         return this.el.select('.roo-radio-set-input', true).first();
34368     },
34369     
34370     getChildContainer : function()
34371     {
34372         return this.itemsEl;
34373     },
34374     
34375     register : function(item)
34376     {
34377         this.radioes.push(item);
34378         
34379     },
34380     
34381     validate : function()
34382     {   
34383         if(this.getVisibilityEl().hasClass('hidden')){
34384             return true;
34385         }
34386         
34387         var valid = false;
34388         
34389         Roo.each(this.radioes, function(i){
34390             if(!i.checked){
34391                 return;
34392             }
34393             
34394             valid = true;
34395             return false;
34396         });
34397         
34398         if(this.allowBlank) {
34399             return true;
34400         }
34401         
34402         if(this.disabled || valid){
34403             this.markValid();
34404             return true;
34405         }
34406         
34407         this.markInvalid();
34408         return false;
34409         
34410     },
34411     
34412     markValid : function()
34413     {
34414         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34415             this.indicatorEl().removeClass('visible');
34416             this.indicatorEl().addClass('invisible');
34417         }
34418         
34419         
34420         if (Roo.bootstrap.version == 3) {
34421             this.el.removeClass([this.invalidClass, this.validClass]);
34422             this.el.addClass(this.validClass);
34423         } else {
34424             this.el.removeClass(['is-invalid','is-valid']);
34425             this.el.addClass(['is-valid']);
34426         }
34427         this.fireEvent('valid', this);
34428     },
34429     
34430     markInvalid : function(msg)
34431     {
34432         if(this.allowBlank || this.disabled){
34433             return;
34434         }
34435         
34436         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34437             this.indicatorEl().removeClass('invisible');
34438             this.indicatorEl().addClass('visible');
34439         }
34440         if (Roo.bootstrap.version == 3) {
34441             this.el.removeClass([this.invalidClass, this.validClass]);
34442             this.el.addClass(this.invalidClass);
34443         } else {
34444             this.el.removeClass(['is-invalid','is-valid']);
34445             this.el.addClass(['is-invalid']);
34446         }
34447         
34448         this.fireEvent('invalid', this, msg);
34449         
34450     },
34451     
34452     setValue : function(v, suppressEvent)
34453     {   
34454         if(this.value === v){
34455             return;
34456         }
34457         
34458         this.value = v;
34459         
34460         if(this.rendered){
34461             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34462         }
34463         
34464         Roo.each(this.radioes, function(i){
34465             i.checked = false;
34466             i.el.removeClass('checked');
34467         });
34468         
34469         Roo.each(this.radioes, function(i){
34470             
34471             if(i.value === v || i.value.toString() === v.toString()){
34472                 i.checked = true;
34473                 i.el.addClass('checked');
34474                 
34475                 if(suppressEvent !== true){
34476                     this.fireEvent('check', this, i);
34477                 }
34478                 
34479                 return false;
34480             }
34481             
34482         }, this);
34483         
34484         this.validate();
34485     },
34486     
34487     clearInvalid : function(){
34488         
34489         if(!this.el || this.preventMark){
34490             return;
34491         }
34492         
34493         this.el.removeClass([this.invalidClass]);
34494         
34495         this.fireEvent('valid', this);
34496     }
34497     
34498 });
34499
34500 Roo.apply(Roo.bootstrap.RadioSet, {
34501     
34502     groups: {},
34503     
34504     register : function(set)
34505     {
34506         this.groups[set.name] = set;
34507     },
34508     
34509     get: function(name) 
34510     {
34511         if (typeof(this.groups[name]) == 'undefined') {
34512             return false;
34513         }
34514         
34515         return this.groups[name] ;
34516     }
34517     
34518 });
34519 /*
34520  * Based on:
34521  * Ext JS Library 1.1.1
34522  * Copyright(c) 2006-2007, Ext JS, LLC.
34523  *
34524  * Originally Released Under LGPL - original licence link has changed is not relivant.
34525  *
34526  * Fork - LGPL
34527  * <script type="text/javascript">
34528  */
34529
34530
34531 /**
34532  * @class Roo.bootstrap.SplitBar
34533  * @extends Roo.util.Observable
34534  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34535  * <br><br>
34536  * Usage:
34537  * <pre><code>
34538 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34539                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34540 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34541 split.minSize = 100;
34542 split.maxSize = 600;
34543 split.animate = true;
34544 split.on('moved', splitterMoved);
34545 </code></pre>
34546  * @constructor
34547  * Create a new SplitBar
34548  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34549  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34550  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34551  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34552                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34553                         position of the SplitBar).
34554  */
34555 Roo.bootstrap.SplitBar = function(cfg){
34556     
34557     /** @private */
34558     
34559     //{
34560     //  dragElement : elm
34561     //  resizingElement: el,
34562         // optional..
34563     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34564     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34565         // existingProxy ???
34566     //}
34567     
34568     this.el = Roo.get(cfg.dragElement, true);
34569     this.el.dom.unselectable = "on";
34570     /** @private */
34571     this.resizingEl = Roo.get(cfg.resizingElement, true);
34572
34573     /**
34574      * @private
34575      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34576      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34577      * @type Number
34578      */
34579     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34580     
34581     /**
34582      * The minimum size of the resizing element. (Defaults to 0)
34583      * @type Number
34584      */
34585     this.minSize = 0;
34586     
34587     /**
34588      * The maximum size of the resizing element. (Defaults to 2000)
34589      * @type Number
34590      */
34591     this.maxSize = 2000;
34592     
34593     /**
34594      * Whether to animate the transition to the new size
34595      * @type Boolean
34596      */
34597     this.animate = false;
34598     
34599     /**
34600      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34601      * @type Boolean
34602      */
34603     this.useShim = false;
34604     
34605     /** @private */
34606     this.shim = null;
34607     
34608     if(!cfg.existingProxy){
34609         /** @private */
34610         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34611     }else{
34612         this.proxy = Roo.get(cfg.existingProxy).dom;
34613     }
34614     /** @private */
34615     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34616     
34617     /** @private */
34618     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34619     
34620     /** @private */
34621     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34622     
34623     /** @private */
34624     this.dragSpecs = {};
34625     
34626     /**
34627      * @private The adapter to use to positon and resize elements
34628      */
34629     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34630     this.adapter.init(this);
34631     
34632     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34633         /** @private */
34634         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34635         this.el.addClass("roo-splitbar-h");
34636     }else{
34637         /** @private */
34638         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34639         this.el.addClass("roo-splitbar-v");
34640     }
34641     
34642     this.addEvents({
34643         /**
34644          * @event resize
34645          * Fires when the splitter is moved (alias for {@link #event-moved})
34646          * @param {Roo.bootstrap.SplitBar} this
34647          * @param {Number} newSize the new width or height
34648          */
34649         "resize" : true,
34650         /**
34651          * @event moved
34652          * Fires when the splitter is moved
34653          * @param {Roo.bootstrap.SplitBar} this
34654          * @param {Number} newSize the new width or height
34655          */
34656         "moved" : true,
34657         /**
34658          * @event beforeresize
34659          * Fires before the splitter is dragged
34660          * @param {Roo.bootstrap.SplitBar} this
34661          */
34662         "beforeresize" : true,
34663
34664         "beforeapply" : true
34665     });
34666
34667     Roo.util.Observable.call(this);
34668 };
34669
34670 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34671     onStartProxyDrag : function(x, y){
34672         this.fireEvent("beforeresize", this);
34673         if(!this.overlay){
34674             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34675             o.unselectable();
34676             o.enableDisplayMode("block");
34677             // all splitbars share the same overlay
34678             Roo.bootstrap.SplitBar.prototype.overlay = o;
34679         }
34680         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34681         this.overlay.show();
34682         Roo.get(this.proxy).setDisplayed("block");
34683         var size = this.adapter.getElementSize(this);
34684         this.activeMinSize = this.getMinimumSize();;
34685         this.activeMaxSize = this.getMaximumSize();;
34686         var c1 = size - this.activeMinSize;
34687         var c2 = Math.max(this.activeMaxSize - size, 0);
34688         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34689             this.dd.resetConstraints();
34690             this.dd.setXConstraint(
34691                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34692                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34693             );
34694             this.dd.setYConstraint(0, 0);
34695         }else{
34696             this.dd.resetConstraints();
34697             this.dd.setXConstraint(0, 0);
34698             this.dd.setYConstraint(
34699                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34700                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34701             );
34702          }
34703         this.dragSpecs.startSize = size;
34704         this.dragSpecs.startPoint = [x, y];
34705         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34706     },
34707     
34708     /** 
34709      * @private Called after the drag operation by the DDProxy
34710      */
34711     onEndProxyDrag : function(e){
34712         Roo.get(this.proxy).setDisplayed(false);
34713         var endPoint = Roo.lib.Event.getXY(e);
34714         if(this.overlay){
34715             this.overlay.hide();
34716         }
34717         var newSize;
34718         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34719             newSize = this.dragSpecs.startSize + 
34720                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34721                     endPoint[0] - this.dragSpecs.startPoint[0] :
34722                     this.dragSpecs.startPoint[0] - endPoint[0]
34723                 );
34724         }else{
34725             newSize = this.dragSpecs.startSize + 
34726                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34727                     endPoint[1] - this.dragSpecs.startPoint[1] :
34728                     this.dragSpecs.startPoint[1] - endPoint[1]
34729                 );
34730         }
34731         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34732         if(newSize != this.dragSpecs.startSize){
34733             if(this.fireEvent('beforeapply', this, newSize) !== false){
34734                 this.adapter.setElementSize(this, newSize);
34735                 this.fireEvent("moved", this, newSize);
34736                 this.fireEvent("resize", this, newSize);
34737             }
34738         }
34739     },
34740     
34741     /**
34742      * Get the adapter this SplitBar uses
34743      * @return The adapter object
34744      */
34745     getAdapter : function(){
34746         return this.adapter;
34747     },
34748     
34749     /**
34750      * Set the adapter this SplitBar uses
34751      * @param {Object} adapter A SplitBar adapter object
34752      */
34753     setAdapter : function(adapter){
34754         this.adapter = adapter;
34755         this.adapter.init(this);
34756     },
34757     
34758     /**
34759      * Gets the minimum size for the resizing element
34760      * @return {Number} The minimum size
34761      */
34762     getMinimumSize : function(){
34763         return this.minSize;
34764     },
34765     
34766     /**
34767      * Sets the minimum size for the resizing element
34768      * @param {Number} minSize The minimum size
34769      */
34770     setMinimumSize : function(minSize){
34771         this.minSize = minSize;
34772     },
34773     
34774     /**
34775      * Gets the maximum size for the resizing element
34776      * @return {Number} The maximum size
34777      */
34778     getMaximumSize : function(){
34779         return this.maxSize;
34780     },
34781     
34782     /**
34783      * Sets the maximum size for the resizing element
34784      * @param {Number} maxSize The maximum size
34785      */
34786     setMaximumSize : function(maxSize){
34787         this.maxSize = maxSize;
34788     },
34789     
34790     /**
34791      * Sets the initialize size for the resizing element
34792      * @param {Number} size The initial size
34793      */
34794     setCurrentSize : function(size){
34795         var oldAnimate = this.animate;
34796         this.animate = false;
34797         this.adapter.setElementSize(this, size);
34798         this.animate = oldAnimate;
34799     },
34800     
34801     /**
34802      * Destroy this splitbar. 
34803      * @param {Boolean} removeEl True to remove the element
34804      */
34805     destroy : function(removeEl){
34806         if(this.shim){
34807             this.shim.remove();
34808         }
34809         this.dd.unreg();
34810         this.proxy.parentNode.removeChild(this.proxy);
34811         if(removeEl){
34812             this.el.remove();
34813         }
34814     }
34815 });
34816
34817 /**
34818  * @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.
34819  */
34820 Roo.bootstrap.SplitBar.createProxy = function(dir){
34821     var proxy = new Roo.Element(document.createElement("div"));
34822     proxy.unselectable();
34823     var cls = 'roo-splitbar-proxy';
34824     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34825     document.body.appendChild(proxy.dom);
34826     return proxy.dom;
34827 };
34828
34829 /** 
34830  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34831  * Default Adapter. It assumes the splitter and resizing element are not positioned
34832  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34833  */
34834 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34835 };
34836
34837 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34838     // do nothing for now
34839     init : function(s){
34840     
34841     },
34842     /**
34843      * Called before drag operations to get the current size of the resizing element. 
34844      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34845      */
34846      getElementSize : function(s){
34847         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34848             return s.resizingEl.getWidth();
34849         }else{
34850             return s.resizingEl.getHeight();
34851         }
34852     },
34853     
34854     /**
34855      * Called after drag operations to set the size of the resizing element.
34856      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34857      * @param {Number} newSize The new size to set
34858      * @param {Function} onComplete A function to be invoked when resizing is complete
34859      */
34860     setElementSize : function(s, newSize, onComplete){
34861         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34862             if(!s.animate){
34863                 s.resizingEl.setWidth(newSize);
34864                 if(onComplete){
34865                     onComplete(s, newSize);
34866                 }
34867             }else{
34868                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34869             }
34870         }else{
34871             
34872             if(!s.animate){
34873                 s.resizingEl.setHeight(newSize);
34874                 if(onComplete){
34875                     onComplete(s, newSize);
34876                 }
34877             }else{
34878                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34879             }
34880         }
34881     }
34882 };
34883
34884 /** 
34885  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34886  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34887  * Adapter that  moves the splitter element to align with the resized sizing element. 
34888  * Used with an absolute positioned SplitBar.
34889  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34890  * document.body, make sure you assign an id to the body element.
34891  */
34892 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34893     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34894     this.container = Roo.get(container);
34895 };
34896
34897 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34898     init : function(s){
34899         this.basic.init(s);
34900     },
34901     
34902     getElementSize : function(s){
34903         return this.basic.getElementSize(s);
34904     },
34905     
34906     setElementSize : function(s, newSize, onComplete){
34907         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34908     },
34909     
34910     moveSplitter : function(s){
34911         var yes = Roo.bootstrap.SplitBar;
34912         switch(s.placement){
34913             case yes.LEFT:
34914                 s.el.setX(s.resizingEl.getRight());
34915                 break;
34916             case yes.RIGHT:
34917                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34918                 break;
34919             case yes.TOP:
34920                 s.el.setY(s.resizingEl.getBottom());
34921                 break;
34922             case yes.BOTTOM:
34923                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34924                 break;
34925         }
34926     }
34927 };
34928
34929 /**
34930  * Orientation constant - Create a vertical SplitBar
34931  * @static
34932  * @type Number
34933  */
34934 Roo.bootstrap.SplitBar.VERTICAL = 1;
34935
34936 /**
34937  * Orientation constant - Create a horizontal SplitBar
34938  * @static
34939  * @type Number
34940  */
34941 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34942
34943 /**
34944  * Placement constant - The resizing element is to the left of the splitter element
34945  * @static
34946  * @type Number
34947  */
34948 Roo.bootstrap.SplitBar.LEFT = 1;
34949
34950 /**
34951  * Placement constant - The resizing element is to the right of the splitter element
34952  * @static
34953  * @type Number
34954  */
34955 Roo.bootstrap.SplitBar.RIGHT = 2;
34956
34957 /**
34958  * Placement constant - The resizing element is positioned above the splitter element
34959  * @static
34960  * @type Number
34961  */
34962 Roo.bootstrap.SplitBar.TOP = 3;
34963
34964 /**
34965  * Placement constant - The resizing element is positioned under splitter element
34966  * @static
34967  * @type Number
34968  */
34969 Roo.bootstrap.SplitBar.BOTTOM = 4;
34970 Roo.namespace("Roo.bootstrap.layout");/*
34971  * Based on:
34972  * Ext JS Library 1.1.1
34973  * Copyright(c) 2006-2007, Ext JS, LLC.
34974  *
34975  * Originally Released Under LGPL - original licence link has changed is not relivant.
34976  *
34977  * Fork - LGPL
34978  * <script type="text/javascript">
34979  */
34980
34981 /**
34982  * @class Roo.bootstrap.layout.Manager
34983  * @extends Roo.bootstrap.Component
34984  * Base class for layout managers.
34985  */
34986 Roo.bootstrap.layout.Manager = function(config)
34987 {
34988     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34989
34990
34991
34992
34993
34994     /** false to disable window resize monitoring @type Boolean */
34995     this.monitorWindowResize = true;
34996     this.regions = {};
34997     this.addEvents({
34998         /**
34999          * @event layout
35000          * Fires when a layout is performed.
35001          * @param {Roo.LayoutManager} this
35002          */
35003         "layout" : true,
35004         /**
35005          * @event regionresized
35006          * Fires when the user resizes a region.
35007          * @param {Roo.LayoutRegion} region The resized region
35008          * @param {Number} newSize The new size (width for east/west, height for north/south)
35009          */
35010         "regionresized" : true,
35011         /**
35012          * @event regioncollapsed
35013          * Fires when a region is collapsed.
35014          * @param {Roo.LayoutRegion} region The collapsed region
35015          */
35016         "regioncollapsed" : true,
35017         /**
35018          * @event regionexpanded
35019          * Fires when a region is expanded.
35020          * @param {Roo.LayoutRegion} region The expanded region
35021          */
35022         "regionexpanded" : true
35023     });
35024     this.updating = false;
35025
35026     if (config.el) {
35027         this.el = Roo.get(config.el);
35028         this.initEvents();
35029     }
35030
35031 };
35032
35033 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35034
35035
35036     regions : null,
35037
35038     monitorWindowResize : true,
35039
35040
35041     updating : false,
35042
35043
35044     onRender : function(ct, position)
35045     {
35046         if(!this.el){
35047             this.el = Roo.get(ct);
35048             this.initEvents();
35049         }
35050         //this.fireEvent('render',this);
35051     },
35052
35053
35054     initEvents: function()
35055     {
35056
35057
35058         // ie scrollbar fix
35059         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35060             document.body.scroll = "no";
35061         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35062             this.el.position('relative');
35063         }
35064         this.id = this.el.id;
35065         this.el.addClass("roo-layout-container");
35066         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35067         if(this.el.dom != document.body ) {
35068             this.el.on('resize', this.layout,this);
35069             this.el.on('show', this.layout,this);
35070         }
35071
35072     },
35073
35074     /**
35075      * Returns true if this layout is currently being updated
35076      * @return {Boolean}
35077      */
35078     isUpdating : function(){
35079         return this.updating;
35080     },
35081
35082     /**
35083      * Suspend the LayoutManager from doing auto-layouts while
35084      * making multiple add or remove calls
35085      */
35086     beginUpdate : function(){
35087         this.updating = true;
35088     },
35089
35090     /**
35091      * Restore auto-layouts and optionally disable the manager from performing a layout
35092      * @param {Boolean} noLayout true to disable a layout update
35093      */
35094     endUpdate : function(noLayout){
35095         this.updating = false;
35096         if(!noLayout){
35097             this.layout();
35098         }
35099     },
35100
35101     layout: function(){
35102         // abstract...
35103     },
35104
35105     onRegionResized : function(region, newSize){
35106         this.fireEvent("regionresized", region, newSize);
35107         this.layout();
35108     },
35109
35110     onRegionCollapsed : function(region){
35111         this.fireEvent("regioncollapsed", region);
35112     },
35113
35114     onRegionExpanded : function(region){
35115         this.fireEvent("regionexpanded", region);
35116     },
35117
35118     /**
35119      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35120      * performs box-model adjustments.
35121      * @return {Object} The size as an object {width: (the width), height: (the height)}
35122      */
35123     getViewSize : function()
35124     {
35125         var size;
35126         if(this.el.dom != document.body){
35127             size = this.el.getSize();
35128         }else{
35129             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35130         }
35131         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35132         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35133         return size;
35134     },
35135
35136     /**
35137      * Returns the Element this layout is bound to.
35138      * @return {Roo.Element}
35139      */
35140     getEl : function(){
35141         return this.el;
35142     },
35143
35144     /**
35145      * Returns the specified region.
35146      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35147      * @return {Roo.LayoutRegion}
35148      */
35149     getRegion : function(target){
35150         return this.regions[target.toLowerCase()];
35151     },
35152
35153     onWindowResize : function(){
35154         if(this.monitorWindowResize){
35155             this.layout();
35156         }
35157     }
35158 });
35159 /*
35160  * Based on:
35161  * Ext JS Library 1.1.1
35162  * Copyright(c) 2006-2007, Ext JS, LLC.
35163  *
35164  * Originally Released Under LGPL - original licence link has changed is not relivant.
35165  *
35166  * Fork - LGPL
35167  * <script type="text/javascript">
35168  */
35169 /**
35170  * @class Roo.bootstrap.layout.Border
35171  * @extends Roo.bootstrap.layout.Manager
35172  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35173  * please see: examples/bootstrap/nested.html<br><br>
35174  
35175 <b>The container the layout is rendered into can be either the body element or any other element.
35176 If it is not the body element, the container needs to either be an absolute positioned element,
35177 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35178 the container size if it is not the body element.</b>
35179
35180 * @constructor
35181 * Create a new Border
35182 * @param {Object} config Configuration options
35183  */
35184 Roo.bootstrap.layout.Border = function(config){
35185     config = config || {};
35186     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35187     
35188     
35189     
35190     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35191         if(config[region]){
35192             config[region].region = region;
35193             this.addRegion(config[region]);
35194         }
35195     },this);
35196     
35197 };
35198
35199 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35200
35201 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35202     
35203     parent : false, // this might point to a 'nest' or a ???
35204     
35205     /**
35206      * Creates and adds a new region if it doesn't already exist.
35207      * @param {String} target The target region key (north, south, east, west or center).
35208      * @param {Object} config The regions config object
35209      * @return {BorderLayoutRegion} The new region
35210      */
35211     addRegion : function(config)
35212     {
35213         if(!this.regions[config.region]){
35214             var r = this.factory(config);
35215             this.bindRegion(r);
35216         }
35217         return this.regions[config.region];
35218     },
35219
35220     // private (kinda)
35221     bindRegion : function(r){
35222         this.regions[r.config.region] = r;
35223         
35224         r.on("visibilitychange",    this.layout, this);
35225         r.on("paneladded",          this.layout, this);
35226         r.on("panelremoved",        this.layout, this);
35227         r.on("invalidated",         this.layout, this);
35228         r.on("resized",             this.onRegionResized, this);
35229         r.on("collapsed",           this.onRegionCollapsed, this);
35230         r.on("expanded",            this.onRegionExpanded, this);
35231     },
35232
35233     /**
35234      * Performs a layout update.
35235      */
35236     layout : function()
35237     {
35238         if(this.updating) {
35239             return;
35240         }
35241         
35242         // render all the rebions if they have not been done alreayd?
35243         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35244             if(this.regions[region] && !this.regions[region].bodyEl){
35245                 this.regions[region].onRender(this.el)
35246             }
35247         },this);
35248         
35249         var size = this.getViewSize();
35250         var w = size.width;
35251         var h = size.height;
35252         var centerW = w;
35253         var centerH = h;
35254         var centerY = 0;
35255         var centerX = 0;
35256         //var x = 0, y = 0;
35257
35258         var rs = this.regions;
35259         var north = rs["north"];
35260         var south = rs["south"]; 
35261         var west = rs["west"];
35262         var east = rs["east"];
35263         var center = rs["center"];
35264         //if(this.hideOnLayout){ // not supported anymore
35265             //c.el.setStyle("display", "none");
35266         //}
35267         if(north && north.isVisible()){
35268             var b = north.getBox();
35269             var m = north.getMargins();
35270             b.width = w - (m.left+m.right);
35271             b.x = m.left;
35272             b.y = m.top;
35273             centerY = b.height + b.y + m.bottom;
35274             centerH -= centerY;
35275             north.updateBox(this.safeBox(b));
35276         }
35277         if(south && south.isVisible()){
35278             var b = south.getBox();
35279             var m = south.getMargins();
35280             b.width = w - (m.left+m.right);
35281             b.x = m.left;
35282             var totalHeight = (b.height + m.top + m.bottom);
35283             b.y = h - totalHeight + m.top;
35284             centerH -= totalHeight;
35285             south.updateBox(this.safeBox(b));
35286         }
35287         if(west && west.isVisible()){
35288             var b = west.getBox();
35289             var m = west.getMargins();
35290             b.height = centerH - (m.top+m.bottom);
35291             b.x = m.left;
35292             b.y = centerY + m.top;
35293             var totalWidth = (b.width + m.left + m.right);
35294             centerX += totalWidth;
35295             centerW -= totalWidth;
35296             west.updateBox(this.safeBox(b));
35297         }
35298         if(east && east.isVisible()){
35299             var b = east.getBox();
35300             var m = east.getMargins();
35301             b.height = centerH - (m.top+m.bottom);
35302             var totalWidth = (b.width + m.left + m.right);
35303             b.x = w - totalWidth + m.left;
35304             b.y = centerY + m.top;
35305             centerW -= totalWidth;
35306             east.updateBox(this.safeBox(b));
35307         }
35308         if(center){
35309             var m = center.getMargins();
35310             var centerBox = {
35311                 x: centerX + m.left,
35312                 y: centerY + m.top,
35313                 width: centerW - (m.left+m.right),
35314                 height: centerH - (m.top+m.bottom)
35315             };
35316             //if(this.hideOnLayout){
35317                 //center.el.setStyle("display", "block");
35318             //}
35319             center.updateBox(this.safeBox(centerBox));
35320         }
35321         this.el.repaint();
35322         this.fireEvent("layout", this);
35323     },
35324
35325     // private
35326     safeBox : function(box){
35327         box.width = Math.max(0, box.width);
35328         box.height = Math.max(0, box.height);
35329         return box;
35330     },
35331
35332     /**
35333      * Adds a ContentPanel (or subclass) to this layout.
35334      * @param {String} target The target region key (north, south, east, west or center).
35335      * @param {Roo.ContentPanel} panel The panel to add
35336      * @return {Roo.ContentPanel} The added panel
35337      */
35338     add : function(target, panel){
35339          
35340         target = target.toLowerCase();
35341         return this.regions[target].add(panel);
35342     },
35343
35344     /**
35345      * Remove a ContentPanel (or subclass) to this layout.
35346      * @param {String} target The target region key (north, south, east, west or center).
35347      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35348      * @return {Roo.ContentPanel} The removed panel
35349      */
35350     remove : function(target, panel){
35351         target = target.toLowerCase();
35352         return this.regions[target].remove(panel);
35353     },
35354
35355     /**
35356      * Searches all regions for a panel with the specified id
35357      * @param {String} panelId
35358      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35359      */
35360     findPanel : function(panelId){
35361         var rs = this.regions;
35362         for(var target in rs){
35363             if(typeof rs[target] != "function"){
35364                 var p = rs[target].getPanel(panelId);
35365                 if(p){
35366                     return p;
35367                 }
35368             }
35369         }
35370         return null;
35371     },
35372
35373     /**
35374      * Searches all regions for a panel with the specified id and activates (shows) it.
35375      * @param {String/ContentPanel} panelId The panels id or the panel itself
35376      * @return {Roo.ContentPanel} The shown panel or null
35377      */
35378     showPanel : function(panelId) {
35379       var rs = this.regions;
35380       for(var target in rs){
35381          var r = rs[target];
35382          if(typeof r != "function"){
35383             if(r.hasPanel(panelId)){
35384                return r.showPanel(panelId);
35385             }
35386          }
35387       }
35388       return null;
35389    },
35390
35391    /**
35392      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35393      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35394      */
35395    /*
35396     restoreState : function(provider){
35397         if(!provider){
35398             provider = Roo.state.Manager;
35399         }
35400         var sm = new Roo.LayoutStateManager();
35401         sm.init(this, provider);
35402     },
35403 */
35404  
35405  
35406     /**
35407      * Adds a xtype elements to the layout.
35408      * <pre><code>
35409
35410 layout.addxtype({
35411        xtype : 'ContentPanel',
35412        region: 'west',
35413        items: [ .... ]
35414    }
35415 );
35416
35417 layout.addxtype({
35418         xtype : 'NestedLayoutPanel',
35419         region: 'west',
35420         layout: {
35421            center: { },
35422            west: { }   
35423         },
35424         items : [ ... list of content panels or nested layout panels.. ]
35425    }
35426 );
35427 </code></pre>
35428      * @param {Object} cfg Xtype definition of item to add.
35429      */
35430     addxtype : function(cfg)
35431     {
35432         // basically accepts a pannel...
35433         // can accept a layout region..!?!?
35434         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35435         
35436         
35437         // theory?  children can only be panels??
35438         
35439         //if (!cfg.xtype.match(/Panel$/)) {
35440         //    return false;
35441         //}
35442         var ret = false;
35443         
35444         if (typeof(cfg.region) == 'undefined') {
35445             Roo.log("Failed to add Panel, region was not set");
35446             Roo.log(cfg);
35447             return false;
35448         }
35449         var region = cfg.region;
35450         delete cfg.region;
35451         
35452           
35453         var xitems = [];
35454         if (cfg.items) {
35455             xitems = cfg.items;
35456             delete cfg.items;
35457         }
35458         var nb = false;
35459         
35460         if ( region == 'center') {
35461             Roo.log("Center: " + cfg.title);
35462         }
35463         
35464         
35465         switch(cfg.xtype) 
35466         {
35467             case 'Content':  // ContentPanel (el, cfg)
35468             case 'Scroll':  // ContentPanel (el, cfg)
35469             case 'View': 
35470                 cfg.autoCreate = cfg.autoCreate || true;
35471                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35472                 //} else {
35473                 //    var el = this.el.createChild();
35474                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35475                 //}
35476                 
35477                 this.add(region, ret);
35478                 break;
35479             
35480             /*
35481             case 'TreePanel': // our new panel!
35482                 cfg.el = this.el.createChild();
35483                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35484                 this.add(region, ret);
35485                 break;
35486             */
35487             
35488             case 'Nest': 
35489                 // create a new Layout (which is  a Border Layout...
35490                 
35491                 var clayout = cfg.layout;
35492                 clayout.el  = this.el.createChild();
35493                 clayout.items   = clayout.items  || [];
35494                 
35495                 delete cfg.layout;
35496                 
35497                 // replace this exitems with the clayout ones..
35498                 xitems = clayout.items;
35499                  
35500                 // force background off if it's in center...
35501                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35502                     cfg.background = false;
35503                 }
35504                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35505                 
35506                 
35507                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35508                 //console.log('adding nested layout panel '  + cfg.toSource());
35509                 this.add(region, ret);
35510                 nb = {}; /// find first...
35511                 break;
35512             
35513             case 'Grid':
35514                 
35515                 // needs grid and region
35516                 
35517                 //var el = this.getRegion(region).el.createChild();
35518                 /*
35519                  *var el = this.el.createChild();
35520                 // create the grid first...
35521                 cfg.grid.container = el;
35522                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35523                 */
35524                 
35525                 if (region == 'center' && this.active ) {
35526                     cfg.background = false;
35527                 }
35528                 
35529                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35530                 
35531                 this.add(region, ret);
35532                 /*
35533                 if (cfg.background) {
35534                     // render grid on panel activation (if panel background)
35535                     ret.on('activate', function(gp) {
35536                         if (!gp.grid.rendered) {
35537                     //        gp.grid.render(el);
35538                         }
35539                     });
35540                 } else {
35541                   //  cfg.grid.render(el);
35542                 }
35543                 */
35544                 break;
35545            
35546            
35547             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35548                 // it was the old xcomponent building that caused this before.
35549                 // espeically if border is the top element in the tree.
35550                 ret = this;
35551                 break; 
35552                 
35553                     
35554                 
35555                 
35556                 
35557             default:
35558                 /*
35559                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35560                     
35561                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35562                     this.add(region, ret);
35563                 } else {
35564                 */
35565                     Roo.log(cfg);
35566                     throw "Can not add '" + cfg.xtype + "' to Border";
35567                     return null;
35568              
35569                                 
35570              
35571         }
35572         this.beginUpdate();
35573         // add children..
35574         var region = '';
35575         var abn = {};
35576         Roo.each(xitems, function(i)  {
35577             region = nb && i.region ? i.region : false;
35578             
35579             var add = ret.addxtype(i);
35580            
35581             if (region) {
35582                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35583                 if (!i.background) {
35584                     abn[region] = nb[region] ;
35585                 }
35586             }
35587             
35588         });
35589         this.endUpdate();
35590
35591         // make the last non-background panel active..
35592         //if (nb) { Roo.log(abn); }
35593         if (nb) {
35594             
35595             for(var r in abn) {
35596                 region = this.getRegion(r);
35597                 if (region) {
35598                     // tried using nb[r], but it does not work..
35599                      
35600                     region.showPanel(abn[r]);
35601                    
35602                 }
35603             }
35604         }
35605         return ret;
35606         
35607     },
35608     
35609     
35610 // private
35611     factory : function(cfg)
35612     {
35613         
35614         var validRegions = Roo.bootstrap.layout.Border.regions;
35615
35616         var target = cfg.region;
35617         cfg.mgr = this;
35618         
35619         var r = Roo.bootstrap.layout;
35620         Roo.log(target);
35621         switch(target){
35622             case "north":
35623                 return new r.North(cfg);
35624             case "south":
35625                 return new r.South(cfg);
35626             case "east":
35627                 return new r.East(cfg);
35628             case "west":
35629                 return new r.West(cfg);
35630             case "center":
35631                 return new r.Center(cfg);
35632         }
35633         throw 'Layout region "'+target+'" not supported.';
35634     }
35635     
35636     
35637 });
35638  /*
35639  * Based on:
35640  * Ext JS Library 1.1.1
35641  * Copyright(c) 2006-2007, Ext JS, LLC.
35642  *
35643  * Originally Released Under LGPL - original licence link has changed is not relivant.
35644  *
35645  * Fork - LGPL
35646  * <script type="text/javascript">
35647  */
35648  
35649 /**
35650  * @class Roo.bootstrap.layout.Basic
35651  * @extends Roo.util.Observable
35652  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35653  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35654  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35655  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35656  * @cfg {string}   region  the region that it inhabits..
35657  * @cfg {bool}   skipConfig skip config?
35658  * 
35659
35660  */
35661 Roo.bootstrap.layout.Basic = function(config){
35662     
35663     this.mgr = config.mgr;
35664     
35665     this.position = config.region;
35666     
35667     var skipConfig = config.skipConfig;
35668     
35669     this.events = {
35670         /**
35671          * @scope Roo.BasicLayoutRegion
35672          */
35673         
35674         /**
35675          * @event beforeremove
35676          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35677          * @param {Roo.LayoutRegion} this
35678          * @param {Roo.ContentPanel} panel The panel
35679          * @param {Object} e The cancel event object
35680          */
35681         "beforeremove" : true,
35682         /**
35683          * @event invalidated
35684          * Fires when the layout for this region is changed.
35685          * @param {Roo.LayoutRegion} this
35686          */
35687         "invalidated" : true,
35688         /**
35689          * @event visibilitychange
35690          * Fires when this region is shown or hidden 
35691          * @param {Roo.LayoutRegion} this
35692          * @param {Boolean} visibility true or false
35693          */
35694         "visibilitychange" : true,
35695         /**
35696          * @event paneladded
35697          * Fires when a panel is added. 
35698          * @param {Roo.LayoutRegion} this
35699          * @param {Roo.ContentPanel} panel The panel
35700          */
35701         "paneladded" : true,
35702         /**
35703          * @event panelremoved
35704          * Fires when a panel is removed. 
35705          * @param {Roo.LayoutRegion} this
35706          * @param {Roo.ContentPanel} panel The panel
35707          */
35708         "panelremoved" : true,
35709         /**
35710          * @event beforecollapse
35711          * Fires when this region before collapse.
35712          * @param {Roo.LayoutRegion} this
35713          */
35714         "beforecollapse" : true,
35715         /**
35716          * @event collapsed
35717          * Fires when this region is collapsed.
35718          * @param {Roo.LayoutRegion} this
35719          */
35720         "collapsed" : true,
35721         /**
35722          * @event expanded
35723          * Fires when this region is expanded.
35724          * @param {Roo.LayoutRegion} this
35725          */
35726         "expanded" : true,
35727         /**
35728          * @event slideshow
35729          * Fires when this region is slid into view.
35730          * @param {Roo.LayoutRegion} this
35731          */
35732         "slideshow" : true,
35733         /**
35734          * @event slidehide
35735          * Fires when this region slides out of view. 
35736          * @param {Roo.LayoutRegion} this
35737          */
35738         "slidehide" : true,
35739         /**
35740          * @event panelactivated
35741          * Fires when a panel is activated. 
35742          * @param {Roo.LayoutRegion} this
35743          * @param {Roo.ContentPanel} panel The activated panel
35744          */
35745         "panelactivated" : true,
35746         /**
35747          * @event resized
35748          * Fires when the user resizes this region. 
35749          * @param {Roo.LayoutRegion} this
35750          * @param {Number} newSize The new size (width for east/west, height for north/south)
35751          */
35752         "resized" : true
35753     };
35754     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35755     this.panels = new Roo.util.MixedCollection();
35756     this.panels.getKey = this.getPanelId.createDelegate(this);
35757     this.box = null;
35758     this.activePanel = null;
35759     // ensure listeners are added...
35760     
35761     if (config.listeners || config.events) {
35762         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35763             listeners : config.listeners || {},
35764             events : config.events || {}
35765         });
35766     }
35767     
35768     if(skipConfig !== true){
35769         this.applyConfig(config);
35770     }
35771 };
35772
35773 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35774 {
35775     getPanelId : function(p){
35776         return p.getId();
35777     },
35778     
35779     applyConfig : function(config){
35780         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35781         this.config = config;
35782         
35783     },
35784     
35785     /**
35786      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35787      * the width, for horizontal (north, south) the height.
35788      * @param {Number} newSize The new width or height
35789      */
35790     resizeTo : function(newSize){
35791         var el = this.el ? this.el :
35792                  (this.activePanel ? this.activePanel.getEl() : null);
35793         if(el){
35794             switch(this.position){
35795                 case "east":
35796                 case "west":
35797                     el.setWidth(newSize);
35798                     this.fireEvent("resized", this, newSize);
35799                 break;
35800                 case "north":
35801                 case "south":
35802                     el.setHeight(newSize);
35803                     this.fireEvent("resized", this, newSize);
35804                 break;                
35805             }
35806         }
35807     },
35808     
35809     getBox : function(){
35810         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35811     },
35812     
35813     getMargins : function(){
35814         return this.margins;
35815     },
35816     
35817     updateBox : function(box){
35818         this.box = box;
35819         var el = this.activePanel.getEl();
35820         el.dom.style.left = box.x + "px";
35821         el.dom.style.top = box.y + "px";
35822         this.activePanel.setSize(box.width, box.height);
35823     },
35824     
35825     /**
35826      * Returns the container element for this region.
35827      * @return {Roo.Element}
35828      */
35829     getEl : function(){
35830         return this.activePanel;
35831     },
35832     
35833     /**
35834      * Returns true if this region is currently visible.
35835      * @return {Boolean}
35836      */
35837     isVisible : function(){
35838         return this.activePanel ? true : false;
35839     },
35840     
35841     setActivePanel : function(panel){
35842         panel = this.getPanel(panel);
35843         if(this.activePanel && this.activePanel != panel){
35844             this.activePanel.setActiveState(false);
35845             this.activePanel.getEl().setLeftTop(-10000,-10000);
35846         }
35847         this.activePanel = panel;
35848         panel.setActiveState(true);
35849         if(this.box){
35850             panel.setSize(this.box.width, this.box.height);
35851         }
35852         this.fireEvent("panelactivated", this, panel);
35853         this.fireEvent("invalidated");
35854     },
35855     
35856     /**
35857      * Show the specified panel.
35858      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35859      * @return {Roo.ContentPanel} The shown panel or null
35860      */
35861     showPanel : function(panel){
35862         panel = this.getPanel(panel);
35863         if(panel){
35864             this.setActivePanel(panel);
35865         }
35866         return panel;
35867     },
35868     
35869     /**
35870      * Get the active panel for this region.
35871      * @return {Roo.ContentPanel} The active panel or null
35872      */
35873     getActivePanel : function(){
35874         return this.activePanel;
35875     },
35876     
35877     /**
35878      * Add the passed ContentPanel(s)
35879      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35880      * @return {Roo.ContentPanel} The panel added (if only one was added)
35881      */
35882     add : function(panel){
35883         if(arguments.length > 1){
35884             for(var i = 0, len = arguments.length; i < len; i++) {
35885                 this.add(arguments[i]);
35886             }
35887             return null;
35888         }
35889         if(this.hasPanel(panel)){
35890             this.showPanel(panel);
35891             return panel;
35892         }
35893         var el = panel.getEl();
35894         if(el.dom.parentNode != this.mgr.el.dom){
35895             this.mgr.el.dom.appendChild(el.dom);
35896         }
35897         if(panel.setRegion){
35898             panel.setRegion(this);
35899         }
35900         this.panels.add(panel);
35901         el.setStyle("position", "absolute");
35902         if(!panel.background){
35903             this.setActivePanel(panel);
35904             if(this.config.initialSize && this.panels.getCount()==1){
35905                 this.resizeTo(this.config.initialSize);
35906             }
35907         }
35908         this.fireEvent("paneladded", this, panel);
35909         return panel;
35910     },
35911     
35912     /**
35913      * Returns true if the panel is in this region.
35914      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35915      * @return {Boolean}
35916      */
35917     hasPanel : function(panel){
35918         if(typeof panel == "object"){ // must be panel obj
35919             panel = panel.getId();
35920         }
35921         return this.getPanel(panel) ? true : false;
35922     },
35923     
35924     /**
35925      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35926      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35927      * @param {Boolean} preservePanel Overrides the config preservePanel option
35928      * @return {Roo.ContentPanel} The panel that was removed
35929      */
35930     remove : function(panel, preservePanel){
35931         panel = this.getPanel(panel);
35932         if(!panel){
35933             return null;
35934         }
35935         var e = {};
35936         this.fireEvent("beforeremove", this, panel, e);
35937         if(e.cancel === true){
35938             return null;
35939         }
35940         var panelId = panel.getId();
35941         this.panels.removeKey(panelId);
35942         return panel;
35943     },
35944     
35945     /**
35946      * Returns the panel specified or null if it's not in this region.
35947      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35948      * @return {Roo.ContentPanel}
35949      */
35950     getPanel : function(id){
35951         if(typeof id == "object"){ // must be panel obj
35952             return id;
35953         }
35954         return this.panels.get(id);
35955     },
35956     
35957     /**
35958      * Returns this regions position (north/south/east/west/center).
35959      * @return {String} 
35960      */
35961     getPosition: function(){
35962         return this.position;    
35963     }
35964 });/*
35965  * Based on:
35966  * Ext JS Library 1.1.1
35967  * Copyright(c) 2006-2007, Ext JS, LLC.
35968  *
35969  * Originally Released Under LGPL - original licence link has changed is not relivant.
35970  *
35971  * Fork - LGPL
35972  * <script type="text/javascript">
35973  */
35974  
35975 /**
35976  * @class Roo.bootstrap.layout.Region
35977  * @extends Roo.bootstrap.layout.Basic
35978  * This class represents a region in a layout manager.
35979  
35980  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35981  * @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})
35982  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35983  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35984  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35985  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35986  * @cfg {String}    title           The title for the region (overrides panel titles)
35987  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35988  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35989  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35990  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35991  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35992  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35993  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35994  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35995  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35996  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35997
35998  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35999  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36000  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36001  * @cfg {Number}    width           For East/West panels
36002  * @cfg {Number}    height          For North/South panels
36003  * @cfg {Boolean}   split           To show the splitter
36004  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36005  * 
36006  * @cfg {string}   cls             Extra CSS classes to add to region
36007  * 
36008  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36009  * @cfg {string}   region  the region that it inhabits..
36010  *
36011
36012  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36013  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36014
36015  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36016  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36017  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36018  */
36019 Roo.bootstrap.layout.Region = function(config)
36020 {
36021     this.applyConfig(config);
36022
36023     var mgr = config.mgr;
36024     var pos = config.region;
36025     config.skipConfig = true;
36026     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36027     
36028     if (mgr.el) {
36029         this.onRender(mgr.el);   
36030     }
36031      
36032     this.visible = true;
36033     this.collapsed = false;
36034     this.unrendered_panels = [];
36035 };
36036
36037 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36038
36039     position: '', // set by wrapper (eg. north/south etc..)
36040     unrendered_panels : null,  // unrendered panels.
36041     
36042     tabPosition : false,
36043     
36044     mgr: false, // points to 'Border'
36045     
36046     
36047     createBody : function(){
36048         /** This region's body element 
36049         * @type Roo.Element */
36050         this.bodyEl = this.el.createChild({
36051                 tag: "div",
36052                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36053         });
36054     },
36055
36056     onRender: function(ctr, pos)
36057     {
36058         var dh = Roo.DomHelper;
36059         /** This region's container element 
36060         * @type Roo.Element */
36061         this.el = dh.append(ctr.dom, {
36062                 tag: "div",
36063                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36064             }, true);
36065         /** This region's title element 
36066         * @type Roo.Element */
36067     
36068         this.titleEl = dh.append(this.el.dom,  {
36069                 tag: "div",
36070                 unselectable: "on",
36071                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36072                 children:[
36073                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36074                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36075                 ]
36076             }, true);
36077         
36078         this.titleEl.enableDisplayMode();
36079         /** This region's title text element 
36080         * @type HTMLElement */
36081         this.titleTextEl = this.titleEl.dom.firstChild;
36082         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36083         /*
36084         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36085         this.closeBtn.enableDisplayMode();
36086         this.closeBtn.on("click", this.closeClicked, this);
36087         this.closeBtn.hide();
36088     */
36089         this.createBody(this.config);
36090         if(this.config.hideWhenEmpty){
36091             this.hide();
36092             this.on("paneladded", this.validateVisibility, this);
36093             this.on("panelremoved", this.validateVisibility, this);
36094         }
36095         if(this.autoScroll){
36096             this.bodyEl.setStyle("overflow", "auto");
36097         }else{
36098             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36099         }
36100         //if(c.titlebar !== false){
36101             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36102                 this.titleEl.hide();
36103             }else{
36104                 this.titleEl.show();
36105                 if(this.config.title){
36106                     this.titleTextEl.innerHTML = this.config.title;
36107                 }
36108             }
36109         //}
36110         if(this.config.collapsed){
36111             this.collapse(true);
36112         }
36113         if(this.config.hidden){
36114             this.hide();
36115         }
36116         
36117         if (this.unrendered_panels && this.unrendered_panels.length) {
36118             for (var i =0;i< this.unrendered_panels.length; i++) {
36119                 this.add(this.unrendered_panels[i]);
36120             }
36121             this.unrendered_panels = null;
36122             
36123         }
36124         
36125     },
36126     
36127     applyConfig : function(c)
36128     {
36129         /*
36130          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36131             var dh = Roo.DomHelper;
36132             if(c.titlebar !== false){
36133                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36134                 this.collapseBtn.on("click", this.collapse, this);
36135                 this.collapseBtn.enableDisplayMode();
36136                 /*
36137                 if(c.showPin === true || this.showPin){
36138                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36139                     this.stickBtn.enableDisplayMode();
36140                     this.stickBtn.on("click", this.expand, this);
36141                     this.stickBtn.hide();
36142                 }
36143                 
36144             }
36145             */
36146             /** This region's collapsed element
36147             * @type Roo.Element */
36148             /*
36149              *
36150             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36151                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36152             ]}, true);
36153             
36154             if(c.floatable !== false){
36155                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36156                this.collapsedEl.on("click", this.collapseClick, this);
36157             }
36158
36159             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36160                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36161                    id: "message", unselectable: "on", style:{"float":"left"}});
36162                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36163              }
36164             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36165             this.expandBtn.on("click", this.expand, this);
36166             
36167         }
36168         
36169         if(this.collapseBtn){
36170             this.collapseBtn.setVisible(c.collapsible == true);
36171         }
36172         
36173         this.cmargins = c.cmargins || this.cmargins ||
36174                          (this.position == "west" || this.position == "east" ?
36175                              {top: 0, left: 2, right:2, bottom: 0} :
36176                              {top: 2, left: 0, right:0, bottom: 2});
36177         */
36178         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36179         
36180         
36181         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36182         
36183         this.autoScroll = c.autoScroll || false;
36184         
36185         
36186        
36187         
36188         this.duration = c.duration || .30;
36189         this.slideDuration = c.slideDuration || .45;
36190         this.config = c;
36191        
36192     },
36193     /**
36194      * Returns true if this region is currently visible.
36195      * @return {Boolean}
36196      */
36197     isVisible : function(){
36198         return this.visible;
36199     },
36200
36201     /**
36202      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36203      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36204      */
36205     //setCollapsedTitle : function(title){
36206     //    title = title || "&#160;";
36207      //   if(this.collapsedTitleTextEl){
36208       //      this.collapsedTitleTextEl.innerHTML = title;
36209        // }
36210     //},
36211
36212     getBox : function(){
36213         var b;
36214       //  if(!this.collapsed){
36215             b = this.el.getBox(false, true);
36216        // }else{
36217           //  b = this.collapsedEl.getBox(false, true);
36218         //}
36219         return b;
36220     },
36221
36222     getMargins : function(){
36223         return this.margins;
36224         //return this.collapsed ? this.cmargins : this.margins;
36225     },
36226 /*
36227     highlight : function(){
36228         this.el.addClass("x-layout-panel-dragover");
36229     },
36230
36231     unhighlight : function(){
36232         this.el.removeClass("x-layout-panel-dragover");
36233     },
36234 */
36235     updateBox : function(box)
36236     {
36237         if (!this.bodyEl) {
36238             return; // not rendered yet..
36239         }
36240         
36241         this.box = box;
36242         if(!this.collapsed){
36243             this.el.dom.style.left = box.x + "px";
36244             this.el.dom.style.top = box.y + "px";
36245             this.updateBody(box.width, box.height);
36246         }else{
36247             this.collapsedEl.dom.style.left = box.x + "px";
36248             this.collapsedEl.dom.style.top = box.y + "px";
36249             this.collapsedEl.setSize(box.width, box.height);
36250         }
36251         if(this.tabs){
36252             this.tabs.autoSizeTabs();
36253         }
36254     },
36255
36256     updateBody : function(w, h)
36257     {
36258         if(w !== null){
36259             this.el.setWidth(w);
36260             w -= this.el.getBorderWidth("rl");
36261             if(this.config.adjustments){
36262                 w += this.config.adjustments[0];
36263             }
36264         }
36265         if(h !== null && h > 0){
36266             this.el.setHeight(h);
36267             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36268             h -= this.el.getBorderWidth("tb");
36269             if(this.config.adjustments){
36270                 h += this.config.adjustments[1];
36271             }
36272             this.bodyEl.setHeight(h);
36273             if(this.tabs){
36274                 h = this.tabs.syncHeight(h);
36275             }
36276         }
36277         if(this.panelSize){
36278             w = w !== null ? w : this.panelSize.width;
36279             h = h !== null ? h : this.panelSize.height;
36280         }
36281         if(this.activePanel){
36282             var el = this.activePanel.getEl();
36283             w = w !== null ? w : el.getWidth();
36284             h = h !== null ? h : el.getHeight();
36285             this.panelSize = {width: w, height: h};
36286             this.activePanel.setSize(w, h);
36287         }
36288         if(Roo.isIE && this.tabs){
36289             this.tabs.el.repaint();
36290         }
36291     },
36292
36293     /**
36294      * Returns the container element for this region.
36295      * @return {Roo.Element}
36296      */
36297     getEl : function(){
36298         return this.el;
36299     },
36300
36301     /**
36302      * Hides this region.
36303      */
36304     hide : function(){
36305         //if(!this.collapsed){
36306             this.el.dom.style.left = "-2000px";
36307             this.el.hide();
36308         //}else{
36309          //   this.collapsedEl.dom.style.left = "-2000px";
36310          //   this.collapsedEl.hide();
36311        // }
36312         this.visible = false;
36313         this.fireEvent("visibilitychange", this, false);
36314     },
36315
36316     /**
36317      * Shows this region if it was previously hidden.
36318      */
36319     show : function(){
36320         //if(!this.collapsed){
36321             this.el.show();
36322         //}else{
36323         //    this.collapsedEl.show();
36324        // }
36325         this.visible = true;
36326         this.fireEvent("visibilitychange", this, true);
36327     },
36328 /*
36329     closeClicked : function(){
36330         if(this.activePanel){
36331             this.remove(this.activePanel);
36332         }
36333     },
36334
36335     collapseClick : function(e){
36336         if(this.isSlid){
36337            e.stopPropagation();
36338            this.slideIn();
36339         }else{
36340            e.stopPropagation();
36341            this.slideOut();
36342         }
36343     },
36344 */
36345     /**
36346      * Collapses this region.
36347      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36348      */
36349     /*
36350     collapse : function(skipAnim, skipCheck = false){
36351         if(this.collapsed) {
36352             return;
36353         }
36354         
36355         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36356             
36357             this.collapsed = true;
36358             if(this.split){
36359                 this.split.el.hide();
36360             }
36361             if(this.config.animate && skipAnim !== true){
36362                 this.fireEvent("invalidated", this);
36363                 this.animateCollapse();
36364             }else{
36365                 this.el.setLocation(-20000,-20000);
36366                 this.el.hide();
36367                 this.collapsedEl.show();
36368                 this.fireEvent("collapsed", this);
36369                 this.fireEvent("invalidated", this);
36370             }
36371         }
36372         
36373     },
36374 */
36375     animateCollapse : function(){
36376         // overridden
36377     },
36378
36379     /**
36380      * Expands this region if it was previously collapsed.
36381      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36382      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36383      */
36384     /*
36385     expand : function(e, skipAnim){
36386         if(e) {
36387             e.stopPropagation();
36388         }
36389         if(!this.collapsed || this.el.hasActiveFx()) {
36390             return;
36391         }
36392         if(this.isSlid){
36393             this.afterSlideIn();
36394             skipAnim = true;
36395         }
36396         this.collapsed = false;
36397         if(this.config.animate && skipAnim !== true){
36398             this.animateExpand();
36399         }else{
36400             this.el.show();
36401             if(this.split){
36402                 this.split.el.show();
36403             }
36404             this.collapsedEl.setLocation(-2000,-2000);
36405             this.collapsedEl.hide();
36406             this.fireEvent("invalidated", this);
36407             this.fireEvent("expanded", this);
36408         }
36409     },
36410 */
36411     animateExpand : function(){
36412         // overridden
36413     },
36414
36415     initTabs : function()
36416     {
36417         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36418         
36419         var ts = new Roo.bootstrap.panel.Tabs({
36420             el: this.bodyEl.dom,
36421             region : this,
36422             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36423             disableTooltips: this.config.disableTabTips,
36424             toolbar : this.config.toolbar
36425         });
36426         
36427         if(this.config.hideTabs){
36428             ts.stripWrap.setDisplayed(false);
36429         }
36430         this.tabs = ts;
36431         ts.resizeTabs = this.config.resizeTabs === true;
36432         ts.minTabWidth = this.config.minTabWidth || 40;
36433         ts.maxTabWidth = this.config.maxTabWidth || 250;
36434         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36435         ts.monitorResize = false;
36436         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36437         ts.bodyEl.addClass('roo-layout-tabs-body');
36438         this.panels.each(this.initPanelAsTab, this);
36439     },
36440
36441     initPanelAsTab : function(panel){
36442         var ti = this.tabs.addTab(
36443             panel.getEl().id,
36444             panel.getTitle(),
36445             null,
36446             this.config.closeOnTab && panel.isClosable(),
36447             panel.tpl
36448         );
36449         if(panel.tabTip !== undefined){
36450             ti.setTooltip(panel.tabTip);
36451         }
36452         ti.on("activate", function(){
36453               this.setActivePanel(panel);
36454         }, this);
36455         
36456         if(this.config.closeOnTab){
36457             ti.on("beforeclose", function(t, e){
36458                 e.cancel = true;
36459                 this.remove(panel);
36460             }, this);
36461         }
36462         
36463         panel.tabItem = ti;
36464         
36465         return ti;
36466     },
36467
36468     updatePanelTitle : function(panel, title)
36469     {
36470         if(this.activePanel == panel){
36471             this.updateTitle(title);
36472         }
36473         if(this.tabs){
36474             var ti = this.tabs.getTab(panel.getEl().id);
36475             ti.setText(title);
36476             if(panel.tabTip !== undefined){
36477                 ti.setTooltip(panel.tabTip);
36478             }
36479         }
36480     },
36481
36482     updateTitle : function(title){
36483         if(this.titleTextEl && !this.config.title){
36484             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36485         }
36486     },
36487
36488     setActivePanel : function(panel)
36489     {
36490         panel = this.getPanel(panel);
36491         if(this.activePanel && this.activePanel != panel){
36492             if(this.activePanel.setActiveState(false) === false){
36493                 return;
36494             }
36495         }
36496         this.activePanel = panel;
36497         panel.setActiveState(true);
36498         if(this.panelSize){
36499             panel.setSize(this.panelSize.width, this.panelSize.height);
36500         }
36501         if(this.closeBtn){
36502             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36503         }
36504         this.updateTitle(panel.getTitle());
36505         if(this.tabs){
36506             this.fireEvent("invalidated", this);
36507         }
36508         this.fireEvent("panelactivated", this, panel);
36509     },
36510
36511     /**
36512      * Shows the specified panel.
36513      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36514      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36515      */
36516     showPanel : function(panel)
36517     {
36518         panel = this.getPanel(panel);
36519         if(panel){
36520             if(this.tabs){
36521                 var tab = this.tabs.getTab(panel.getEl().id);
36522                 if(tab.isHidden()){
36523                     this.tabs.unhideTab(tab.id);
36524                 }
36525                 tab.activate();
36526             }else{
36527                 this.setActivePanel(panel);
36528             }
36529         }
36530         return panel;
36531     },
36532
36533     /**
36534      * Get the active panel for this region.
36535      * @return {Roo.ContentPanel} The active panel or null
36536      */
36537     getActivePanel : function(){
36538         return this.activePanel;
36539     },
36540
36541     validateVisibility : function(){
36542         if(this.panels.getCount() < 1){
36543             this.updateTitle("&#160;");
36544             this.closeBtn.hide();
36545             this.hide();
36546         }else{
36547             if(!this.isVisible()){
36548                 this.show();
36549             }
36550         }
36551     },
36552
36553     /**
36554      * Adds the passed ContentPanel(s) to this region.
36555      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36556      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36557      */
36558     add : function(panel)
36559     {
36560         if(arguments.length > 1){
36561             for(var i = 0, len = arguments.length; i < len; i++) {
36562                 this.add(arguments[i]);
36563             }
36564             return null;
36565         }
36566         
36567         // if we have not been rendered yet, then we can not really do much of this..
36568         if (!this.bodyEl) {
36569             this.unrendered_panels.push(panel);
36570             return panel;
36571         }
36572         
36573         
36574         
36575         
36576         if(this.hasPanel(panel)){
36577             this.showPanel(panel);
36578             return panel;
36579         }
36580         panel.setRegion(this);
36581         this.panels.add(panel);
36582        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36583             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36584             // and hide them... ???
36585             this.bodyEl.dom.appendChild(panel.getEl().dom);
36586             if(panel.background !== true){
36587                 this.setActivePanel(panel);
36588             }
36589             this.fireEvent("paneladded", this, panel);
36590             return panel;
36591         }
36592         */
36593         if(!this.tabs){
36594             this.initTabs();
36595         }else{
36596             this.initPanelAsTab(panel);
36597         }
36598         
36599         
36600         if(panel.background !== true){
36601             this.tabs.activate(panel.getEl().id);
36602         }
36603         this.fireEvent("paneladded", this, panel);
36604         return panel;
36605     },
36606
36607     /**
36608      * Hides the tab for the specified panel.
36609      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36610      */
36611     hidePanel : function(panel){
36612         if(this.tabs && (panel = this.getPanel(panel))){
36613             this.tabs.hideTab(panel.getEl().id);
36614         }
36615     },
36616
36617     /**
36618      * Unhides the tab for a previously hidden panel.
36619      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36620      */
36621     unhidePanel : function(panel){
36622         if(this.tabs && (panel = this.getPanel(panel))){
36623             this.tabs.unhideTab(panel.getEl().id);
36624         }
36625     },
36626
36627     clearPanels : function(){
36628         while(this.panels.getCount() > 0){
36629              this.remove(this.panels.first());
36630         }
36631     },
36632
36633     /**
36634      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36635      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36636      * @param {Boolean} preservePanel Overrides the config preservePanel option
36637      * @return {Roo.ContentPanel} The panel that was removed
36638      */
36639     remove : function(panel, preservePanel)
36640     {
36641         panel = this.getPanel(panel);
36642         if(!panel){
36643             return null;
36644         }
36645         var e = {};
36646         this.fireEvent("beforeremove", this, panel, e);
36647         if(e.cancel === true){
36648             return null;
36649         }
36650         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36651         var panelId = panel.getId();
36652         this.panels.removeKey(panelId);
36653         if(preservePanel){
36654             document.body.appendChild(panel.getEl().dom);
36655         }
36656         if(this.tabs){
36657             this.tabs.removeTab(panel.getEl().id);
36658         }else if (!preservePanel){
36659             this.bodyEl.dom.removeChild(panel.getEl().dom);
36660         }
36661         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36662             var p = this.panels.first();
36663             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36664             tempEl.appendChild(p.getEl().dom);
36665             this.bodyEl.update("");
36666             this.bodyEl.dom.appendChild(p.getEl().dom);
36667             tempEl = null;
36668             this.updateTitle(p.getTitle());
36669             this.tabs = null;
36670             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36671             this.setActivePanel(p);
36672         }
36673         panel.setRegion(null);
36674         if(this.activePanel == panel){
36675             this.activePanel = null;
36676         }
36677         if(this.config.autoDestroy !== false && preservePanel !== true){
36678             try{panel.destroy();}catch(e){}
36679         }
36680         this.fireEvent("panelremoved", this, panel);
36681         return panel;
36682     },
36683
36684     /**
36685      * Returns the TabPanel component used by this region
36686      * @return {Roo.TabPanel}
36687      */
36688     getTabs : function(){
36689         return this.tabs;
36690     },
36691
36692     createTool : function(parentEl, className){
36693         var btn = Roo.DomHelper.append(parentEl, {
36694             tag: "div",
36695             cls: "x-layout-tools-button",
36696             children: [ {
36697                 tag: "div",
36698                 cls: "roo-layout-tools-button-inner " + className,
36699                 html: "&#160;"
36700             }]
36701         }, true);
36702         btn.addClassOnOver("roo-layout-tools-button-over");
36703         return btn;
36704     }
36705 });/*
36706  * Based on:
36707  * Ext JS Library 1.1.1
36708  * Copyright(c) 2006-2007, Ext JS, LLC.
36709  *
36710  * Originally Released Under LGPL - original licence link has changed is not relivant.
36711  *
36712  * Fork - LGPL
36713  * <script type="text/javascript">
36714  */
36715  
36716
36717
36718 /**
36719  * @class Roo.SplitLayoutRegion
36720  * @extends Roo.LayoutRegion
36721  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36722  */
36723 Roo.bootstrap.layout.Split = function(config){
36724     this.cursor = config.cursor;
36725     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36726 };
36727
36728 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36729 {
36730     splitTip : "Drag to resize.",
36731     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36732     useSplitTips : false,
36733
36734     applyConfig : function(config){
36735         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36736     },
36737     
36738     onRender : function(ctr,pos) {
36739         
36740         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36741         if(!this.config.split){
36742             return;
36743         }
36744         if(!this.split){
36745             
36746             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36747                             tag: "div",
36748                             id: this.el.id + "-split",
36749                             cls: "roo-layout-split roo-layout-split-"+this.position,
36750                             html: "&#160;"
36751             });
36752             /** The SplitBar for this region 
36753             * @type Roo.SplitBar */
36754             // does not exist yet...
36755             Roo.log([this.position, this.orientation]);
36756             
36757             this.split = new Roo.bootstrap.SplitBar({
36758                 dragElement : splitEl,
36759                 resizingElement: this.el,
36760                 orientation : this.orientation
36761             });
36762             
36763             this.split.on("moved", this.onSplitMove, this);
36764             this.split.useShim = this.config.useShim === true;
36765             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36766             if(this.useSplitTips){
36767                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36768             }
36769             //if(config.collapsible){
36770             //    this.split.el.on("dblclick", this.collapse,  this);
36771             //}
36772         }
36773         if(typeof this.config.minSize != "undefined"){
36774             this.split.minSize = this.config.minSize;
36775         }
36776         if(typeof this.config.maxSize != "undefined"){
36777             this.split.maxSize = this.config.maxSize;
36778         }
36779         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36780             this.hideSplitter();
36781         }
36782         
36783     },
36784
36785     getHMaxSize : function(){
36786          var cmax = this.config.maxSize || 10000;
36787          var center = this.mgr.getRegion("center");
36788          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36789     },
36790
36791     getVMaxSize : function(){
36792          var cmax = this.config.maxSize || 10000;
36793          var center = this.mgr.getRegion("center");
36794          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36795     },
36796
36797     onSplitMove : function(split, newSize){
36798         this.fireEvent("resized", this, newSize);
36799     },
36800     
36801     /** 
36802      * Returns the {@link Roo.SplitBar} for this region.
36803      * @return {Roo.SplitBar}
36804      */
36805     getSplitBar : function(){
36806         return this.split;
36807     },
36808     
36809     hide : function(){
36810         this.hideSplitter();
36811         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36812     },
36813
36814     hideSplitter : function(){
36815         if(this.split){
36816             this.split.el.setLocation(-2000,-2000);
36817             this.split.el.hide();
36818         }
36819     },
36820
36821     show : function(){
36822         if(this.split){
36823             this.split.el.show();
36824         }
36825         Roo.bootstrap.layout.Split.superclass.show.call(this);
36826     },
36827     
36828     beforeSlide: function(){
36829         if(Roo.isGecko){// firefox overflow auto bug workaround
36830             this.bodyEl.clip();
36831             if(this.tabs) {
36832                 this.tabs.bodyEl.clip();
36833             }
36834             if(this.activePanel){
36835                 this.activePanel.getEl().clip();
36836                 
36837                 if(this.activePanel.beforeSlide){
36838                     this.activePanel.beforeSlide();
36839                 }
36840             }
36841         }
36842     },
36843     
36844     afterSlide : function(){
36845         if(Roo.isGecko){// firefox overflow auto bug workaround
36846             this.bodyEl.unclip();
36847             if(this.tabs) {
36848                 this.tabs.bodyEl.unclip();
36849             }
36850             if(this.activePanel){
36851                 this.activePanel.getEl().unclip();
36852                 if(this.activePanel.afterSlide){
36853                     this.activePanel.afterSlide();
36854                 }
36855             }
36856         }
36857     },
36858
36859     initAutoHide : function(){
36860         if(this.autoHide !== false){
36861             if(!this.autoHideHd){
36862                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36863                 this.autoHideHd = {
36864                     "mouseout": function(e){
36865                         if(!e.within(this.el, true)){
36866                             st.delay(500);
36867                         }
36868                     },
36869                     "mouseover" : function(e){
36870                         st.cancel();
36871                     },
36872                     scope : this
36873                 };
36874             }
36875             this.el.on(this.autoHideHd);
36876         }
36877     },
36878
36879     clearAutoHide : function(){
36880         if(this.autoHide !== false){
36881             this.el.un("mouseout", this.autoHideHd.mouseout);
36882             this.el.un("mouseover", this.autoHideHd.mouseover);
36883         }
36884     },
36885
36886     clearMonitor : function(){
36887         Roo.get(document).un("click", this.slideInIf, this);
36888     },
36889
36890     // these names are backwards but not changed for compat
36891     slideOut : function(){
36892         if(this.isSlid || this.el.hasActiveFx()){
36893             return;
36894         }
36895         this.isSlid = true;
36896         if(this.collapseBtn){
36897             this.collapseBtn.hide();
36898         }
36899         this.closeBtnState = this.closeBtn.getStyle('display');
36900         this.closeBtn.hide();
36901         if(this.stickBtn){
36902             this.stickBtn.show();
36903         }
36904         this.el.show();
36905         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36906         this.beforeSlide();
36907         this.el.setStyle("z-index", 10001);
36908         this.el.slideIn(this.getSlideAnchor(), {
36909             callback: function(){
36910                 this.afterSlide();
36911                 this.initAutoHide();
36912                 Roo.get(document).on("click", this.slideInIf, this);
36913                 this.fireEvent("slideshow", this);
36914             },
36915             scope: this,
36916             block: true
36917         });
36918     },
36919
36920     afterSlideIn : function(){
36921         this.clearAutoHide();
36922         this.isSlid = false;
36923         this.clearMonitor();
36924         this.el.setStyle("z-index", "");
36925         if(this.collapseBtn){
36926             this.collapseBtn.show();
36927         }
36928         this.closeBtn.setStyle('display', this.closeBtnState);
36929         if(this.stickBtn){
36930             this.stickBtn.hide();
36931         }
36932         this.fireEvent("slidehide", this);
36933     },
36934
36935     slideIn : function(cb){
36936         if(!this.isSlid || this.el.hasActiveFx()){
36937             Roo.callback(cb);
36938             return;
36939         }
36940         this.isSlid = false;
36941         this.beforeSlide();
36942         this.el.slideOut(this.getSlideAnchor(), {
36943             callback: function(){
36944                 this.el.setLeftTop(-10000, -10000);
36945                 this.afterSlide();
36946                 this.afterSlideIn();
36947                 Roo.callback(cb);
36948             },
36949             scope: this,
36950             block: true
36951         });
36952     },
36953     
36954     slideInIf : function(e){
36955         if(!e.within(this.el)){
36956             this.slideIn();
36957         }
36958     },
36959
36960     animateCollapse : function(){
36961         this.beforeSlide();
36962         this.el.setStyle("z-index", 20000);
36963         var anchor = this.getSlideAnchor();
36964         this.el.slideOut(anchor, {
36965             callback : function(){
36966                 this.el.setStyle("z-index", "");
36967                 this.collapsedEl.slideIn(anchor, {duration:.3});
36968                 this.afterSlide();
36969                 this.el.setLocation(-10000,-10000);
36970                 this.el.hide();
36971                 this.fireEvent("collapsed", this);
36972             },
36973             scope: this,
36974             block: true
36975         });
36976     },
36977
36978     animateExpand : function(){
36979         this.beforeSlide();
36980         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36981         this.el.setStyle("z-index", 20000);
36982         this.collapsedEl.hide({
36983             duration:.1
36984         });
36985         this.el.slideIn(this.getSlideAnchor(), {
36986             callback : function(){
36987                 this.el.setStyle("z-index", "");
36988                 this.afterSlide();
36989                 if(this.split){
36990                     this.split.el.show();
36991                 }
36992                 this.fireEvent("invalidated", this);
36993                 this.fireEvent("expanded", this);
36994             },
36995             scope: this,
36996             block: true
36997         });
36998     },
36999
37000     anchors : {
37001         "west" : "left",
37002         "east" : "right",
37003         "north" : "top",
37004         "south" : "bottom"
37005     },
37006
37007     sanchors : {
37008         "west" : "l",
37009         "east" : "r",
37010         "north" : "t",
37011         "south" : "b"
37012     },
37013
37014     canchors : {
37015         "west" : "tl-tr",
37016         "east" : "tr-tl",
37017         "north" : "tl-bl",
37018         "south" : "bl-tl"
37019     },
37020
37021     getAnchor : function(){
37022         return this.anchors[this.position];
37023     },
37024
37025     getCollapseAnchor : function(){
37026         return this.canchors[this.position];
37027     },
37028
37029     getSlideAnchor : function(){
37030         return this.sanchors[this.position];
37031     },
37032
37033     getAlignAdj : function(){
37034         var cm = this.cmargins;
37035         switch(this.position){
37036             case "west":
37037                 return [0, 0];
37038             break;
37039             case "east":
37040                 return [0, 0];
37041             break;
37042             case "north":
37043                 return [0, 0];
37044             break;
37045             case "south":
37046                 return [0, 0];
37047             break;
37048         }
37049     },
37050
37051     getExpandAdj : function(){
37052         var c = this.collapsedEl, cm = this.cmargins;
37053         switch(this.position){
37054             case "west":
37055                 return [-(cm.right+c.getWidth()+cm.left), 0];
37056             break;
37057             case "east":
37058                 return [cm.right+c.getWidth()+cm.left, 0];
37059             break;
37060             case "north":
37061                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37062             break;
37063             case "south":
37064                 return [0, cm.top+cm.bottom+c.getHeight()];
37065             break;
37066         }
37067     }
37068 });/*
37069  * Based on:
37070  * Ext JS Library 1.1.1
37071  * Copyright(c) 2006-2007, Ext JS, LLC.
37072  *
37073  * Originally Released Under LGPL - original licence link has changed is not relivant.
37074  *
37075  * Fork - LGPL
37076  * <script type="text/javascript">
37077  */
37078 /*
37079  * These classes are private internal classes
37080  */
37081 Roo.bootstrap.layout.Center = function(config){
37082     config.region = "center";
37083     Roo.bootstrap.layout.Region.call(this, config);
37084     this.visible = true;
37085     this.minWidth = config.minWidth || 20;
37086     this.minHeight = config.minHeight || 20;
37087 };
37088
37089 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37090     hide : function(){
37091         // center panel can't be hidden
37092     },
37093     
37094     show : function(){
37095         // center panel can't be hidden
37096     },
37097     
37098     getMinWidth: function(){
37099         return this.minWidth;
37100     },
37101     
37102     getMinHeight: function(){
37103         return this.minHeight;
37104     }
37105 });
37106
37107
37108
37109
37110  
37111
37112
37113
37114
37115
37116
37117 Roo.bootstrap.layout.North = function(config)
37118 {
37119     config.region = 'north';
37120     config.cursor = 'n-resize';
37121     
37122     Roo.bootstrap.layout.Split.call(this, config);
37123     
37124     
37125     if(this.split){
37126         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37127         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37128         this.split.el.addClass("roo-layout-split-v");
37129     }
37130     var size = config.initialSize || config.height;
37131     if(typeof size != "undefined"){
37132         this.el.setHeight(size);
37133     }
37134 };
37135 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37136 {
37137     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37138     
37139     
37140     
37141     getBox : function(){
37142         if(this.collapsed){
37143             return this.collapsedEl.getBox();
37144         }
37145         var box = this.el.getBox();
37146         if(this.split){
37147             box.height += this.split.el.getHeight();
37148         }
37149         return box;
37150     },
37151     
37152     updateBox : function(box){
37153         if(this.split && !this.collapsed){
37154             box.height -= this.split.el.getHeight();
37155             this.split.el.setLeft(box.x);
37156             this.split.el.setTop(box.y+box.height);
37157             this.split.el.setWidth(box.width);
37158         }
37159         if(this.collapsed){
37160             this.updateBody(box.width, null);
37161         }
37162         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37163     }
37164 });
37165
37166
37167
37168
37169
37170 Roo.bootstrap.layout.South = function(config){
37171     config.region = 'south';
37172     config.cursor = 's-resize';
37173     Roo.bootstrap.layout.Split.call(this, config);
37174     if(this.split){
37175         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37176         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37177         this.split.el.addClass("roo-layout-split-v");
37178     }
37179     var size = config.initialSize || config.height;
37180     if(typeof size != "undefined"){
37181         this.el.setHeight(size);
37182     }
37183 };
37184
37185 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37186     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37187     getBox : function(){
37188         if(this.collapsed){
37189             return this.collapsedEl.getBox();
37190         }
37191         var box = this.el.getBox();
37192         if(this.split){
37193             var sh = this.split.el.getHeight();
37194             box.height += sh;
37195             box.y -= sh;
37196         }
37197         return box;
37198     },
37199     
37200     updateBox : function(box){
37201         if(this.split && !this.collapsed){
37202             var sh = this.split.el.getHeight();
37203             box.height -= sh;
37204             box.y += sh;
37205             this.split.el.setLeft(box.x);
37206             this.split.el.setTop(box.y-sh);
37207             this.split.el.setWidth(box.width);
37208         }
37209         if(this.collapsed){
37210             this.updateBody(box.width, null);
37211         }
37212         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37213     }
37214 });
37215
37216 Roo.bootstrap.layout.East = function(config){
37217     config.region = "east";
37218     config.cursor = "e-resize";
37219     Roo.bootstrap.layout.Split.call(this, config);
37220     if(this.split){
37221         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37222         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37223         this.split.el.addClass("roo-layout-split-h");
37224     }
37225     var size = config.initialSize || config.width;
37226     if(typeof size != "undefined"){
37227         this.el.setWidth(size);
37228     }
37229 };
37230 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37231     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37232     getBox : function(){
37233         if(this.collapsed){
37234             return this.collapsedEl.getBox();
37235         }
37236         var box = this.el.getBox();
37237         if(this.split){
37238             var sw = this.split.el.getWidth();
37239             box.width += sw;
37240             box.x -= sw;
37241         }
37242         return box;
37243     },
37244
37245     updateBox : function(box){
37246         if(this.split && !this.collapsed){
37247             var sw = this.split.el.getWidth();
37248             box.width -= sw;
37249             this.split.el.setLeft(box.x);
37250             this.split.el.setTop(box.y);
37251             this.split.el.setHeight(box.height);
37252             box.x += sw;
37253         }
37254         if(this.collapsed){
37255             this.updateBody(null, box.height);
37256         }
37257         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37258     }
37259 });
37260
37261 Roo.bootstrap.layout.West = function(config){
37262     config.region = "west";
37263     config.cursor = "w-resize";
37264     
37265     Roo.bootstrap.layout.Split.call(this, config);
37266     if(this.split){
37267         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37268         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37269         this.split.el.addClass("roo-layout-split-h");
37270     }
37271     
37272 };
37273 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37274     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37275     
37276     onRender: function(ctr, pos)
37277     {
37278         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37279         var size = this.config.initialSize || this.config.width;
37280         if(typeof size != "undefined"){
37281             this.el.setWidth(size);
37282         }
37283     },
37284     
37285     getBox : function(){
37286         if(this.collapsed){
37287             return this.collapsedEl.getBox();
37288         }
37289         var box = this.el.getBox();
37290         if(this.split){
37291             box.width += this.split.el.getWidth();
37292         }
37293         return box;
37294     },
37295     
37296     updateBox : function(box){
37297         if(this.split && !this.collapsed){
37298             var sw = this.split.el.getWidth();
37299             box.width -= sw;
37300             this.split.el.setLeft(box.x+box.width);
37301             this.split.el.setTop(box.y);
37302             this.split.el.setHeight(box.height);
37303         }
37304         if(this.collapsed){
37305             this.updateBody(null, box.height);
37306         }
37307         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37308     }
37309 });Roo.namespace("Roo.bootstrap.panel");/*
37310  * Based on:
37311  * Ext JS Library 1.1.1
37312  * Copyright(c) 2006-2007, Ext JS, LLC.
37313  *
37314  * Originally Released Under LGPL - original licence link has changed is not relivant.
37315  *
37316  * Fork - LGPL
37317  * <script type="text/javascript">
37318  */
37319 /**
37320  * @class Roo.ContentPanel
37321  * @extends Roo.util.Observable
37322  * A basic ContentPanel element.
37323  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37324  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37325  * @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
37326  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37327  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37328  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37329  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37330  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37331  * @cfg {String} title          The title for this panel
37332  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37333  * @cfg {String} url            Calls {@link #setUrl} with this value
37334  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37335  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37336  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37337  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37338  * @cfg {Boolean} badges render the badges
37339
37340  * @constructor
37341  * Create a new ContentPanel.
37342  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37343  * @param {String/Object} config A string to set only the title or a config object
37344  * @param {String} content (optional) Set the HTML content for this panel
37345  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37346  */
37347 Roo.bootstrap.panel.Content = function( config){
37348     
37349     this.tpl = config.tpl || false;
37350     
37351     var el = config.el;
37352     var content = config.content;
37353
37354     if(config.autoCreate){ // xtype is available if this is called from factory
37355         el = Roo.id();
37356     }
37357     this.el = Roo.get(el);
37358     if(!this.el && config && config.autoCreate){
37359         if(typeof config.autoCreate == "object"){
37360             if(!config.autoCreate.id){
37361                 config.autoCreate.id = config.id||el;
37362             }
37363             this.el = Roo.DomHelper.append(document.body,
37364                         config.autoCreate, true);
37365         }else{
37366             var elcfg =  {   tag: "div",
37367                             cls: "roo-layout-inactive-content",
37368                             id: config.id||el
37369                             };
37370             if (config.html) {
37371                 elcfg.html = config.html;
37372                 
37373             }
37374                         
37375             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37376         }
37377     } 
37378     this.closable = false;
37379     this.loaded = false;
37380     this.active = false;
37381    
37382       
37383     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37384         
37385         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37386         
37387         this.wrapEl = this.el; //this.el.wrap();
37388         var ti = [];
37389         if (config.toolbar.items) {
37390             ti = config.toolbar.items ;
37391             delete config.toolbar.items ;
37392         }
37393         
37394         var nitems = [];
37395         this.toolbar.render(this.wrapEl, 'before');
37396         for(var i =0;i < ti.length;i++) {
37397           //  Roo.log(['add child', items[i]]);
37398             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37399         }
37400         this.toolbar.items = nitems;
37401         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37402         delete config.toolbar;
37403         
37404     }
37405     /*
37406     // xtype created footer. - not sure if will work as we normally have to render first..
37407     if (this.footer && !this.footer.el && this.footer.xtype) {
37408         if (!this.wrapEl) {
37409             this.wrapEl = this.el.wrap();
37410         }
37411     
37412         this.footer.container = this.wrapEl.createChild();
37413          
37414         this.footer = Roo.factory(this.footer, Roo);
37415         
37416     }
37417     */
37418     
37419      if(typeof config == "string"){
37420         this.title = config;
37421     }else{
37422         Roo.apply(this, config);
37423     }
37424     
37425     if(this.resizeEl){
37426         this.resizeEl = Roo.get(this.resizeEl, true);
37427     }else{
37428         this.resizeEl = this.el;
37429     }
37430     // handle view.xtype
37431     
37432  
37433     
37434     
37435     this.addEvents({
37436         /**
37437          * @event activate
37438          * Fires when this panel is activated. 
37439          * @param {Roo.ContentPanel} this
37440          */
37441         "activate" : true,
37442         /**
37443          * @event deactivate
37444          * Fires when this panel is activated. 
37445          * @param {Roo.ContentPanel} this
37446          */
37447         "deactivate" : true,
37448
37449         /**
37450          * @event resize
37451          * Fires when this panel is resized if fitToFrame is true.
37452          * @param {Roo.ContentPanel} this
37453          * @param {Number} width The width after any component adjustments
37454          * @param {Number} height The height after any component adjustments
37455          */
37456         "resize" : true,
37457         
37458          /**
37459          * @event render
37460          * Fires when this tab is created
37461          * @param {Roo.ContentPanel} this
37462          */
37463         "render" : true
37464         
37465         
37466         
37467     });
37468     
37469
37470     
37471     
37472     if(this.autoScroll){
37473         this.resizeEl.setStyle("overflow", "auto");
37474     } else {
37475         // fix randome scrolling
37476         //this.el.on('scroll', function() {
37477         //    Roo.log('fix random scolling');
37478         //    this.scrollTo('top',0); 
37479         //});
37480     }
37481     content = content || this.content;
37482     if(content){
37483         this.setContent(content);
37484     }
37485     if(config && config.url){
37486         this.setUrl(this.url, this.params, this.loadOnce);
37487     }
37488     
37489     
37490     
37491     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37492     
37493     if (this.view && typeof(this.view.xtype) != 'undefined') {
37494         this.view.el = this.el.appendChild(document.createElement("div"));
37495         this.view = Roo.factory(this.view); 
37496         this.view.render  &&  this.view.render(false, '');  
37497     }
37498     
37499     
37500     this.fireEvent('render', this);
37501 };
37502
37503 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37504     
37505     tabTip : '',
37506     
37507     setRegion : function(region){
37508         this.region = region;
37509         this.setActiveClass(region && !this.background);
37510     },
37511     
37512     
37513     setActiveClass: function(state)
37514     {
37515         if(state){
37516            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37517            this.el.setStyle('position','relative');
37518         }else{
37519            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37520            this.el.setStyle('position', 'absolute');
37521         } 
37522     },
37523     
37524     /**
37525      * Returns the toolbar for this Panel if one was configured. 
37526      * @return {Roo.Toolbar} 
37527      */
37528     getToolbar : function(){
37529         return this.toolbar;
37530     },
37531     
37532     setActiveState : function(active)
37533     {
37534         this.active = active;
37535         this.setActiveClass(active);
37536         if(!active){
37537             if(this.fireEvent("deactivate", this) === false){
37538                 return false;
37539             }
37540             return true;
37541         }
37542         this.fireEvent("activate", this);
37543         return true;
37544     },
37545     /**
37546      * Updates this panel's element
37547      * @param {String} content The new content
37548      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37549     */
37550     setContent : function(content, loadScripts){
37551         this.el.update(content, loadScripts);
37552     },
37553
37554     ignoreResize : function(w, h){
37555         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37556             return true;
37557         }else{
37558             this.lastSize = {width: w, height: h};
37559             return false;
37560         }
37561     },
37562     /**
37563      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37564      * @return {Roo.UpdateManager} The UpdateManager
37565      */
37566     getUpdateManager : function(){
37567         return this.el.getUpdateManager();
37568     },
37569      /**
37570      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37571      * @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:
37572 <pre><code>
37573 panel.load({
37574     url: "your-url.php",
37575     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37576     callback: yourFunction,
37577     scope: yourObject, //(optional scope)
37578     discardUrl: false,
37579     nocache: false,
37580     text: "Loading...",
37581     timeout: 30,
37582     scripts: false
37583 });
37584 </code></pre>
37585      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37586      * 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.
37587      * @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}
37588      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37589      * @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.
37590      * @return {Roo.ContentPanel} this
37591      */
37592     load : function(){
37593         var um = this.el.getUpdateManager();
37594         um.update.apply(um, arguments);
37595         return this;
37596     },
37597
37598
37599     /**
37600      * 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.
37601      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37602      * @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)
37603      * @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)
37604      * @return {Roo.UpdateManager} The UpdateManager
37605      */
37606     setUrl : function(url, params, loadOnce){
37607         if(this.refreshDelegate){
37608             this.removeListener("activate", this.refreshDelegate);
37609         }
37610         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37611         this.on("activate", this.refreshDelegate);
37612         return this.el.getUpdateManager();
37613     },
37614     
37615     _handleRefresh : function(url, params, loadOnce){
37616         if(!loadOnce || !this.loaded){
37617             var updater = this.el.getUpdateManager();
37618             updater.update(url, params, this._setLoaded.createDelegate(this));
37619         }
37620     },
37621     
37622     _setLoaded : function(){
37623         this.loaded = true;
37624     }, 
37625     
37626     /**
37627      * Returns this panel's id
37628      * @return {String} 
37629      */
37630     getId : function(){
37631         return this.el.id;
37632     },
37633     
37634     /** 
37635      * Returns this panel's element - used by regiosn to add.
37636      * @return {Roo.Element} 
37637      */
37638     getEl : function(){
37639         return this.wrapEl || this.el;
37640     },
37641     
37642    
37643     
37644     adjustForComponents : function(width, height)
37645     {
37646         //Roo.log('adjustForComponents ');
37647         if(this.resizeEl != this.el){
37648             width -= this.el.getFrameWidth('lr');
37649             height -= this.el.getFrameWidth('tb');
37650         }
37651         if(this.toolbar){
37652             var te = this.toolbar.getEl();
37653             te.setWidth(width);
37654             height -= te.getHeight();
37655         }
37656         if(this.footer){
37657             var te = this.footer.getEl();
37658             te.setWidth(width);
37659             height -= te.getHeight();
37660         }
37661         
37662         
37663         if(this.adjustments){
37664             width += this.adjustments[0];
37665             height += this.adjustments[1];
37666         }
37667         return {"width": width, "height": height};
37668     },
37669     
37670     setSize : function(width, height){
37671         if(this.fitToFrame && !this.ignoreResize(width, height)){
37672             if(this.fitContainer && this.resizeEl != this.el){
37673                 this.el.setSize(width, height);
37674             }
37675             var size = this.adjustForComponents(width, height);
37676             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37677             this.fireEvent('resize', this, size.width, size.height);
37678         }
37679     },
37680     
37681     /**
37682      * Returns this panel's title
37683      * @return {String} 
37684      */
37685     getTitle : function(){
37686         
37687         if (typeof(this.title) != 'object') {
37688             return this.title;
37689         }
37690         
37691         var t = '';
37692         for (var k in this.title) {
37693             if (!this.title.hasOwnProperty(k)) {
37694                 continue;
37695             }
37696             
37697             if (k.indexOf('-') >= 0) {
37698                 var s = k.split('-');
37699                 for (var i = 0; i<s.length; i++) {
37700                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37701                 }
37702             } else {
37703                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37704             }
37705         }
37706         return t;
37707     },
37708     
37709     /**
37710      * Set this panel's title
37711      * @param {String} title
37712      */
37713     setTitle : function(title){
37714         this.title = title;
37715         if(this.region){
37716             this.region.updatePanelTitle(this, title);
37717         }
37718     },
37719     
37720     /**
37721      * Returns true is this panel was configured to be closable
37722      * @return {Boolean} 
37723      */
37724     isClosable : function(){
37725         return this.closable;
37726     },
37727     
37728     beforeSlide : function(){
37729         this.el.clip();
37730         this.resizeEl.clip();
37731     },
37732     
37733     afterSlide : function(){
37734         this.el.unclip();
37735         this.resizeEl.unclip();
37736     },
37737     
37738     /**
37739      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37740      *   Will fail silently if the {@link #setUrl} method has not been called.
37741      *   This does not activate the panel, just updates its content.
37742      */
37743     refresh : function(){
37744         if(this.refreshDelegate){
37745            this.loaded = false;
37746            this.refreshDelegate();
37747         }
37748     },
37749     
37750     /**
37751      * Destroys this panel
37752      */
37753     destroy : function(){
37754         this.el.removeAllListeners();
37755         var tempEl = document.createElement("span");
37756         tempEl.appendChild(this.el.dom);
37757         tempEl.innerHTML = "";
37758         this.el.remove();
37759         this.el = null;
37760     },
37761     
37762     /**
37763      * form - if the content panel contains a form - this is a reference to it.
37764      * @type {Roo.form.Form}
37765      */
37766     form : false,
37767     /**
37768      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37769      *    This contains a reference to it.
37770      * @type {Roo.View}
37771      */
37772     view : false,
37773     
37774       /**
37775      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37776      * <pre><code>
37777
37778 layout.addxtype({
37779        xtype : 'Form',
37780        items: [ .... ]
37781    }
37782 );
37783
37784 </code></pre>
37785      * @param {Object} cfg Xtype definition of item to add.
37786      */
37787     
37788     
37789     getChildContainer: function () {
37790         return this.getEl();
37791     }
37792     
37793     
37794     /*
37795         var  ret = new Roo.factory(cfg);
37796         return ret;
37797         
37798         
37799         // add form..
37800         if (cfg.xtype.match(/^Form$/)) {
37801             
37802             var el;
37803             //if (this.footer) {
37804             //    el = this.footer.container.insertSibling(false, 'before');
37805             //} else {
37806                 el = this.el.createChild();
37807             //}
37808
37809             this.form = new  Roo.form.Form(cfg);
37810             
37811             
37812             if ( this.form.allItems.length) {
37813                 this.form.render(el.dom);
37814             }
37815             return this.form;
37816         }
37817         // should only have one of theses..
37818         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37819             // views.. should not be just added - used named prop 'view''
37820             
37821             cfg.el = this.el.appendChild(document.createElement("div"));
37822             // factory?
37823             
37824             var ret = new Roo.factory(cfg);
37825              
37826              ret.render && ret.render(false, ''); // render blank..
37827             this.view = ret;
37828             return ret;
37829         }
37830         return false;
37831     }
37832     \*/
37833 });
37834  
37835 /**
37836  * @class Roo.bootstrap.panel.Grid
37837  * @extends Roo.bootstrap.panel.Content
37838  * @constructor
37839  * Create a new GridPanel.
37840  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37841  * @param {Object} config A the config object
37842   
37843  */
37844
37845
37846
37847 Roo.bootstrap.panel.Grid = function(config)
37848 {
37849     
37850       
37851     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37852         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37853
37854     config.el = this.wrapper;
37855     //this.el = this.wrapper;
37856     
37857       if (config.container) {
37858         // ctor'ed from a Border/panel.grid
37859         
37860         
37861         this.wrapper.setStyle("overflow", "hidden");
37862         this.wrapper.addClass('roo-grid-container');
37863
37864     }
37865     
37866     
37867     if(config.toolbar){
37868         var tool_el = this.wrapper.createChild();    
37869         this.toolbar = Roo.factory(config.toolbar);
37870         var ti = [];
37871         if (config.toolbar.items) {
37872             ti = config.toolbar.items ;
37873             delete config.toolbar.items ;
37874         }
37875         
37876         var nitems = [];
37877         this.toolbar.render(tool_el);
37878         for(var i =0;i < ti.length;i++) {
37879           //  Roo.log(['add child', items[i]]);
37880             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37881         }
37882         this.toolbar.items = nitems;
37883         
37884         delete config.toolbar;
37885     }
37886     
37887     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37888     config.grid.scrollBody = true;;
37889     config.grid.monitorWindowResize = false; // turn off autosizing
37890     config.grid.autoHeight = false;
37891     config.grid.autoWidth = false;
37892     
37893     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37894     
37895     if (config.background) {
37896         // render grid on panel activation (if panel background)
37897         this.on('activate', function(gp) {
37898             if (!gp.grid.rendered) {
37899                 gp.grid.render(this.wrapper);
37900                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37901             }
37902         });
37903             
37904     } else {
37905         this.grid.render(this.wrapper);
37906         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37907
37908     }
37909     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37910     // ??? needed ??? config.el = this.wrapper;
37911     
37912     
37913     
37914   
37915     // xtype created footer. - not sure if will work as we normally have to render first..
37916     if (this.footer && !this.footer.el && this.footer.xtype) {
37917         
37918         var ctr = this.grid.getView().getFooterPanel(true);
37919         this.footer.dataSource = this.grid.dataSource;
37920         this.footer = Roo.factory(this.footer, Roo);
37921         this.footer.render(ctr);
37922         
37923     }
37924     
37925     
37926     
37927     
37928      
37929 };
37930
37931 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37932     getId : function(){
37933         return this.grid.id;
37934     },
37935     
37936     /**
37937      * Returns the grid for this panel
37938      * @return {Roo.bootstrap.Table} 
37939      */
37940     getGrid : function(){
37941         return this.grid;    
37942     },
37943     
37944     setSize : function(width, height){
37945         if(!this.ignoreResize(width, height)){
37946             var grid = this.grid;
37947             var size = this.adjustForComponents(width, height);
37948             var gridel = grid.getGridEl();
37949             gridel.setSize(size.width, size.height);
37950             /*
37951             var thd = grid.getGridEl().select('thead',true).first();
37952             var tbd = grid.getGridEl().select('tbody', true).first();
37953             if (tbd) {
37954                 tbd.setSize(width, height - thd.getHeight());
37955             }
37956             */
37957             grid.autoSize();
37958         }
37959     },
37960      
37961     
37962     
37963     beforeSlide : function(){
37964         this.grid.getView().scroller.clip();
37965     },
37966     
37967     afterSlide : function(){
37968         this.grid.getView().scroller.unclip();
37969     },
37970     
37971     destroy : function(){
37972         this.grid.destroy();
37973         delete this.grid;
37974         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37975     }
37976 });
37977
37978 /**
37979  * @class Roo.bootstrap.panel.Nest
37980  * @extends Roo.bootstrap.panel.Content
37981  * @constructor
37982  * Create a new Panel, that can contain a layout.Border.
37983  * 
37984  * 
37985  * @param {Roo.BorderLayout} layout The layout for this panel
37986  * @param {String/Object} config A string to set only the title or a config object
37987  */
37988 Roo.bootstrap.panel.Nest = function(config)
37989 {
37990     // construct with only one argument..
37991     /* FIXME - implement nicer consturctors
37992     if (layout.layout) {
37993         config = layout;
37994         layout = config.layout;
37995         delete config.layout;
37996     }
37997     if (layout.xtype && !layout.getEl) {
37998         // then layout needs constructing..
37999         layout = Roo.factory(layout, Roo);
38000     }
38001     */
38002     
38003     config.el =  config.layout.getEl();
38004     
38005     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38006     
38007     config.layout.monitorWindowResize = false; // turn off autosizing
38008     this.layout = config.layout;
38009     this.layout.getEl().addClass("roo-layout-nested-layout");
38010     this.layout.parent = this;
38011     
38012     
38013     
38014     
38015 };
38016
38017 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38018
38019     setSize : function(width, height){
38020         if(!this.ignoreResize(width, height)){
38021             var size = this.adjustForComponents(width, height);
38022             var el = this.layout.getEl();
38023             if (size.height < 1) {
38024                 el.setWidth(size.width);   
38025             } else {
38026                 el.setSize(size.width, size.height);
38027             }
38028             var touch = el.dom.offsetWidth;
38029             this.layout.layout();
38030             // ie requires a double layout on the first pass
38031             if(Roo.isIE && !this.initialized){
38032                 this.initialized = true;
38033                 this.layout.layout();
38034             }
38035         }
38036     },
38037     
38038     // activate all subpanels if not currently active..
38039     
38040     setActiveState : function(active){
38041         this.active = active;
38042         this.setActiveClass(active);
38043         
38044         if(!active){
38045             this.fireEvent("deactivate", this);
38046             return;
38047         }
38048         
38049         this.fireEvent("activate", this);
38050         // not sure if this should happen before or after..
38051         if (!this.layout) {
38052             return; // should not happen..
38053         }
38054         var reg = false;
38055         for (var r in this.layout.regions) {
38056             reg = this.layout.getRegion(r);
38057             if (reg.getActivePanel()) {
38058                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38059                 reg.setActivePanel(reg.getActivePanel());
38060                 continue;
38061             }
38062             if (!reg.panels.length) {
38063                 continue;
38064             }
38065             reg.showPanel(reg.getPanel(0));
38066         }
38067         
38068         
38069         
38070         
38071     },
38072     
38073     /**
38074      * Returns the nested BorderLayout for this panel
38075      * @return {Roo.BorderLayout} 
38076      */
38077     getLayout : function(){
38078         return this.layout;
38079     },
38080     
38081      /**
38082      * Adds a xtype elements to the layout of the nested panel
38083      * <pre><code>
38084
38085 panel.addxtype({
38086        xtype : 'ContentPanel',
38087        region: 'west',
38088        items: [ .... ]
38089    }
38090 );
38091
38092 panel.addxtype({
38093         xtype : 'NestedLayoutPanel',
38094         region: 'west',
38095         layout: {
38096            center: { },
38097            west: { }   
38098         },
38099         items : [ ... list of content panels or nested layout panels.. ]
38100    }
38101 );
38102 </code></pre>
38103      * @param {Object} cfg Xtype definition of item to add.
38104      */
38105     addxtype : function(cfg) {
38106         return this.layout.addxtype(cfg);
38107     
38108     }
38109 });/*
38110  * Based on:
38111  * Ext JS Library 1.1.1
38112  * Copyright(c) 2006-2007, Ext JS, LLC.
38113  *
38114  * Originally Released Under LGPL - original licence link has changed is not relivant.
38115  *
38116  * Fork - LGPL
38117  * <script type="text/javascript">
38118  */
38119 /**
38120  * @class Roo.TabPanel
38121  * @extends Roo.util.Observable
38122  * A lightweight tab container.
38123  * <br><br>
38124  * Usage:
38125  * <pre><code>
38126 // basic tabs 1, built from existing content
38127 var tabs = new Roo.TabPanel("tabs1");
38128 tabs.addTab("script", "View Script");
38129 tabs.addTab("markup", "View Markup");
38130 tabs.activate("script");
38131
38132 // more advanced tabs, built from javascript
38133 var jtabs = new Roo.TabPanel("jtabs");
38134 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38135
38136 // set up the UpdateManager
38137 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38138 var updater = tab2.getUpdateManager();
38139 updater.setDefaultUrl("ajax1.htm");
38140 tab2.on('activate', updater.refresh, updater, true);
38141
38142 // Use setUrl for Ajax loading
38143 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38144 tab3.setUrl("ajax2.htm", null, true);
38145
38146 // Disabled tab
38147 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38148 tab4.disable();
38149
38150 jtabs.activate("jtabs-1");
38151  * </code></pre>
38152  * @constructor
38153  * Create a new TabPanel.
38154  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38155  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38156  */
38157 Roo.bootstrap.panel.Tabs = function(config){
38158     /**
38159     * The container element for this TabPanel.
38160     * @type Roo.Element
38161     */
38162     this.el = Roo.get(config.el);
38163     delete config.el;
38164     if(config){
38165         if(typeof config == "boolean"){
38166             this.tabPosition = config ? "bottom" : "top";
38167         }else{
38168             Roo.apply(this, config);
38169         }
38170     }
38171     
38172     if(this.tabPosition == "bottom"){
38173         // if tabs are at the bottom = create the body first.
38174         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38175         this.el.addClass("roo-tabs-bottom");
38176     }
38177     // next create the tabs holders
38178     
38179     if (this.tabPosition == "west"){
38180         
38181         var reg = this.region; // fake it..
38182         while (reg) {
38183             if (!reg.mgr.parent) {
38184                 break;
38185             }
38186             reg = reg.mgr.parent.region;
38187         }
38188         Roo.log("got nest?");
38189         Roo.log(reg);
38190         if (reg.mgr.getRegion('west')) {
38191             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38192             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38193             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38194             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38195             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38196         
38197             
38198         }
38199         
38200         
38201     } else {
38202      
38203         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38204         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38205         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38206         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38207     }
38208     
38209     
38210     if(Roo.isIE){
38211         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38212     }
38213     
38214     // finally - if tabs are at the top, then create the body last..
38215     if(this.tabPosition != "bottom"){
38216         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38217          * @type Roo.Element
38218          */
38219         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38220         this.el.addClass("roo-tabs-top");
38221     }
38222     this.items = [];
38223
38224     this.bodyEl.setStyle("position", "relative");
38225
38226     this.active = null;
38227     this.activateDelegate = this.activate.createDelegate(this);
38228
38229     this.addEvents({
38230         /**
38231          * @event tabchange
38232          * Fires when the active tab changes
38233          * @param {Roo.TabPanel} this
38234          * @param {Roo.TabPanelItem} activePanel The new active tab
38235          */
38236         "tabchange": true,
38237         /**
38238          * @event beforetabchange
38239          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38240          * @param {Roo.TabPanel} this
38241          * @param {Object} e Set cancel to true on this object to cancel the tab change
38242          * @param {Roo.TabPanelItem} tab The tab being changed to
38243          */
38244         "beforetabchange" : true
38245     });
38246
38247     Roo.EventManager.onWindowResize(this.onResize, this);
38248     this.cpad = this.el.getPadding("lr");
38249     this.hiddenCount = 0;
38250
38251
38252     // toolbar on the tabbar support...
38253     if (this.toolbar) {
38254         alert("no toolbar support yet");
38255         this.toolbar  = false;
38256         /*
38257         var tcfg = this.toolbar;
38258         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38259         this.toolbar = new Roo.Toolbar(tcfg);
38260         if (Roo.isSafari) {
38261             var tbl = tcfg.container.child('table', true);
38262             tbl.setAttribute('width', '100%');
38263         }
38264         */
38265         
38266     }
38267    
38268
38269
38270     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38271 };
38272
38273 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38274     /*
38275      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38276      */
38277     tabPosition : "top",
38278     /*
38279      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38280      */
38281     currentTabWidth : 0,
38282     /*
38283      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38284      */
38285     minTabWidth : 40,
38286     /*
38287      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38288      */
38289     maxTabWidth : 250,
38290     /*
38291      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38292      */
38293     preferredTabWidth : 175,
38294     /*
38295      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38296      */
38297     resizeTabs : false,
38298     /*
38299      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38300      */
38301     monitorResize : true,
38302     /*
38303      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38304      */
38305     toolbar : false,  // set by caller..
38306     
38307     region : false, /// set by caller
38308     
38309     disableTooltips : true, // not used yet...
38310
38311     /**
38312      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38313      * @param {String} id The id of the div to use <b>or create</b>
38314      * @param {String} text The text for the tab
38315      * @param {String} content (optional) Content to put in the TabPanelItem body
38316      * @param {Boolean} closable (optional) True to create a close icon on the tab
38317      * @return {Roo.TabPanelItem} The created TabPanelItem
38318      */
38319     addTab : function(id, text, content, closable, tpl)
38320     {
38321         var item = new Roo.bootstrap.panel.TabItem({
38322             panel: this,
38323             id : id,
38324             text : text,
38325             closable : closable,
38326             tpl : tpl
38327         });
38328         this.addTabItem(item);
38329         if(content){
38330             item.setContent(content);
38331         }
38332         return item;
38333     },
38334
38335     /**
38336      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38337      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38338      * @return {Roo.TabPanelItem}
38339      */
38340     getTab : function(id){
38341         return this.items[id];
38342     },
38343
38344     /**
38345      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38346      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38347      */
38348     hideTab : function(id){
38349         var t = this.items[id];
38350         if(!t.isHidden()){
38351            t.setHidden(true);
38352            this.hiddenCount++;
38353            this.autoSizeTabs();
38354         }
38355     },
38356
38357     /**
38358      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38359      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38360      */
38361     unhideTab : function(id){
38362         var t = this.items[id];
38363         if(t.isHidden()){
38364            t.setHidden(false);
38365            this.hiddenCount--;
38366            this.autoSizeTabs();
38367         }
38368     },
38369
38370     /**
38371      * Adds an existing {@link Roo.TabPanelItem}.
38372      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38373      */
38374     addTabItem : function(item)
38375     {
38376         this.items[item.id] = item;
38377         this.items.push(item);
38378         this.autoSizeTabs();
38379       //  if(this.resizeTabs){
38380     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38381   //         this.autoSizeTabs();
38382 //        }else{
38383 //            item.autoSize();
38384        // }
38385     },
38386
38387     /**
38388      * Removes a {@link Roo.TabPanelItem}.
38389      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38390      */
38391     removeTab : function(id){
38392         var items = this.items;
38393         var tab = items[id];
38394         if(!tab) { return; }
38395         var index = items.indexOf(tab);
38396         if(this.active == tab && items.length > 1){
38397             var newTab = this.getNextAvailable(index);
38398             if(newTab) {
38399                 newTab.activate();
38400             }
38401         }
38402         this.stripEl.dom.removeChild(tab.pnode.dom);
38403         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38404             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38405         }
38406         items.splice(index, 1);
38407         delete this.items[tab.id];
38408         tab.fireEvent("close", tab);
38409         tab.purgeListeners();
38410         this.autoSizeTabs();
38411     },
38412
38413     getNextAvailable : function(start){
38414         var items = this.items;
38415         var index = start;
38416         // look for a next tab that will slide over to
38417         // replace the one being removed
38418         while(index < items.length){
38419             var item = items[++index];
38420             if(item && !item.isHidden()){
38421                 return item;
38422             }
38423         }
38424         // if one isn't found select the previous tab (on the left)
38425         index = start;
38426         while(index >= 0){
38427             var item = items[--index];
38428             if(item && !item.isHidden()){
38429                 return item;
38430             }
38431         }
38432         return null;
38433     },
38434
38435     /**
38436      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38437      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38438      */
38439     disableTab : function(id){
38440         var tab = this.items[id];
38441         if(tab && this.active != tab){
38442             tab.disable();
38443         }
38444     },
38445
38446     /**
38447      * Enables a {@link Roo.TabPanelItem} that is disabled.
38448      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38449      */
38450     enableTab : function(id){
38451         var tab = this.items[id];
38452         tab.enable();
38453     },
38454
38455     /**
38456      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38457      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38458      * @return {Roo.TabPanelItem} The TabPanelItem.
38459      */
38460     activate : function(id)
38461     {
38462         //Roo.log('activite:'  + id);
38463         
38464         var tab = this.items[id];
38465         if(!tab){
38466             return null;
38467         }
38468         if(tab == this.active || tab.disabled){
38469             return tab;
38470         }
38471         var e = {};
38472         this.fireEvent("beforetabchange", this, e, tab);
38473         if(e.cancel !== true && !tab.disabled){
38474             if(this.active){
38475                 this.active.hide();
38476             }
38477             this.active = this.items[id];
38478             this.active.show();
38479             this.fireEvent("tabchange", this, this.active);
38480         }
38481         return tab;
38482     },
38483
38484     /**
38485      * Gets the active {@link Roo.TabPanelItem}.
38486      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38487      */
38488     getActiveTab : function(){
38489         return this.active;
38490     },
38491
38492     /**
38493      * Updates the tab body element to fit the height of the container element
38494      * for overflow scrolling
38495      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38496      */
38497     syncHeight : function(targetHeight){
38498         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38499         var bm = this.bodyEl.getMargins();
38500         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38501         this.bodyEl.setHeight(newHeight);
38502         return newHeight;
38503     },
38504
38505     onResize : function(){
38506         if(this.monitorResize){
38507             this.autoSizeTabs();
38508         }
38509     },
38510
38511     /**
38512      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38513      */
38514     beginUpdate : function(){
38515         this.updating = true;
38516     },
38517
38518     /**
38519      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38520      */
38521     endUpdate : function(){
38522         this.updating = false;
38523         this.autoSizeTabs();
38524     },
38525
38526     /**
38527      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38528      */
38529     autoSizeTabs : function()
38530     {
38531         var count = this.items.length;
38532         var vcount = count - this.hiddenCount;
38533         
38534         if (vcount < 2) {
38535             this.stripEl.hide();
38536         } else {
38537             this.stripEl.show();
38538         }
38539         
38540         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38541             return;
38542         }
38543         
38544         
38545         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38546         var availWidth = Math.floor(w / vcount);
38547         var b = this.stripBody;
38548         if(b.getWidth() > w){
38549             var tabs = this.items;
38550             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38551             if(availWidth < this.minTabWidth){
38552                 /*if(!this.sleft){    // incomplete scrolling code
38553                     this.createScrollButtons();
38554                 }
38555                 this.showScroll();
38556                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38557             }
38558         }else{
38559             if(this.currentTabWidth < this.preferredTabWidth){
38560                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38561             }
38562         }
38563     },
38564
38565     /**
38566      * Returns the number of tabs in this TabPanel.
38567      * @return {Number}
38568      */
38569      getCount : function(){
38570          return this.items.length;
38571      },
38572
38573     /**
38574      * Resizes all the tabs to the passed width
38575      * @param {Number} The new width
38576      */
38577     setTabWidth : function(width){
38578         this.currentTabWidth = width;
38579         for(var i = 0, len = this.items.length; i < len; i++) {
38580                 if(!this.items[i].isHidden()) {
38581                 this.items[i].setWidth(width);
38582             }
38583         }
38584     },
38585
38586     /**
38587      * Destroys this TabPanel
38588      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38589      */
38590     destroy : function(removeEl){
38591         Roo.EventManager.removeResizeListener(this.onResize, this);
38592         for(var i = 0, len = this.items.length; i < len; i++){
38593             this.items[i].purgeListeners();
38594         }
38595         if(removeEl === true){
38596             this.el.update("");
38597             this.el.remove();
38598         }
38599     },
38600     
38601     createStrip : function(container)
38602     {
38603         var strip = document.createElement("nav");
38604         strip.className = Roo.bootstrap.version == 4 ?
38605             "navbar-light bg-light" : 
38606             "navbar navbar-default"; //"x-tabs-wrap";
38607         container.appendChild(strip);
38608         return strip;
38609     },
38610     
38611     createStripList : function(strip)
38612     {
38613         // div wrapper for retard IE
38614         // returns the "tr" element.
38615         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38616         //'<div class="x-tabs-strip-wrap">'+
38617           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38618           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38619         return strip.firstChild; //.firstChild.firstChild.firstChild;
38620     },
38621     createBody : function(container)
38622     {
38623         var body = document.createElement("div");
38624         Roo.id(body, "tab-body");
38625         //Roo.fly(body).addClass("x-tabs-body");
38626         Roo.fly(body).addClass("tab-content");
38627         container.appendChild(body);
38628         return body;
38629     },
38630     createItemBody :function(bodyEl, id){
38631         var body = Roo.getDom(id);
38632         if(!body){
38633             body = document.createElement("div");
38634             body.id = id;
38635         }
38636         //Roo.fly(body).addClass("x-tabs-item-body");
38637         Roo.fly(body).addClass("tab-pane");
38638          bodyEl.insertBefore(body, bodyEl.firstChild);
38639         return body;
38640     },
38641     /** @private */
38642     createStripElements :  function(stripEl, text, closable, tpl)
38643     {
38644         var td = document.createElement("li"); // was td..
38645         td.className = 'nav-item';
38646         
38647         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38648         
38649         
38650         stripEl.appendChild(td);
38651         /*if(closable){
38652             td.className = "x-tabs-closable";
38653             if(!this.closeTpl){
38654                 this.closeTpl = new Roo.Template(
38655                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38656                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38657                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38658                 );
38659             }
38660             var el = this.closeTpl.overwrite(td, {"text": text});
38661             var close = el.getElementsByTagName("div")[0];
38662             var inner = el.getElementsByTagName("em")[0];
38663             return {"el": el, "close": close, "inner": inner};
38664         } else {
38665         */
38666         // not sure what this is..
38667 //            if(!this.tabTpl){
38668                 //this.tabTpl = new Roo.Template(
38669                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38670                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38671                 //);
38672 //                this.tabTpl = new Roo.Template(
38673 //                   '<a href="#">' +
38674 //                   '<span unselectable="on"' +
38675 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38676 //                            ' >{text}</span></a>'
38677 //                );
38678 //                
38679 //            }
38680
38681
38682             var template = tpl || this.tabTpl || false;
38683             
38684             if(!template){
38685                 template =  new Roo.Template(
38686                         Roo.bootstrap.version == 4 ? 
38687                             (
38688                                 '<a class="nav-link" href="#" unselectable="on"' +
38689                                      (this.disableTooltips ? '' : ' title="{text}"') +
38690                                      ' >{text}</a>'
38691                             ) : (
38692                                 '<a class="nav-link" href="#">' +
38693                                 '<span unselectable="on"' +
38694                                          (this.disableTooltips ? '' : ' title="{text}"') +
38695                                     ' >{text}</span></a>'
38696                             )
38697                 );
38698             }
38699             
38700             switch (typeof(template)) {
38701                 case 'object' :
38702                     break;
38703                 case 'string' :
38704                     template = new Roo.Template(template);
38705                     break;
38706                 default :
38707                     break;
38708             }
38709             
38710             var el = template.overwrite(td, {"text": text});
38711             
38712             var inner = el.getElementsByTagName("span")[0];
38713             
38714             return {"el": el, "inner": inner};
38715             
38716     }
38717         
38718     
38719 });
38720
38721 /**
38722  * @class Roo.TabPanelItem
38723  * @extends Roo.util.Observable
38724  * Represents an individual item (tab plus body) in a TabPanel.
38725  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38726  * @param {String} id The id of this TabPanelItem
38727  * @param {String} text The text for the tab of this TabPanelItem
38728  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38729  */
38730 Roo.bootstrap.panel.TabItem = function(config){
38731     /**
38732      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38733      * @type Roo.TabPanel
38734      */
38735     this.tabPanel = config.panel;
38736     /**
38737      * The id for this TabPanelItem
38738      * @type String
38739      */
38740     this.id = config.id;
38741     /** @private */
38742     this.disabled = false;
38743     /** @private */
38744     this.text = config.text;
38745     /** @private */
38746     this.loaded = false;
38747     this.closable = config.closable;
38748
38749     /**
38750      * The body element for this TabPanelItem.
38751      * @type Roo.Element
38752      */
38753     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38754     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38755     this.bodyEl.setStyle("display", "block");
38756     this.bodyEl.setStyle("zoom", "1");
38757     //this.hideAction();
38758
38759     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38760     /** @private */
38761     this.el = Roo.get(els.el);
38762     this.inner = Roo.get(els.inner, true);
38763      this.textEl = Roo.bootstrap.version == 4 ?
38764         this.el : Roo.get(this.el.dom.firstChild, true);
38765
38766     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38767     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38768
38769     
38770 //    this.el.on("mousedown", this.onTabMouseDown, this);
38771     this.el.on("click", this.onTabClick, this);
38772     /** @private */
38773     if(config.closable){
38774         var c = Roo.get(els.close, true);
38775         c.dom.title = this.closeText;
38776         c.addClassOnOver("close-over");
38777         c.on("click", this.closeClick, this);
38778      }
38779
38780     this.addEvents({
38781          /**
38782          * @event activate
38783          * Fires when this tab becomes the active tab.
38784          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38785          * @param {Roo.TabPanelItem} this
38786          */
38787         "activate": true,
38788         /**
38789          * @event beforeclose
38790          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38791          * @param {Roo.TabPanelItem} this
38792          * @param {Object} e Set cancel to true on this object to cancel the close.
38793          */
38794         "beforeclose": true,
38795         /**
38796          * @event close
38797          * Fires when this tab is closed.
38798          * @param {Roo.TabPanelItem} this
38799          */
38800          "close": true,
38801         /**
38802          * @event deactivate
38803          * Fires when this tab is no longer the active tab.
38804          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38805          * @param {Roo.TabPanelItem} this
38806          */
38807          "deactivate" : true
38808     });
38809     this.hidden = false;
38810
38811     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38812 };
38813
38814 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38815            {
38816     purgeListeners : function(){
38817        Roo.util.Observable.prototype.purgeListeners.call(this);
38818        this.el.removeAllListeners();
38819     },
38820     /**
38821      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38822      */
38823     show : function(){
38824         this.status_node.addClass("active");
38825         this.showAction();
38826         if(Roo.isOpera){
38827             this.tabPanel.stripWrap.repaint();
38828         }
38829         this.fireEvent("activate", this.tabPanel, this);
38830     },
38831
38832     /**
38833      * Returns true if this tab is the active tab.
38834      * @return {Boolean}
38835      */
38836     isActive : function(){
38837         return this.tabPanel.getActiveTab() == this;
38838     },
38839
38840     /**
38841      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38842      */
38843     hide : function(){
38844         this.status_node.removeClass("active");
38845         this.hideAction();
38846         this.fireEvent("deactivate", this.tabPanel, this);
38847     },
38848
38849     hideAction : function(){
38850         this.bodyEl.hide();
38851         this.bodyEl.setStyle("position", "absolute");
38852         this.bodyEl.setLeft("-20000px");
38853         this.bodyEl.setTop("-20000px");
38854     },
38855
38856     showAction : function(){
38857         this.bodyEl.setStyle("position", "relative");
38858         this.bodyEl.setTop("");
38859         this.bodyEl.setLeft("");
38860         this.bodyEl.show();
38861     },
38862
38863     /**
38864      * Set the tooltip for the tab.
38865      * @param {String} tooltip The tab's tooltip
38866      */
38867     setTooltip : function(text){
38868         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38869             this.textEl.dom.qtip = text;
38870             this.textEl.dom.removeAttribute('title');
38871         }else{
38872             this.textEl.dom.title = text;
38873         }
38874     },
38875
38876     onTabClick : function(e){
38877         e.preventDefault();
38878         this.tabPanel.activate(this.id);
38879     },
38880
38881     onTabMouseDown : function(e){
38882         e.preventDefault();
38883         this.tabPanel.activate(this.id);
38884     },
38885 /*
38886     getWidth : function(){
38887         return this.inner.getWidth();
38888     },
38889
38890     setWidth : function(width){
38891         var iwidth = width - this.linode.getPadding("lr");
38892         this.inner.setWidth(iwidth);
38893         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38894         this.linode.setWidth(width);
38895     },
38896 */
38897     /**
38898      * Show or hide the tab
38899      * @param {Boolean} hidden True to hide or false to show.
38900      */
38901     setHidden : function(hidden){
38902         this.hidden = hidden;
38903         this.linode.setStyle("display", hidden ? "none" : "");
38904     },
38905
38906     /**
38907      * Returns true if this tab is "hidden"
38908      * @return {Boolean}
38909      */
38910     isHidden : function(){
38911         return this.hidden;
38912     },
38913
38914     /**
38915      * Returns the text for this tab
38916      * @return {String}
38917      */
38918     getText : function(){
38919         return this.text;
38920     },
38921     /*
38922     autoSize : function(){
38923         //this.el.beginMeasure();
38924         this.textEl.setWidth(1);
38925         /*
38926          *  #2804 [new] Tabs in Roojs
38927          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38928          */
38929         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38930         //this.el.endMeasure();
38931     //},
38932
38933     /**
38934      * Sets the text for the tab (Note: this also sets the tooltip text)
38935      * @param {String} text The tab's text and tooltip
38936      */
38937     setText : function(text){
38938         this.text = text;
38939         this.textEl.update(text);
38940         this.setTooltip(text);
38941         //if(!this.tabPanel.resizeTabs){
38942         //    this.autoSize();
38943         //}
38944     },
38945     /**
38946      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38947      */
38948     activate : function(){
38949         this.tabPanel.activate(this.id);
38950     },
38951
38952     /**
38953      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38954      */
38955     disable : function(){
38956         if(this.tabPanel.active != this){
38957             this.disabled = true;
38958             this.status_node.addClass("disabled");
38959         }
38960     },
38961
38962     /**
38963      * Enables this TabPanelItem if it was previously disabled.
38964      */
38965     enable : function(){
38966         this.disabled = false;
38967         this.status_node.removeClass("disabled");
38968     },
38969
38970     /**
38971      * Sets the content for this TabPanelItem.
38972      * @param {String} content The content
38973      * @param {Boolean} loadScripts true to look for and load scripts
38974      */
38975     setContent : function(content, loadScripts){
38976         this.bodyEl.update(content, loadScripts);
38977     },
38978
38979     /**
38980      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38981      * @return {Roo.UpdateManager} The UpdateManager
38982      */
38983     getUpdateManager : function(){
38984         return this.bodyEl.getUpdateManager();
38985     },
38986
38987     /**
38988      * Set a URL to be used to load the content for this TabPanelItem.
38989      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38990      * @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)
38991      * @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)
38992      * @return {Roo.UpdateManager} The UpdateManager
38993      */
38994     setUrl : function(url, params, loadOnce){
38995         if(this.refreshDelegate){
38996             this.un('activate', this.refreshDelegate);
38997         }
38998         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38999         this.on("activate", this.refreshDelegate);
39000         return this.bodyEl.getUpdateManager();
39001     },
39002
39003     /** @private */
39004     _handleRefresh : function(url, params, loadOnce){
39005         if(!loadOnce || !this.loaded){
39006             var updater = this.bodyEl.getUpdateManager();
39007             updater.update(url, params, this._setLoaded.createDelegate(this));
39008         }
39009     },
39010
39011     /**
39012      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39013      *   Will fail silently if the setUrl method has not been called.
39014      *   This does not activate the panel, just updates its content.
39015      */
39016     refresh : function(){
39017         if(this.refreshDelegate){
39018            this.loaded = false;
39019            this.refreshDelegate();
39020         }
39021     },
39022
39023     /** @private */
39024     _setLoaded : function(){
39025         this.loaded = true;
39026     },
39027
39028     /** @private */
39029     closeClick : function(e){
39030         var o = {};
39031         e.stopEvent();
39032         this.fireEvent("beforeclose", this, o);
39033         if(o.cancel !== true){
39034             this.tabPanel.removeTab(this.id);
39035         }
39036     },
39037     /**
39038      * The text displayed in the tooltip for the close icon.
39039      * @type String
39040      */
39041     closeText : "Close this tab"
39042 });
39043 /**
39044 *    This script refer to:
39045 *    Title: International Telephone Input
39046 *    Author: Jack O'Connor
39047 *    Code version:  v12.1.12
39048 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39049 **/
39050
39051 Roo.bootstrap.PhoneInputData = function() {
39052     var d = [
39053       [
39054         "Afghanistan (‫افغانستان‬‎)",
39055         "af",
39056         "93"
39057       ],
39058       [
39059         "Albania (Shqipëri)",
39060         "al",
39061         "355"
39062       ],
39063       [
39064         "Algeria (‫الجزائر‬‎)",
39065         "dz",
39066         "213"
39067       ],
39068       [
39069         "American Samoa",
39070         "as",
39071         "1684"
39072       ],
39073       [
39074         "Andorra",
39075         "ad",
39076         "376"
39077       ],
39078       [
39079         "Angola",
39080         "ao",
39081         "244"
39082       ],
39083       [
39084         "Anguilla",
39085         "ai",
39086         "1264"
39087       ],
39088       [
39089         "Antigua and Barbuda",
39090         "ag",
39091         "1268"
39092       ],
39093       [
39094         "Argentina",
39095         "ar",
39096         "54"
39097       ],
39098       [
39099         "Armenia (Հայաստան)",
39100         "am",
39101         "374"
39102       ],
39103       [
39104         "Aruba",
39105         "aw",
39106         "297"
39107       ],
39108       [
39109         "Australia",
39110         "au",
39111         "61",
39112         0
39113       ],
39114       [
39115         "Austria (Österreich)",
39116         "at",
39117         "43"
39118       ],
39119       [
39120         "Azerbaijan (Azərbaycan)",
39121         "az",
39122         "994"
39123       ],
39124       [
39125         "Bahamas",
39126         "bs",
39127         "1242"
39128       ],
39129       [
39130         "Bahrain (‫البحرين‬‎)",
39131         "bh",
39132         "973"
39133       ],
39134       [
39135         "Bangladesh (বাংলাদেশ)",
39136         "bd",
39137         "880"
39138       ],
39139       [
39140         "Barbados",
39141         "bb",
39142         "1246"
39143       ],
39144       [
39145         "Belarus (Беларусь)",
39146         "by",
39147         "375"
39148       ],
39149       [
39150         "Belgium (België)",
39151         "be",
39152         "32"
39153       ],
39154       [
39155         "Belize",
39156         "bz",
39157         "501"
39158       ],
39159       [
39160         "Benin (Bénin)",
39161         "bj",
39162         "229"
39163       ],
39164       [
39165         "Bermuda",
39166         "bm",
39167         "1441"
39168       ],
39169       [
39170         "Bhutan (འབྲུག)",
39171         "bt",
39172         "975"
39173       ],
39174       [
39175         "Bolivia",
39176         "bo",
39177         "591"
39178       ],
39179       [
39180         "Bosnia and Herzegovina (Босна и Херцеговина)",
39181         "ba",
39182         "387"
39183       ],
39184       [
39185         "Botswana",
39186         "bw",
39187         "267"
39188       ],
39189       [
39190         "Brazil (Brasil)",
39191         "br",
39192         "55"
39193       ],
39194       [
39195         "British Indian Ocean Territory",
39196         "io",
39197         "246"
39198       ],
39199       [
39200         "British Virgin Islands",
39201         "vg",
39202         "1284"
39203       ],
39204       [
39205         "Brunei",
39206         "bn",
39207         "673"
39208       ],
39209       [
39210         "Bulgaria (България)",
39211         "bg",
39212         "359"
39213       ],
39214       [
39215         "Burkina Faso",
39216         "bf",
39217         "226"
39218       ],
39219       [
39220         "Burundi (Uburundi)",
39221         "bi",
39222         "257"
39223       ],
39224       [
39225         "Cambodia (កម្ពុជា)",
39226         "kh",
39227         "855"
39228       ],
39229       [
39230         "Cameroon (Cameroun)",
39231         "cm",
39232         "237"
39233       ],
39234       [
39235         "Canada",
39236         "ca",
39237         "1",
39238         1,
39239         ["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"]
39240       ],
39241       [
39242         "Cape Verde (Kabu Verdi)",
39243         "cv",
39244         "238"
39245       ],
39246       [
39247         "Caribbean Netherlands",
39248         "bq",
39249         "599",
39250         1
39251       ],
39252       [
39253         "Cayman Islands",
39254         "ky",
39255         "1345"
39256       ],
39257       [
39258         "Central African Republic (République centrafricaine)",
39259         "cf",
39260         "236"
39261       ],
39262       [
39263         "Chad (Tchad)",
39264         "td",
39265         "235"
39266       ],
39267       [
39268         "Chile",
39269         "cl",
39270         "56"
39271       ],
39272       [
39273         "China (中国)",
39274         "cn",
39275         "86"
39276       ],
39277       [
39278         "Christmas Island",
39279         "cx",
39280         "61",
39281         2
39282       ],
39283       [
39284         "Cocos (Keeling) Islands",
39285         "cc",
39286         "61",
39287         1
39288       ],
39289       [
39290         "Colombia",
39291         "co",
39292         "57"
39293       ],
39294       [
39295         "Comoros (‫جزر القمر‬‎)",
39296         "km",
39297         "269"
39298       ],
39299       [
39300         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39301         "cd",
39302         "243"
39303       ],
39304       [
39305         "Congo (Republic) (Congo-Brazzaville)",
39306         "cg",
39307         "242"
39308       ],
39309       [
39310         "Cook Islands",
39311         "ck",
39312         "682"
39313       ],
39314       [
39315         "Costa Rica",
39316         "cr",
39317         "506"
39318       ],
39319       [
39320         "Côte d’Ivoire",
39321         "ci",
39322         "225"
39323       ],
39324       [
39325         "Croatia (Hrvatska)",
39326         "hr",
39327         "385"
39328       ],
39329       [
39330         "Cuba",
39331         "cu",
39332         "53"
39333       ],
39334       [
39335         "Curaçao",
39336         "cw",
39337         "599",
39338         0
39339       ],
39340       [
39341         "Cyprus (Κύπρος)",
39342         "cy",
39343         "357"
39344       ],
39345       [
39346         "Czech Republic (Česká republika)",
39347         "cz",
39348         "420"
39349       ],
39350       [
39351         "Denmark (Danmark)",
39352         "dk",
39353         "45"
39354       ],
39355       [
39356         "Djibouti",
39357         "dj",
39358         "253"
39359       ],
39360       [
39361         "Dominica",
39362         "dm",
39363         "1767"
39364       ],
39365       [
39366         "Dominican Republic (República Dominicana)",
39367         "do",
39368         "1",
39369         2,
39370         ["809", "829", "849"]
39371       ],
39372       [
39373         "Ecuador",
39374         "ec",
39375         "593"
39376       ],
39377       [
39378         "Egypt (‫مصر‬‎)",
39379         "eg",
39380         "20"
39381       ],
39382       [
39383         "El Salvador",
39384         "sv",
39385         "503"
39386       ],
39387       [
39388         "Equatorial Guinea (Guinea Ecuatorial)",
39389         "gq",
39390         "240"
39391       ],
39392       [
39393         "Eritrea",
39394         "er",
39395         "291"
39396       ],
39397       [
39398         "Estonia (Eesti)",
39399         "ee",
39400         "372"
39401       ],
39402       [
39403         "Ethiopia",
39404         "et",
39405         "251"
39406       ],
39407       [
39408         "Falkland Islands (Islas Malvinas)",
39409         "fk",
39410         "500"
39411       ],
39412       [
39413         "Faroe Islands (Føroyar)",
39414         "fo",
39415         "298"
39416       ],
39417       [
39418         "Fiji",
39419         "fj",
39420         "679"
39421       ],
39422       [
39423         "Finland (Suomi)",
39424         "fi",
39425         "358",
39426         0
39427       ],
39428       [
39429         "France",
39430         "fr",
39431         "33"
39432       ],
39433       [
39434         "French Guiana (Guyane française)",
39435         "gf",
39436         "594"
39437       ],
39438       [
39439         "French Polynesia (Polynésie française)",
39440         "pf",
39441         "689"
39442       ],
39443       [
39444         "Gabon",
39445         "ga",
39446         "241"
39447       ],
39448       [
39449         "Gambia",
39450         "gm",
39451         "220"
39452       ],
39453       [
39454         "Georgia (საქართველო)",
39455         "ge",
39456         "995"
39457       ],
39458       [
39459         "Germany (Deutschland)",
39460         "de",
39461         "49"
39462       ],
39463       [
39464         "Ghana (Gaana)",
39465         "gh",
39466         "233"
39467       ],
39468       [
39469         "Gibraltar",
39470         "gi",
39471         "350"
39472       ],
39473       [
39474         "Greece (Ελλάδα)",
39475         "gr",
39476         "30"
39477       ],
39478       [
39479         "Greenland (Kalaallit Nunaat)",
39480         "gl",
39481         "299"
39482       ],
39483       [
39484         "Grenada",
39485         "gd",
39486         "1473"
39487       ],
39488       [
39489         "Guadeloupe",
39490         "gp",
39491         "590",
39492         0
39493       ],
39494       [
39495         "Guam",
39496         "gu",
39497         "1671"
39498       ],
39499       [
39500         "Guatemala",
39501         "gt",
39502         "502"
39503       ],
39504       [
39505         "Guernsey",
39506         "gg",
39507         "44",
39508         1
39509       ],
39510       [
39511         "Guinea (Guinée)",
39512         "gn",
39513         "224"
39514       ],
39515       [
39516         "Guinea-Bissau (Guiné Bissau)",
39517         "gw",
39518         "245"
39519       ],
39520       [
39521         "Guyana",
39522         "gy",
39523         "592"
39524       ],
39525       [
39526         "Haiti",
39527         "ht",
39528         "509"
39529       ],
39530       [
39531         "Honduras",
39532         "hn",
39533         "504"
39534       ],
39535       [
39536         "Hong Kong (香港)",
39537         "hk",
39538         "852"
39539       ],
39540       [
39541         "Hungary (Magyarország)",
39542         "hu",
39543         "36"
39544       ],
39545       [
39546         "Iceland (Ísland)",
39547         "is",
39548         "354"
39549       ],
39550       [
39551         "India (भारत)",
39552         "in",
39553         "91"
39554       ],
39555       [
39556         "Indonesia",
39557         "id",
39558         "62"
39559       ],
39560       [
39561         "Iran (‫ایران‬‎)",
39562         "ir",
39563         "98"
39564       ],
39565       [
39566         "Iraq (‫العراق‬‎)",
39567         "iq",
39568         "964"
39569       ],
39570       [
39571         "Ireland",
39572         "ie",
39573         "353"
39574       ],
39575       [
39576         "Isle of Man",
39577         "im",
39578         "44",
39579         2
39580       ],
39581       [
39582         "Israel (‫ישראל‬‎)",
39583         "il",
39584         "972"
39585       ],
39586       [
39587         "Italy (Italia)",
39588         "it",
39589         "39",
39590         0
39591       ],
39592       [
39593         "Jamaica",
39594         "jm",
39595         "1876"
39596       ],
39597       [
39598         "Japan (日本)",
39599         "jp",
39600         "81"
39601       ],
39602       [
39603         "Jersey",
39604         "je",
39605         "44",
39606         3
39607       ],
39608       [
39609         "Jordan (‫الأردن‬‎)",
39610         "jo",
39611         "962"
39612       ],
39613       [
39614         "Kazakhstan (Казахстан)",
39615         "kz",
39616         "7",
39617         1
39618       ],
39619       [
39620         "Kenya",
39621         "ke",
39622         "254"
39623       ],
39624       [
39625         "Kiribati",
39626         "ki",
39627         "686"
39628       ],
39629       [
39630         "Kosovo",
39631         "xk",
39632         "383"
39633       ],
39634       [
39635         "Kuwait (‫الكويت‬‎)",
39636         "kw",
39637         "965"
39638       ],
39639       [
39640         "Kyrgyzstan (Кыргызстан)",
39641         "kg",
39642         "996"
39643       ],
39644       [
39645         "Laos (ລາວ)",
39646         "la",
39647         "856"
39648       ],
39649       [
39650         "Latvia (Latvija)",
39651         "lv",
39652         "371"
39653       ],
39654       [
39655         "Lebanon (‫لبنان‬‎)",
39656         "lb",
39657         "961"
39658       ],
39659       [
39660         "Lesotho",
39661         "ls",
39662         "266"
39663       ],
39664       [
39665         "Liberia",
39666         "lr",
39667         "231"
39668       ],
39669       [
39670         "Libya (‫ليبيا‬‎)",
39671         "ly",
39672         "218"
39673       ],
39674       [
39675         "Liechtenstein",
39676         "li",
39677         "423"
39678       ],
39679       [
39680         "Lithuania (Lietuva)",
39681         "lt",
39682         "370"
39683       ],
39684       [
39685         "Luxembourg",
39686         "lu",
39687         "352"
39688       ],
39689       [
39690         "Macau (澳門)",
39691         "mo",
39692         "853"
39693       ],
39694       [
39695         "Macedonia (FYROM) (Македонија)",
39696         "mk",
39697         "389"
39698       ],
39699       [
39700         "Madagascar (Madagasikara)",
39701         "mg",
39702         "261"
39703       ],
39704       [
39705         "Malawi",
39706         "mw",
39707         "265"
39708       ],
39709       [
39710         "Malaysia",
39711         "my",
39712         "60"
39713       ],
39714       [
39715         "Maldives",
39716         "mv",
39717         "960"
39718       ],
39719       [
39720         "Mali",
39721         "ml",
39722         "223"
39723       ],
39724       [
39725         "Malta",
39726         "mt",
39727         "356"
39728       ],
39729       [
39730         "Marshall Islands",
39731         "mh",
39732         "692"
39733       ],
39734       [
39735         "Martinique",
39736         "mq",
39737         "596"
39738       ],
39739       [
39740         "Mauritania (‫موريتانيا‬‎)",
39741         "mr",
39742         "222"
39743       ],
39744       [
39745         "Mauritius (Moris)",
39746         "mu",
39747         "230"
39748       ],
39749       [
39750         "Mayotte",
39751         "yt",
39752         "262",
39753         1
39754       ],
39755       [
39756         "Mexico (México)",
39757         "mx",
39758         "52"
39759       ],
39760       [
39761         "Micronesia",
39762         "fm",
39763         "691"
39764       ],
39765       [
39766         "Moldova (Republica Moldova)",
39767         "md",
39768         "373"
39769       ],
39770       [
39771         "Monaco",
39772         "mc",
39773         "377"
39774       ],
39775       [
39776         "Mongolia (Монгол)",
39777         "mn",
39778         "976"
39779       ],
39780       [
39781         "Montenegro (Crna Gora)",
39782         "me",
39783         "382"
39784       ],
39785       [
39786         "Montserrat",
39787         "ms",
39788         "1664"
39789       ],
39790       [
39791         "Morocco (‫المغرب‬‎)",
39792         "ma",
39793         "212",
39794         0
39795       ],
39796       [
39797         "Mozambique (Moçambique)",
39798         "mz",
39799         "258"
39800       ],
39801       [
39802         "Myanmar (Burma) (မြန်မာ)",
39803         "mm",
39804         "95"
39805       ],
39806       [
39807         "Namibia (Namibië)",
39808         "na",
39809         "264"
39810       ],
39811       [
39812         "Nauru",
39813         "nr",
39814         "674"
39815       ],
39816       [
39817         "Nepal (नेपाल)",
39818         "np",
39819         "977"
39820       ],
39821       [
39822         "Netherlands (Nederland)",
39823         "nl",
39824         "31"
39825       ],
39826       [
39827         "New Caledonia (Nouvelle-Calédonie)",
39828         "nc",
39829         "687"
39830       ],
39831       [
39832         "New Zealand",
39833         "nz",
39834         "64"
39835       ],
39836       [
39837         "Nicaragua",
39838         "ni",
39839         "505"
39840       ],
39841       [
39842         "Niger (Nijar)",
39843         "ne",
39844         "227"
39845       ],
39846       [
39847         "Nigeria",
39848         "ng",
39849         "234"
39850       ],
39851       [
39852         "Niue",
39853         "nu",
39854         "683"
39855       ],
39856       [
39857         "Norfolk Island",
39858         "nf",
39859         "672"
39860       ],
39861       [
39862         "North Korea (조선 민주주의 인민 공화국)",
39863         "kp",
39864         "850"
39865       ],
39866       [
39867         "Northern Mariana Islands",
39868         "mp",
39869         "1670"
39870       ],
39871       [
39872         "Norway (Norge)",
39873         "no",
39874         "47",
39875         0
39876       ],
39877       [
39878         "Oman (‫عُمان‬‎)",
39879         "om",
39880         "968"
39881       ],
39882       [
39883         "Pakistan (‫پاکستان‬‎)",
39884         "pk",
39885         "92"
39886       ],
39887       [
39888         "Palau",
39889         "pw",
39890         "680"
39891       ],
39892       [
39893         "Palestine (‫فلسطين‬‎)",
39894         "ps",
39895         "970"
39896       ],
39897       [
39898         "Panama (Panamá)",
39899         "pa",
39900         "507"
39901       ],
39902       [
39903         "Papua New Guinea",
39904         "pg",
39905         "675"
39906       ],
39907       [
39908         "Paraguay",
39909         "py",
39910         "595"
39911       ],
39912       [
39913         "Peru (Perú)",
39914         "pe",
39915         "51"
39916       ],
39917       [
39918         "Philippines",
39919         "ph",
39920         "63"
39921       ],
39922       [
39923         "Poland (Polska)",
39924         "pl",
39925         "48"
39926       ],
39927       [
39928         "Portugal",
39929         "pt",
39930         "351"
39931       ],
39932       [
39933         "Puerto Rico",
39934         "pr",
39935         "1",
39936         3,
39937         ["787", "939"]
39938       ],
39939       [
39940         "Qatar (‫قطر‬‎)",
39941         "qa",
39942         "974"
39943       ],
39944       [
39945         "Réunion (La Réunion)",
39946         "re",
39947         "262",
39948         0
39949       ],
39950       [
39951         "Romania (România)",
39952         "ro",
39953         "40"
39954       ],
39955       [
39956         "Russia (Россия)",
39957         "ru",
39958         "7",
39959         0
39960       ],
39961       [
39962         "Rwanda",
39963         "rw",
39964         "250"
39965       ],
39966       [
39967         "Saint Barthélemy",
39968         "bl",
39969         "590",
39970         1
39971       ],
39972       [
39973         "Saint Helena",
39974         "sh",
39975         "290"
39976       ],
39977       [
39978         "Saint Kitts and Nevis",
39979         "kn",
39980         "1869"
39981       ],
39982       [
39983         "Saint Lucia",
39984         "lc",
39985         "1758"
39986       ],
39987       [
39988         "Saint Martin (Saint-Martin (partie française))",
39989         "mf",
39990         "590",
39991         2
39992       ],
39993       [
39994         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39995         "pm",
39996         "508"
39997       ],
39998       [
39999         "Saint Vincent and the Grenadines",
40000         "vc",
40001         "1784"
40002       ],
40003       [
40004         "Samoa",
40005         "ws",
40006         "685"
40007       ],
40008       [
40009         "San Marino",
40010         "sm",
40011         "378"
40012       ],
40013       [
40014         "São Tomé and Príncipe (São Tomé e Príncipe)",
40015         "st",
40016         "239"
40017       ],
40018       [
40019         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40020         "sa",
40021         "966"
40022       ],
40023       [
40024         "Senegal (Sénégal)",
40025         "sn",
40026         "221"
40027       ],
40028       [
40029         "Serbia (Србија)",
40030         "rs",
40031         "381"
40032       ],
40033       [
40034         "Seychelles",
40035         "sc",
40036         "248"
40037       ],
40038       [
40039         "Sierra Leone",
40040         "sl",
40041         "232"
40042       ],
40043       [
40044         "Singapore",
40045         "sg",
40046         "65"
40047       ],
40048       [
40049         "Sint Maarten",
40050         "sx",
40051         "1721"
40052       ],
40053       [
40054         "Slovakia (Slovensko)",
40055         "sk",
40056         "421"
40057       ],
40058       [
40059         "Slovenia (Slovenija)",
40060         "si",
40061         "386"
40062       ],
40063       [
40064         "Solomon Islands",
40065         "sb",
40066         "677"
40067       ],
40068       [
40069         "Somalia (Soomaaliya)",
40070         "so",
40071         "252"
40072       ],
40073       [
40074         "South Africa",
40075         "za",
40076         "27"
40077       ],
40078       [
40079         "South Korea (대한민국)",
40080         "kr",
40081         "82"
40082       ],
40083       [
40084         "South Sudan (‫جنوب السودان‬‎)",
40085         "ss",
40086         "211"
40087       ],
40088       [
40089         "Spain (España)",
40090         "es",
40091         "34"
40092       ],
40093       [
40094         "Sri Lanka (ශ්‍රී ලංකාව)",
40095         "lk",
40096         "94"
40097       ],
40098       [
40099         "Sudan (‫السودان‬‎)",
40100         "sd",
40101         "249"
40102       ],
40103       [
40104         "Suriname",
40105         "sr",
40106         "597"
40107       ],
40108       [
40109         "Svalbard and Jan Mayen",
40110         "sj",
40111         "47",
40112         1
40113       ],
40114       [
40115         "Swaziland",
40116         "sz",
40117         "268"
40118       ],
40119       [
40120         "Sweden (Sverige)",
40121         "se",
40122         "46"
40123       ],
40124       [
40125         "Switzerland (Schweiz)",
40126         "ch",
40127         "41"
40128       ],
40129       [
40130         "Syria (‫سوريا‬‎)",
40131         "sy",
40132         "963"
40133       ],
40134       [
40135         "Taiwan (台灣)",
40136         "tw",
40137         "886"
40138       ],
40139       [
40140         "Tajikistan",
40141         "tj",
40142         "992"
40143       ],
40144       [
40145         "Tanzania",
40146         "tz",
40147         "255"
40148       ],
40149       [
40150         "Thailand (ไทย)",
40151         "th",
40152         "66"
40153       ],
40154       [
40155         "Timor-Leste",
40156         "tl",
40157         "670"
40158       ],
40159       [
40160         "Togo",
40161         "tg",
40162         "228"
40163       ],
40164       [
40165         "Tokelau",
40166         "tk",
40167         "690"
40168       ],
40169       [
40170         "Tonga",
40171         "to",
40172         "676"
40173       ],
40174       [
40175         "Trinidad and Tobago",
40176         "tt",
40177         "1868"
40178       ],
40179       [
40180         "Tunisia (‫تونس‬‎)",
40181         "tn",
40182         "216"
40183       ],
40184       [
40185         "Turkey (Türkiye)",
40186         "tr",
40187         "90"
40188       ],
40189       [
40190         "Turkmenistan",
40191         "tm",
40192         "993"
40193       ],
40194       [
40195         "Turks and Caicos Islands",
40196         "tc",
40197         "1649"
40198       ],
40199       [
40200         "Tuvalu",
40201         "tv",
40202         "688"
40203       ],
40204       [
40205         "U.S. Virgin Islands",
40206         "vi",
40207         "1340"
40208       ],
40209       [
40210         "Uganda",
40211         "ug",
40212         "256"
40213       ],
40214       [
40215         "Ukraine (Україна)",
40216         "ua",
40217         "380"
40218       ],
40219       [
40220         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40221         "ae",
40222         "971"
40223       ],
40224       [
40225         "United Kingdom",
40226         "gb",
40227         "44",
40228         0
40229       ],
40230       [
40231         "United States",
40232         "us",
40233         "1",
40234         0
40235       ],
40236       [
40237         "Uruguay",
40238         "uy",
40239         "598"
40240       ],
40241       [
40242         "Uzbekistan (Oʻzbekiston)",
40243         "uz",
40244         "998"
40245       ],
40246       [
40247         "Vanuatu",
40248         "vu",
40249         "678"
40250       ],
40251       [
40252         "Vatican City (Città del Vaticano)",
40253         "va",
40254         "39",
40255         1
40256       ],
40257       [
40258         "Venezuela",
40259         "ve",
40260         "58"
40261       ],
40262       [
40263         "Vietnam (Việt Nam)",
40264         "vn",
40265         "84"
40266       ],
40267       [
40268         "Wallis and Futuna (Wallis-et-Futuna)",
40269         "wf",
40270         "681"
40271       ],
40272       [
40273         "Western Sahara (‫الصحراء الغربية‬‎)",
40274         "eh",
40275         "212",
40276         1
40277       ],
40278       [
40279         "Yemen (‫اليمن‬‎)",
40280         "ye",
40281         "967"
40282       ],
40283       [
40284         "Zambia",
40285         "zm",
40286         "260"
40287       ],
40288       [
40289         "Zimbabwe",
40290         "zw",
40291         "263"
40292       ],
40293       [
40294         "Åland Islands",
40295         "ax",
40296         "358",
40297         1
40298       ]
40299   ];
40300   
40301   return d;
40302 }/**
40303 *    This script refer to:
40304 *    Title: International Telephone Input
40305 *    Author: Jack O'Connor
40306 *    Code version:  v12.1.12
40307 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40308 **/
40309
40310 /**
40311  * @class Roo.bootstrap.PhoneInput
40312  * @extends Roo.bootstrap.TriggerField
40313  * An input with International dial-code selection
40314  
40315  * @cfg {String} defaultDialCode default '+852'
40316  * @cfg {Array} preferedCountries default []
40317   
40318  * @constructor
40319  * Create a new PhoneInput.
40320  * @param {Object} config Configuration options
40321  */
40322
40323 Roo.bootstrap.PhoneInput = function(config) {
40324     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40325 };
40326
40327 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40328         
40329         listWidth: undefined,
40330         
40331         selectedClass: 'active',
40332         
40333         invalidClass : "has-warning",
40334         
40335         validClass: 'has-success',
40336         
40337         allowed: '0123456789',
40338         
40339         max_length: 15,
40340         
40341         /**
40342          * @cfg {String} defaultDialCode The default dial code when initializing the input
40343          */
40344         defaultDialCode: '+852',
40345         
40346         /**
40347          * @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
40348          */
40349         preferedCountries: false,
40350         
40351         getAutoCreate : function()
40352         {
40353             var data = Roo.bootstrap.PhoneInputData();
40354             var align = this.labelAlign || this.parentLabelAlign();
40355             var id = Roo.id();
40356             
40357             this.allCountries = [];
40358             this.dialCodeMapping = [];
40359             
40360             for (var i = 0; i < data.length; i++) {
40361               var c = data[i];
40362               this.allCountries[i] = {
40363                 name: c[0],
40364                 iso2: c[1],
40365                 dialCode: c[2],
40366                 priority: c[3] || 0,
40367                 areaCodes: c[4] || null
40368               };
40369               this.dialCodeMapping[c[2]] = {
40370                   name: c[0],
40371                   iso2: c[1],
40372                   priority: c[3] || 0,
40373                   areaCodes: c[4] || null
40374               };
40375             }
40376             
40377             var cfg = {
40378                 cls: 'form-group',
40379                 cn: []
40380             };
40381             
40382             var input =  {
40383                 tag: 'input',
40384                 id : id,
40385                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40386                 maxlength: this.max_length,
40387                 cls : 'form-control tel-input',
40388                 autocomplete: 'new-password'
40389             };
40390             
40391             var hiddenInput = {
40392                 tag: 'input',
40393                 type: 'hidden',
40394                 cls: 'hidden-tel-input'
40395             };
40396             
40397             if (this.name) {
40398                 hiddenInput.name = this.name;
40399             }
40400             
40401             if (this.disabled) {
40402                 input.disabled = true;
40403             }
40404             
40405             var flag_container = {
40406                 tag: 'div',
40407                 cls: 'flag-box',
40408                 cn: [
40409                     {
40410                         tag: 'div',
40411                         cls: 'flag'
40412                     },
40413                     {
40414                         tag: 'div',
40415                         cls: 'caret'
40416                     }
40417                 ]
40418             };
40419             
40420             var box = {
40421                 tag: 'div',
40422                 cls: this.hasFeedback ? 'has-feedback' : '',
40423                 cn: [
40424                     hiddenInput,
40425                     input,
40426                     {
40427                         tag: 'input',
40428                         cls: 'dial-code-holder',
40429                         disabled: true
40430                     }
40431                 ]
40432             };
40433             
40434             var container = {
40435                 cls: 'roo-select2-container input-group',
40436                 cn: [
40437                     flag_container,
40438                     box
40439                 ]
40440             };
40441             
40442             if (this.fieldLabel.length) {
40443                 var indicator = {
40444                     tag: 'i',
40445                     tooltip: 'This field is required'
40446                 };
40447                 
40448                 var label = {
40449                     tag: 'label',
40450                     'for':  id,
40451                     cls: 'control-label',
40452                     cn: []
40453                 };
40454                 
40455                 var label_text = {
40456                     tag: 'span',
40457                     html: this.fieldLabel
40458                 };
40459                 
40460                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40461                 label.cn = [
40462                     indicator,
40463                     label_text
40464                 ];
40465                 
40466                 if(this.indicatorpos == 'right') {
40467                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40468                     label.cn = [
40469                         label_text,
40470                         indicator
40471                     ];
40472                 }
40473                 
40474                 if(align == 'left') {
40475                     container = {
40476                         tag: 'div',
40477                         cn: [
40478                             container
40479                         ]
40480                     };
40481                     
40482                     if(this.labelWidth > 12){
40483                         label.style = "width: " + this.labelWidth + 'px';
40484                     }
40485                     if(this.labelWidth < 13 && this.labelmd == 0){
40486                         this.labelmd = this.labelWidth;
40487                     }
40488                     if(this.labellg > 0){
40489                         label.cls += ' col-lg-' + this.labellg;
40490                         input.cls += ' col-lg-' + (12 - this.labellg);
40491                     }
40492                     if(this.labelmd > 0){
40493                         label.cls += ' col-md-' + this.labelmd;
40494                         container.cls += ' col-md-' + (12 - this.labelmd);
40495                     }
40496                     if(this.labelsm > 0){
40497                         label.cls += ' col-sm-' + this.labelsm;
40498                         container.cls += ' col-sm-' + (12 - this.labelsm);
40499                     }
40500                     if(this.labelxs > 0){
40501                         label.cls += ' col-xs-' + this.labelxs;
40502                         container.cls += ' col-xs-' + (12 - this.labelxs);
40503                     }
40504                 }
40505             }
40506             
40507             cfg.cn = [
40508                 label,
40509                 container
40510             ];
40511             
40512             var settings = this;
40513             
40514             ['xs','sm','md','lg'].map(function(size){
40515                 if (settings[size]) {
40516                     cfg.cls += ' col-' + size + '-' + settings[size];
40517                 }
40518             });
40519             
40520             this.store = new Roo.data.Store({
40521                 proxy : new Roo.data.MemoryProxy({}),
40522                 reader : new Roo.data.JsonReader({
40523                     fields : [
40524                         {
40525                             'name' : 'name',
40526                             'type' : 'string'
40527                         },
40528                         {
40529                             'name' : 'iso2',
40530                             'type' : 'string'
40531                         },
40532                         {
40533                             'name' : 'dialCode',
40534                             'type' : 'string'
40535                         },
40536                         {
40537                             'name' : 'priority',
40538                             'type' : 'string'
40539                         },
40540                         {
40541                             'name' : 'areaCodes',
40542                             'type' : 'string'
40543                         }
40544                     ]
40545                 })
40546             });
40547             
40548             if(!this.preferedCountries) {
40549                 this.preferedCountries = [
40550                     'hk',
40551                     'gb',
40552                     'us'
40553                 ];
40554             }
40555             
40556             var p = this.preferedCountries.reverse();
40557             
40558             if(p) {
40559                 for (var i = 0; i < p.length; i++) {
40560                     for (var j = 0; j < this.allCountries.length; j++) {
40561                         if(this.allCountries[j].iso2 == p[i]) {
40562                             var t = this.allCountries[j];
40563                             this.allCountries.splice(j,1);
40564                             this.allCountries.unshift(t);
40565                         }
40566                     } 
40567                 }
40568             }
40569             
40570             this.store.proxy.data = {
40571                 success: true,
40572                 data: this.allCountries
40573             };
40574             
40575             return cfg;
40576         },
40577         
40578         initEvents : function()
40579         {
40580             this.createList();
40581             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40582             
40583             this.indicator = this.indicatorEl();
40584             this.flag = this.flagEl();
40585             this.dialCodeHolder = this.dialCodeHolderEl();
40586             
40587             this.trigger = this.el.select('div.flag-box',true).first();
40588             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40589             
40590             var _this = this;
40591             
40592             (function(){
40593                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40594                 _this.list.setWidth(lw);
40595             }).defer(100);
40596             
40597             this.list.on('mouseover', this.onViewOver, this);
40598             this.list.on('mousemove', this.onViewMove, this);
40599             this.inputEl().on("keyup", this.onKeyUp, this);
40600             this.inputEl().on("keypress", this.onKeyPress, this);
40601             
40602             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40603
40604             this.view = new Roo.View(this.list, this.tpl, {
40605                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40606             });
40607             
40608             this.view.on('click', this.onViewClick, this);
40609             this.setValue(this.defaultDialCode);
40610         },
40611         
40612         onTriggerClick : function(e)
40613         {
40614             Roo.log('trigger click');
40615             if(this.disabled){
40616                 return;
40617             }
40618             
40619             if(this.isExpanded()){
40620                 this.collapse();
40621                 this.hasFocus = false;
40622             }else {
40623                 this.store.load({});
40624                 this.hasFocus = true;
40625                 this.expand();
40626             }
40627         },
40628         
40629         isExpanded : function()
40630         {
40631             return this.list.isVisible();
40632         },
40633         
40634         collapse : function()
40635         {
40636             if(!this.isExpanded()){
40637                 return;
40638             }
40639             this.list.hide();
40640             Roo.get(document).un('mousedown', this.collapseIf, this);
40641             Roo.get(document).un('mousewheel', this.collapseIf, this);
40642             this.fireEvent('collapse', this);
40643             this.validate();
40644         },
40645         
40646         expand : function()
40647         {
40648             Roo.log('expand');
40649
40650             if(this.isExpanded() || !this.hasFocus){
40651                 return;
40652             }
40653             
40654             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40655             this.list.setWidth(lw);
40656             
40657             this.list.show();
40658             this.restrictHeight();
40659             
40660             Roo.get(document).on('mousedown', this.collapseIf, this);
40661             Roo.get(document).on('mousewheel', this.collapseIf, this);
40662             
40663             this.fireEvent('expand', this);
40664         },
40665         
40666         restrictHeight : function()
40667         {
40668             this.list.alignTo(this.inputEl(), this.listAlign);
40669             this.list.alignTo(this.inputEl(), this.listAlign);
40670         },
40671         
40672         onViewOver : function(e, t)
40673         {
40674             if(this.inKeyMode){
40675                 return;
40676             }
40677             var item = this.view.findItemFromChild(t);
40678             
40679             if(item){
40680                 var index = this.view.indexOf(item);
40681                 this.select(index, false);
40682             }
40683         },
40684
40685         // private
40686         onViewClick : function(view, doFocus, el, e)
40687         {
40688             var index = this.view.getSelectedIndexes()[0];
40689             
40690             var r = this.store.getAt(index);
40691             
40692             if(r){
40693                 this.onSelect(r, index);
40694             }
40695             if(doFocus !== false && !this.blockFocus){
40696                 this.inputEl().focus();
40697             }
40698         },
40699         
40700         onViewMove : function(e, t)
40701         {
40702             this.inKeyMode = false;
40703         },
40704         
40705         select : function(index, scrollIntoView)
40706         {
40707             this.selectedIndex = index;
40708             this.view.select(index);
40709             if(scrollIntoView !== false){
40710                 var el = this.view.getNode(index);
40711                 if(el){
40712                     this.list.scrollChildIntoView(el, false);
40713                 }
40714             }
40715         },
40716         
40717         createList : function()
40718         {
40719             this.list = Roo.get(document.body).createChild({
40720                 tag: 'ul',
40721                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40722                 style: 'display:none'
40723             });
40724             
40725             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40726         },
40727         
40728         collapseIf : function(e)
40729         {
40730             var in_combo  = e.within(this.el);
40731             var in_list =  e.within(this.list);
40732             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40733             
40734             if (in_combo || in_list || is_list) {
40735                 return;
40736             }
40737             this.collapse();
40738         },
40739         
40740         onSelect : function(record, index)
40741         {
40742             if(this.fireEvent('beforeselect', this, record, index) !== false){
40743                 
40744                 this.setFlagClass(record.data.iso2);
40745                 this.setDialCode(record.data.dialCode);
40746                 this.hasFocus = false;
40747                 this.collapse();
40748                 this.fireEvent('select', this, record, index);
40749             }
40750         },
40751         
40752         flagEl : function()
40753         {
40754             var flag = this.el.select('div.flag',true).first();
40755             if(!flag){
40756                 return false;
40757             }
40758             return flag;
40759         },
40760         
40761         dialCodeHolderEl : function()
40762         {
40763             var d = this.el.select('input.dial-code-holder',true).first();
40764             if(!d){
40765                 return false;
40766             }
40767             return d;
40768         },
40769         
40770         setDialCode : function(v)
40771         {
40772             this.dialCodeHolder.dom.value = '+'+v;
40773         },
40774         
40775         setFlagClass : function(n)
40776         {
40777             this.flag.dom.className = 'flag '+n;
40778         },
40779         
40780         getValue : function()
40781         {
40782             var v = this.inputEl().getValue();
40783             if(this.dialCodeHolder) {
40784                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40785             }
40786             return v;
40787         },
40788         
40789         setValue : function(v)
40790         {
40791             var d = this.getDialCode(v);
40792             
40793             //invalid dial code
40794             if(v.length == 0 || !d || d.length == 0) {
40795                 if(this.rendered){
40796                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40797                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40798                 }
40799                 return;
40800             }
40801             
40802             //valid dial code
40803             this.setFlagClass(this.dialCodeMapping[d].iso2);
40804             this.setDialCode(d);
40805             this.inputEl().dom.value = v.replace('+'+d,'');
40806             this.hiddenEl().dom.value = this.getValue();
40807             
40808             this.validate();
40809         },
40810         
40811         getDialCode : function(v)
40812         {
40813             v = v ||  '';
40814             
40815             if (v.length == 0) {
40816                 return this.dialCodeHolder.dom.value;
40817             }
40818             
40819             var dialCode = "";
40820             if (v.charAt(0) != "+") {
40821                 return false;
40822             }
40823             var numericChars = "";
40824             for (var i = 1; i < v.length; i++) {
40825               var c = v.charAt(i);
40826               if (!isNaN(c)) {
40827                 numericChars += c;
40828                 if (this.dialCodeMapping[numericChars]) {
40829                   dialCode = v.substr(1, i);
40830                 }
40831                 if (numericChars.length == 4) {
40832                   break;
40833                 }
40834               }
40835             }
40836             return dialCode;
40837         },
40838         
40839         reset : function()
40840         {
40841             this.setValue(this.defaultDialCode);
40842             this.validate();
40843         },
40844         
40845         hiddenEl : function()
40846         {
40847             return this.el.select('input.hidden-tel-input',true).first();
40848         },
40849         
40850         // after setting val
40851         onKeyUp : function(e){
40852             this.setValue(this.getValue());
40853         },
40854         
40855         onKeyPress : function(e){
40856             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40857                 e.stopEvent();
40858             }
40859         }
40860         
40861 });
40862 /**
40863  * @class Roo.bootstrap.MoneyField
40864  * @extends Roo.bootstrap.ComboBox
40865  * Bootstrap MoneyField class
40866  * 
40867  * @constructor
40868  * Create a new MoneyField.
40869  * @param {Object} config Configuration options
40870  */
40871
40872 Roo.bootstrap.MoneyField = function(config) {
40873     
40874     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40875     
40876 };
40877
40878 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40879     
40880     /**
40881      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40882      */
40883     allowDecimals : true,
40884     /**
40885      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40886      */
40887     decimalSeparator : ".",
40888     /**
40889      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40890      */
40891     decimalPrecision : 0,
40892     /**
40893      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40894      */
40895     allowNegative : true,
40896     /**
40897      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40898      */
40899     allowZero: true,
40900     /**
40901      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40902      */
40903     minValue : Number.NEGATIVE_INFINITY,
40904     /**
40905      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40906      */
40907     maxValue : Number.MAX_VALUE,
40908     /**
40909      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40910      */
40911     minText : "The minimum value for this field is {0}",
40912     /**
40913      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40914      */
40915     maxText : "The maximum value for this field is {0}",
40916     /**
40917      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40918      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40919      */
40920     nanText : "{0} is not a valid number",
40921     /**
40922      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40923      */
40924     castInt : true,
40925     /**
40926      * @cfg {String} defaults currency of the MoneyField
40927      * value should be in lkey
40928      */
40929     defaultCurrency : false,
40930     /**
40931      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40932      */
40933     thousandsDelimiter : false,
40934     /**
40935      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40936      */
40937     max_length: false,
40938     
40939     inputlg : 9,
40940     inputmd : 9,
40941     inputsm : 9,
40942     inputxs : 6,
40943     
40944     store : false,
40945     
40946     getAutoCreate : function()
40947     {
40948         var align = this.labelAlign || this.parentLabelAlign();
40949         
40950         var id = Roo.id();
40951
40952         var cfg = {
40953             cls: 'form-group',
40954             cn: []
40955         };
40956
40957         var input =  {
40958             tag: 'input',
40959             id : id,
40960             cls : 'form-control roo-money-amount-input',
40961             autocomplete: 'new-password'
40962         };
40963         
40964         var hiddenInput = {
40965             tag: 'input',
40966             type: 'hidden',
40967             id: Roo.id(),
40968             cls: 'hidden-number-input'
40969         };
40970         
40971         if(this.max_length) {
40972             input.maxlength = this.max_length; 
40973         }
40974         
40975         if (this.name) {
40976             hiddenInput.name = this.name;
40977         }
40978
40979         if (this.disabled) {
40980             input.disabled = true;
40981         }
40982
40983         var clg = 12 - this.inputlg;
40984         var cmd = 12 - this.inputmd;
40985         var csm = 12 - this.inputsm;
40986         var cxs = 12 - this.inputxs;
40987         
40988         var container = {
40989             tag : 'div',
40990             cls : 'row roo-money-field',
40991             cn : [
40992                 {
40993                     tag : 'div',
40994                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40995                     cn : [
40996                         {
40997                             tag : 'div',
40998                             cls: 'roo-select2-container input-group',
40999                             cn: [
41000                                 {
41001                                     tag : 'input',
41002                                     cls : 'form-control roo-money-currency-input',
41003                                     autocomplete: 'new-password',
41004                                     readOnly : 1,
41005                                     name : this.currencyName
41006                                 },
41007                                 {
41008                                     tag :'span',
41009                                     cls : 'input-group-addon',
41010                                     cn : [
41011                                         {
41012                                             tag: 'span',
41013                                             cls: 'caret'
41014                                         }
41015                                     ]
41016                                 }
41017                             ]
41018                         }
41019                     ]
41020                 },
41021                 {
41022                     tag : 'div',
41023                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41024                     cn : [
41025                         {
41026                             tag: 'div',
41027                             cls: this.hasFeedback ? 'has-feedback' : '',
41028                             cn: [
41029                                 input
41030                             ]
41031                         }
41032                     ]
41033                 }
41034             ]
41035             
41036         };
41037         
41038         if (this.fieldLabel.length) {
41039             var indicator = {
41040                 tag: 'i',
41041                 tooltip: 'This field is required'
41042             };
41043
41044             var label = {
41045                 tag: 'label',
41046                 'for':  id,
41047                 cls: 'control-label',
41048                 cn: []
41049             };
41050
41051             var label_text = {
41052                 tag: 'span',
41053                 html: this.fieldLabel
41054             };
41055
41056             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41057             label.cn = [
41058                 indicator,
41059                 label_text
41060             ];
41061
41062             if(this.indicatorpos == 'right') {
41063                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41064                 label.cn = [
41065                     label_text,
41066                     indicator
41067                 ];
41068             }
41069
41070             if(align == 'left') {
41071                 container = {
41072                     tag: 'div',
41073                     cn: [
41074                         container
41075                     ]
41076                 };
41077
41078                 if(this.labelWidth > 12){
41079                     label.style = "width: " + this.labelWidth + 'px';
41080                 }
41081                 if(this.labelWidth < 13 && this.labelmd == 0){
41082                     this.labelmd = this.labelWidth;
41083                 }
41084                 if(this.labellg > 0){
41085                     label.cls += ' col-lg-' + this.labellg;
41086                     input.cls += ' col-lg-' + (12 - this.labellg);
41087                 }
41088                 if(this.labelmd > 0){
41089                     label.cls += ' col-md-' + this.labelmd;
41090                     container.cls += ' col-md-' + (12 - this.labelmd);
41091                 }
41092                 if(this.labelsm > 0){
41093                     label.cls += ' col-sm-' + this.labelsm;
41094                     container.cls += ' col-sm-' + (12 - this.labelsm);
41095                 }
41096                 if(this.labelxs > 0){
41097                     label.cls += ' col-xs-' + this.labelxs;
41098                     container.cls += ' col-xs-' + (12 - this.labelxs);
41099                 }
41100             }
41101         }
41102
41103         cfg.cn = [
41104             label,
41105             container,
41106             hiddenInput
41107         ];
41108         
41109         var settings = this;
41110
41111         ['xs','sm','md','lg'].map(function(size){
41112             if (settings[size]) {
41113                 cfg.cls += ' col-' + size + '-' + settings[size];
41114             }
41115         });
41116         
41117         return cfg;
41118     },
41119     
41120     initEvents : function()
41121     {
41122         this.indicator = this.indicatorEl();
41123         
41124         this.initCurrencyEvent();
41125         
41126         this.initNumberEvent();
41127     },
41128     
41129     initCurrencyEvent : function()
41130     {
41131         if (!this.store) {
41132             throw "can not find store for combo";
41133         }
41134         
41135         this.store = Roo.factory(this.store, Roo.data);
41136         this.store.parent = this;
41137         
41138         this.createList();
41139         
41140         this.triggerEl = this.el.select('.input-group-addon', true).first();
41141         
41142         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41143         
41144         var _this = this;
41145         
41146         (function(){
41147             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41148             _this.list.setWidth(lw);
41149         }).defer(100);
41150         
41151         this.list.on('mouseover', this.onViewOver, this);
41152         this.list.on('mousemove', this.onViewMove, this);
41153         this.list.on('scroll', this.onViewScroll, this);
41154         
41155         if(!this.tpl){
41156             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41157         }
41158         
41159         this.view = new Roo.View(this.list, this.tpl, {
41160             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41161         });
41162         
41163         this.view.on('click', this.onViewClick, this);
41164         
41165         this.store.on('beforeload', this.onBeforeLoad, this);
41166         this.store.on('load', this.onLoad, this);
41167         this.store.on('loadexception', this.onLoadException, this);
41168         
41169         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41170             "up" : function(e){
41171                 this.inKeyMode = true;
41172                 this.selectPrev();
41173             },
41174
41175             "down" : function(e){
41176                 if(!this.isExpanded()){
41177                     this.onTriggerClick();
41178                 }else{
41179                     this.inKeyMode = true;
41180                     this.selectNext();
41181                 }
41182             },
41183
41184             "enter" : function(e){
41185                 this.collapse();
41186                 
41187                 if(this.fireEvent("specialkey", this, e)){
41188                     this.onViewClick(false);
41189                 }
41190                 
41191                 return true;
41192             },
41193
41194             "esc" : function(e){
41195                 this.collapse();
41196             },
41197
41198             "tab" : function(e){
41199                 this.collapse();
41200                 
41201                 if(this.fireEvent("specialkey", this, e)){
41202                     this.onViewClick(false);
41203                 }
41204                 
41205                 return true;
41206             },
41207
41208             scope : this,
41209
41210             doRelay : function(foo, bar, hname){
41211                 if(hname == 'down' || this.scope.isExpanded()){
41212                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41213                 }
41214                 return true;
41215             },
41216
41217             forceKeyDown: true
41218         });
41219         
41220         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41221         
41222     },
41223     
41224     initNumberEvent : function(e)
41225     {
41226         this.inputEl().on("keydown" , this.fireKey,  this);
41227         this.inputEl().on("focus", this.onFocus,  this);
41228         this.inputEl().on("blur", this.onBlur,  this);
41229         
41230         this.inputEl().relayEvent('keyup', this);
41231         
41232         if(this.indicator){
41233             this.indicator.addClass('invisible');
41234         }
41235  
41236         this.originalValue = this.getValue();
41237         
41238         if(this.validationEvent == 'keyup'){
41239             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41240             this.inputEl().on('keyup', this.filterValidation, this);
41241         }
41242         else if(this.validationEvent !== false){
41243             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41244         }
41245         
41246         if(this.selectOnFocus){
41247             this.on("focus", this.preFocus, this);
41248             
41249         }
41250         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41251             this.inputEl().on("keypress", this.filterKeys, this);
41252         } else {
41253             this.inputEl().relayEvent('keypress', this);
41254         }
41255         
41256         var allowed = "0123456789";
41257         
41258         if(this.allowDecimals){
41259             allowed += this.decimalSeparator;
41260         }
41261         
41262         if(this.allowNegative){
41263             allowed += "-";
41264         }
41265         
41266         if(this.thousandsDelimiter) {
41267             allowed += ",";
41268         }
41269         
41270         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41271         
41272         var keyPress = function(e){
41273             
41274             var k = e.getKey();
41275             
41276             var c = e.getCharCode();
41277             
41278             if(
41279                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41280                     allowed.indexOf(String.fromCharCode(c)) === -1
41281             ){
41282                 e.stopEvent();
41283                 return;
41284             }
41285             
41286             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41287                 return;
41288             }
41289             
41290             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41291                 e.stopEvent();
41292             }
41293         };
41294         
41295         this.inputEl().on("keypress", keyPress, this);
41296         
41297     },
41298     
41299     onTriggerClick : function(e)
41300     {   
41301         if(this.disabled){
41302             return;
41303         }
41304         
41305         this.page = 0;
41306         this.loadNext = false;
41307         
41308         if(this.isExpanded()){
41309             this.collapse();
41310             return;
41311         }
41312         
41313         this.hasFocus = true;
41314         
41315         if(this.triggerAction == 'all') {
41316             this.doQuery(this.allQuery, true);
41317             return;
41318         }
41319         
41320         this.doQuery(this.getRawValue());
41321     },
41322     
41323     getCurrency : function()
41324     {   
41325         var v = this.currencyEl().getValue();
41326         
41327         return v;
41328     },
41329     
41330     restrictHeight : function()
41331     {
41332         this.list.alignTo(this.currencyEl(), this.listAlign);
41333         this.list.alignTo(this.currencyEl(), this.listAlign);
41334     },
41335     
41336     onViewClick : function(view, doFocus, el, e)
41337     {
41338         var index = this.view.getSelectedIndexes()[0];
41339         
41340         var r = this.store.getAt(index);
41341         
41342         if(r){
41343             this.onSelect(r, index);
41344         }
41345     },
41346     
41347     onSelect : function(record, index){
41348         
41349         if(this.fireEvent('beforeselect', this, record, index) !== false){
41350         
41351             this.setFromCurrencyData(index > -1 ? record.data : false);
41352             
41353             this.collapse();
41354             
41355             this.fireEvent('select', this, record, index);
41356         }
41357     },
41358     
41359     setFromCurrencyData : function(o)
41360     {
41361         var currency = '';
41362         
41363         this.lastCurrency = o;
41364         
41365         if (this.currencyField) {
41366             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41367         } else {
41368             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41369         }
41370         
41371         this.lastSelectionText = currency;
41372         
41373         //setting default currency
41374         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41375             this.setCurrency(this.defaultCurrency);
41376             return;
41377         }
41378         
41379         this.setCurrency(currency);
41380     },
41381     
41382     setFromData : function(o)
41383     {
41384         var c = {};
41385         
41386         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41387         
41388         this.setFromCurrencyData(c);
41389         
41390         var value = '';
41391         
41392         if (this.name) {
41393             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41394         } else {
41395             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41396         }
41397         
41398         this.setValue(value);
41399         
41400     },
41401     
41402     setCurrency : function(v)
41403     {   
41404         this.currencyValue = v;
41405         
41406         if(this.rendered){
41407             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41408             this.validate();
41409         }
41410     },
41411     
41412     setValue : function(v)
41413     {
41414         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41415         
41416         this.value = v;
41417         
41418         if(this.rendered){
41419             
41420             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41421             
41422             this.inputEl().dom.value = (v == '') ? '' :
41423                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41424             
41425             if(!this.allowZero && v === '0') {
41426                 this.hiddenEl().dom.value = '';
41427                 this.inputEl().dom.value = '';
41428             }
41429             
41430             this.validate();
41431         }
41432     },
41433     
41434     getRawValue : function()
41435     {
41436         var v = this.inputEl().getValue();
41437         
41438         return v;
41439     },
41440     
41441     getValue : function()
41442     {
41443         return this.fixPrecision(this.parseValue(this.getRawValue()));
41444     },
41445     
41446     parseValue : function(value)
41447     {
41448         if(this.thousandsDelimiter) {
41449             value += "";
41450             r = new RegExp(",", "g");
41451             value = value.replace(r, "");
41452         }
41453         
41454         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41455         return isNaN(value) ? '' : value;
41456         
41457     },
41458     
41459     fixPrecision : function(value)
41460     {
41461         if(this.thousandsDelimiter) {
41462             value += "";
41463             r = new RegExp(",", "g");
41464             value = value.replace(r, "");
41465         }
41466         
41467         var nan = isNaN(value);
41468         
41469         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41470             return nan ? '' : value;
41471         }
41472         return parseFloat(value).toFixed(this.decimalPrecision);
41473     },
41474     
41475     decimalPrecisionFcn : function(v)
41476     {
41477         return Math.floor(v);
41478     },
41479     
41480     validateValue : function(value)
41481     {
41482         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41483             return false;
41484         }
41485         
41486         var num = this.parseValue(value);
41487         
41488         if(isNaN(num)){
41489             this.markInvalid(String.format(this.nanText, value));
41490             return false;
41491         }
41492         
41493         if(num < this.minValue){
41494             this.markInvalid(String.format(this.minText, this.minValue));
41495             return false;
41496         }
41497         
41498         if(num > this.maxValue){
41499             this.markInvalid(String.format(this.maxText, this.maxValue));
41500             return false;
41501         }
41502         
41503         return true;
41504     },
41505     
41506     validate : function()
41507     {
41508         if(this.disabled || this.allowBlank){
41509             this.markValid();
41510             return true;
41511         }
41512         
41513         var currency = this.getCurrency();
41514         
41515         if(this.validateValue(this.getRawValue()) && currency.length){
41516             this.markValid();
41517             return true;
41518         }
41519         
41520         this.markInvalid();
41521         return false;
41522     },
41523     
41524     getName: function()
41525     {
41526         return this.name;
41527     },
41528     
41529     beforeBlur : function()
41530     {
41531         if(!this.castInt){
41532             return;
41533         }
41534         
41535         var v = this.parseValue(this.getRawValue());
41536         
41537         if(v || v == 0){
41538             this.setValue(v);
41539         }
41540     },
41541     
41542     onBlur : function()
41543     {
41544         this.beforeBlur();
41545         
41546         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41547             //this.el.removeClass(this.focusClass);
41548         }
41549         
41550         this.hasFocus = false;
41551         
41552         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41553             this.validate();
41554         }
41555         
41556         var v = this.getValue();
41557         
41558         if(String(v) !== String(this.startValue)){
41559             this.fireEvent('change', this, v, this.startValue);
41560         }
41561         
41562         this.fireEvent("blur", this);
41563     },
41564     
41565     inputEl : function()
41566     {
41567         return this.el.select('.roo-money-amount-input', true).first();
41568     },
41569     
41570     currencyEl : function()
41571     {
41572         return this.el.select('.roo-money-currency-input', true).first();
41573     },
41574     
41575     hiddenEl : function()
41576     {
41577         return this.el.select('input.hidden-number-input',true).first();
41578     }
41579     
41580 });/**
41581  * @class Roo.bootstrap.BezierSignature
41582  * @extends Roo.bootstrap.Component
41583  * Bootstrap BezierSignature class
41584  * This script refer to:
41585  *    Title: Signature Pad
41586  *    Author: szimek
41587  *    Availability: https://github.com/szimek/signature_pad
41588  *
41589  * @constructor
41590  * Create a new BezierSignature
41591  * @param {Object} config The config object
41592  */
41593
41594 Roo.bootstrap.BezierSignature = function(config){
41595     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41596     this.addEvents({
41597         "resize" : true
41598     });
41599 };
41600
41601 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41602 {
41603      
41604     curve_data: [],
41605     
41606     is_empty: true,
41607     
41608     mouse_btn_down: true,
41609     
41610     /**
41611      * @cfg {int} canvas height
41612      */
41613     canvas_height: '200px',
41614     
41615     /**
41616      * @cfg {float|function} Radius of a single dot.
41617      */ 
41618     dot_size: false,
41619     
41620     /**
41621      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41622      */
41623     min_width: 0.5,
41624     
41625     /**
41626      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41627      */
41628     max_width: 2.5,
41629     
41630     /**
41631      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41632      */
41633     throttle: 16,
41634     
41635     /**
41636      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41637      */
41638     min_distance: 5,
41639     
41640     /**
41641      * @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.
41642      */
41643     bg_color: 'rgba(0, 0, 0, 0)',
41644     
41645     /**
41646      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41647      */
41648     dot_color: 'black',
41649     
41650     /**
41651      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41652      */ 
41653     velocity_filter_weight: 0.7,
41654     
41655     /**
41656      * @cfg {function} Callback when stroke begin. 
41657      */
41658     onBegin: false,
41659     
41660     /**
41661      * @cfg {function} Callback when stroke end.
41662      */
41663     onEnd: false,
41664     
41665     getAutoCreate : function()
41666     {
41667         var cls = 'roo-signature column';
41668         
41669         if(this.cls){
41670             cls += ' ' + this.cls;
41671         }
41672         
41673         var col_sizes = [
41674             'lg',
41675             'md',
41676             'sm',
41677             'xs'
41678         ];
41679         
41680         for(var i = 0; i < col_sizes.length; i++) {
41681             if(this[col_sizes[i]]) {
41682                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41683             }
41684         }
41685         
41686         var cfg = {
41687             tag: 'div',
41688             cls: cls,
41689             cn: [
41690                 {
41691                     tag: 'div',
41692                     cls: 'roo-signature-body',
41693                     cn: [
41694                         {
41695                             tag: 'canvas',
41696                             cls: 'roo-signature-body-canvas',
41697                             height: this.canvas_height,
41698                             width: this.canvas_width
41699                         }
41700                     ]
41701                 },
41702                 {
41703                     tag: 'input',
41704                     type: 'file',
41705                     style: 'display: none'
41706                 }
41707             ]
41708         };
41709         
41710         return cfg;
41711     },
41712     
41713     initEvents: function() 
41714     {
41715         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41716         
41717         var canvas = this.canvasEl();
41718         
41719         // mouse && touch event swapping...
41720         canvas.dom.style.touchAction = 'none';
41721         canvas.dom.style.msTouchAction = 'none';
41722         
41723         this.mouse_btn_down = false;
41724         canvas.on('mousedown', this._handleMouseDown, this);
41725         canvas.on('mousemove', this._handleMouseMove, this);
41726         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41727         
41728         if (window.PointerEvent) {
41729             canvas.on('pointerdown', this._handleMouseDown, this);
41730             canvas.on('pointermove', this._handleMouseMove, this);
41731             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41732         }
41733         
41734         if ('ontouchstart' in window) {
41735             canvas.on('touchstart', this._handleTouchStart, this);
41736             canvas.on('touchmove', this._handleTouchMove, this);
41737             canvas.on('touchend', this._handleTouchEnd, this);
41738         }
41739         
41740         Roo.EventManager.onWindowResize(this.resize, this, true);
41741         
41742         // file input event
41743         this.fileEl().on('change', this.uploadImage, this);
41744         
41745         this.clear();
41746         
41747         this.resize();
41748     },
41749     
41750     resize: function(){
41751         
41752         var canvas = this.canvasEl().dom;
41753         var ctx = this.canvasElCtx();
41754         var img_data = false;
41755         
41756         if(canvas.width > 0) {
41757             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41758         }
41759         // setting canvas width will clean img data
41760         canvas.width = 0;
41761         
41762         var style = window.getComputedStyle ? 
41763             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41764             
41765         var padding_left = parseInt(style.paddingLeft) || 0;
41766         var padding_right = parseInt(style.paddingRight) || 0;
41767         
41768         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41769         
41770         if(img_data) {
41771             ctx.putImageData(img_data, 0, 0);
41772         }
41773     },
41774     
41775     _handleMouseDown: function(e)
41776     {
41777         if (e.browserEvent.which === 1) {
41778             this.mouse_btn_down = true;
41779             this.strokeBegin(e);
41780         }
41781     },
41782     
41783     _handleMouseMove: function (e)
41784     {
41785         if (this.mouse_btn_down) {
41786             this.strokeMoveUpdate(e);
41787         }
41788     },
41789     
41790     _handleMouseUp: function (e)
41791     {
41792         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41793             this.mouse_btn_down = false;
41794             this.strokeEnd(e);
41795         }
41796     },
41797     
41798     _handleTouchStart: function (e) {
41799         
41800         e.preventDefault();
41801         if (e.browserEvent.targetTouches.length === 1) {
41802             // var touch = e.browserEvent.changedTouches[0];
41803             // this.strokeBegin(touch);
41804             
41805              this.strokeBegin(e); // assume e catching the correct xy...
41806         }
41807     },
41808     
41809     _handleTouchMove: function (e) {
41810         e.preventDefault();
41811         // var touch = event.targetTouches[0];
41812         // _this._strokeMoveUpdate(touch);
41813         this.strokeMoveUpdate(e);
41814     },
41815     
41816     _handleTouchEnd: function (e) {
41817         var wasCanvasTouched = e.target === this.canvasEl().dom;
41818         if (wasCanvasTouched) {
41819             e.preventDefault();
41820             // var touch = event.changedTouches[0];
41821             // _this._strokeEnd(touch);
41822             this.strokeEnd(e);
41823         }
41824     },
41825     
41826     reset: function () {
41827         this._lastPoints = [];
41828         this._lastVelocity = 0;
41829         this._lastWidth = (this.min_width + this.max_width) / 2;
41830         this.canvasElCtx().fillStyle = this.dot_color;
41831     },
41832     
41833     strokeMoveUpdate: function(e)
41834     {
41835         this.strokeUpdate(e);
41836         
41837         if (this.throttle) {
41838             this.throttleStroke(this.strokeUpdate, this.throttle);
41839         }
41840         else {
41841             this.strokeUpdate(e);
41842         }
41843     },
41844     
41845     strokeBegin: function(e)
41846     {
41847         var newPointGroup = {
41848             color: this.dot_color,
41849             points: []
41850         };
41851         
41852         if (typeof this.onBegin === 'function') {
41853             this.onBegin(e);
41854         }
41855         
41856         this.curve_data.push(newPointGroup);
41857         this.reset();
41858         this.strokeUpdate(e);
41859     },
41860     
41861     strokeUpdate: function(e)
41862     {
41863         var rect = this.canvasEl().dom.getBoundingClientRect();
41864         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41865         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41866         var lastPoints = lastPointGroup.points;
41867         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41868         var isLastPointTooClose = lastPoint
41869             ? point.distanceTo(lastPoint) <= this.min_distance
41870             : false;
41871         var color = lastPointGroup.color;
41872         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41873             var curve = this.addPoint(point);
41874             if (!lastPoint) {
41875                 this.drawDot({color: color, point: point});
41876             }
41877             else if (curve) {
41878                 this.drawCurve({color: color, curve: curve});
41879             }
41880             lastPoints.push({
41881                 time: point.time,
41882                 x: point.x,
41883                 y: point.y
41884             });
41885         }
41886     },
41887     
41888     strokeEnd: function(e)
41889     {
41890         this.strokeUpdate(e);
41891         if (typeof this.onEnd === 'function') {
41892             this.onEnd(e);
41893         }
41894     },
41895     
41896     addPoint:  function (point) {
41897         var _lastPoints = this._lastPoints;
41898         _lastPoints.push(point);
41899         if (_lastPoints.length > 2) {
41900             if (_lastPoints.length === 3) {
41901                 _lastPoints.unshift(_lastPoints[0]);
41902             }
41903             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41904             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41905             _lastPoints.shift();
41906             return curve;
41907         }
41908         return null;
41909     },
41910     
41911     calculateCurveWidths: function (startPoint, endPoint) {
41912         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41913             (1 - this.velocity_filter_weight) * this._lastVelocity;
41914
41915         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41916         var widths = {
41917             end: newWidth,
41918             start: this._lastWidth
41919         };
41920         
41921         this._lastVelocity = velocity;
41922         this._lastWidth = newWidth;
41923         return widths;
41924     },
41925     
41926     drawDot: function (_a) {
41927         var color = _a.color, point = _a.point;
41928         var ctx = this.canvasElCtx();
41929         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41930         ctx.beginPath();
41931         this.drawCurveSegment(point.x, point.y, width);
41932         ctx.closePath();
41933         ctx.fillStyle = color;
41934         ctx.fill();
41935     },
41936     
41937     drawCurve: function (_a) {
41938         var color = _a.color, curve = _a.curve;
41939         var ctx = this.canvasElCtx();
41940         var widthDelta = curve.endWidth - curve.startWidth;
41941         var drawSteps = Math.floor(curve.length()) * 2;
41942         ctx.beginPath();
41943         ctx.fillStyle = color;
41944         for (var i = 0; i < drawSteps; i += 1) {
41945         var t = i / drawSteps;
41946         var tt = t * t;
41947         var ttt = tt * t;
41948         var u = 1 - t;
41949         var uu = u * u;
41950         var uuu = uu * u;
41951         var x = uuu * curve.startPoint.x;
41952         x += 3 * uu * t * curve.control1.x;
41953         x += 3 * u * tt * curve.control2.x;
41954         x += ttt * curve.endPoint.x;
41955         var y = uuu * curve.startPoint.y;
41956         y += 3 * uu * t * curve.control1.y;
41957         y += 3 * u * tt * curve.control2.y;
41958         y += ttt * curve.endPoint.y;
41959         var width = curve.startWidth + ttt * widthDelta;
41960         this.drawCurveSegment(x, y, width);
41961         }
41962         ctx.closePath();
41963         ctx.fill();
41964     },
41965     
41966     drawCurveSegment: function (x, y, width) {
41967         var ctx = this.canvasElCtx();
41968         ctx.moveTo(x, y);
41969         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41970         this.is_empty = false;
41971     },
41972     
41973     clear: function()
41974     {
41975         var ctx = this.canvasElCtx();
41976         var canvas = this.canvasEl().dom;
41977         ctx.fillStyle = this.bg_color;
41978         ctx.clearRect(0, 0, canvas.width, canvas.height);
41979         ctx.fillRect(0, 0, canvas.width, canvas.height);
41980         this.curve_data = [];
41981         this.reset();
41982         this.is_empty = true;
41983     },
41984     
41985     fileEl: function()
41986     {
41987         return  this.el.select('input',true).first();
41988     },
41989     
41990     canvasEl: function()
41991     {
41992         return this.el.select('canvas',true).first();
41993     },
41994     
41995     canvasElCtx: function()
41996     {
41997         return this.el.select('canvas',true).first().dom.getContext('2d');
41998     },
41999     
42000     getImage: function(type)
42001     {
42002         if(this.is_empty) {
42003             return false;
42004         }
42005         
42006         // encryption ?
42007         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42008     },
42009     
42010     drawFromImage: function(img_src)
42011     {
42012         var img = new Image();
42013         
42014         img.onload = function(){
42015             this.canvasElCtx().drawImage(img, 0, 0);
42016         }.bind(this);
42017         
42018         img.src = img_src;
42019         
42020         this.is_empty = false;
42021     },
42022     
42023     selectImage: function()
42024     {
42025         this.fileEl().dom.click();
42026     },
42027     
42028     uploadImage: function(e)
42029     {
42030         var reader = new FileReader();
42031         
42032         reader.onload = function(e){
42033             var img = new Image();
42034             img.onload = function(){
42035                 this.reset();
42036                 this.canvasElCtx().drawImage(img, 0, 0);
42037             }.bind(this);
42038             img.src = e.target.result;
42039         }.bind(this);
42040         
42041         reader.readAsDataURL(e.target.files[0]);
42042     },
42043     
42044     // Bezier Point Constructor
42045     Point: (function () {
42046         function Point(x, y, time) {
42047             this.x = x;
42048             this.y = y;
42049             this.time = time || Date.now();
42050         }
42051         Point.prototype.distanceTo = function (start) {
42052             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42053         };
42054         Point.prototype.equals = function (other) {
42055             return this.x === other.x && this.y === other.y && this.time === other.time;
42056         };
42057         Point.prototype.velocityFrom = function (start) {
42058             return this.time !== start.time
42059             ? this.distanceTo(start) / (this.time - start.time)
42060             : 0;
42061         };
42062         return Point;
42063     }()),
42064     
42065     
42066     // Bezier Constructor
42067     Bezier: (function () {
42068         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42069             this.startPoint = startPoint;
42070             this.control2 = control2;
42071             this.control1 = control1;
42072             this.endPoint = endPoint;
42073             this.startWidth = startWidth;
42074             this.endWidth = endWidth;
42075         }
42076         Bezier.fromPoints = function (points, widths, scope) {
42077             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42078             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42079             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42080         };
42081         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42082             var dx1 = s1.x - s2.x;
42083             var dy1 = s1.y - s2.y;
42084             var dx2 = s2.x - s3.x;
42085             var dy2 = s2.y - s3.y;
42086             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42087             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42088             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42089             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42090             var dxm = m1.x - m2.x;
42091             var dym = m1.y - m2.y;
42092             var k = l2 / (l1 + l2);
42093             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42094             var tx = s2.x - cm.x;
42095             var ty = s2.y - cm.y;
42096             return {
42097                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42098                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42099             };
42100         };
42101         Bezier.prototype.length = function () {
42102             var steps = 10;
42103             var length = 0;
42104             var px;
42105             var py;
42106             for (var i = 0; i <= steps; i += 1) {
42107                 var t = i / steps;
42108                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42109                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42110                 if (i > 0) {
42111                     var xdiff = cx - px;
42112                     var ydiff = cy - py;
42113                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42114                 }
42115                 px = cx;
42116                 py = cy;
42117             }
42118             return length;
42119         };
42120         Bezier.prototype.point = function (t, start, c1, c2, end) {
42121             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42122             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42123             + (3.0 * c2 * (1.0 - t) * t * t)
42124             + (end * t * t * t);
42125         };
42126         return Bezier;
42127     }()),
42128     
42129     throttleStroke: function(fn, wait) {
42130       if (wait === void 0) { wait = 250; }
42131       var previous = 0;
42132       var timeout = null;
42133       var result;
42134       var storedContext;
42135       var storedArgs;
42136       var later = function () {
42137           previous = Date.now();
42138           timeout = null;
42139           result = fn.apply(storedContext, storedArgs);
42140           if (!timeout) {
42141               storedContext = null;
42142               storedArgs = [];
42143           }
42144       };
42145       return function wrapper() {
42146           var args = [];
42147           for (var _i = 0; _i < arguments.length; _i++) {
42148               args[_i] = arguments[_i];
42149           }
42150           var now = Date.now();
42151           var remaining = wait - (now - previous);
42152           storedContext = this;
42153           storedArgs = args;
42154           if (remaining <= 0 || remaining > wait) {
42155               if (timeout) {
42156                   clearTimeout(timeout);
42157                   timeout = null;
42158               }
42159               previous = now;
42160               result = fn.apply(storedContext, storedArgs);
42161               if (!timeout) {
42162                   storedContext = null;
42163                   storedArgs = [];
42164               }
42165           }
42166           else if (!timeout) {
42167               timeout = window.setTimeout(later, remaining);
42168           }
42169           return result;
42170       };
42171   }
42172   
42173 });
42174
42175  
42176
42177