css/button.css
[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 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
62     
63     
64     allowDomMove : false, // to stop relocations in parent onRender...
65     
66     cls : false,
67     
68     style : false,
69     
70     autoCreate : false,
71     
72     tooltip : null,
73     /**
74      * Initialize Events for the element
75      */
76     initEvents : function() { },
77     
78     xattr : false,
79     
80     parentId : false,
81     
82     can_build_overlaid : true,
83     
84     container_method : false,
85     
86     dataId : false,
87     
88     name : false,
89     
90     parent: function() {
91         // returns the parent component..
92         return Roo.ComponentMgr.get(this.parentId)
93         
94         
95     },
96     
97     // private
98     onRender : function(ct, position)
99     {
100        // Roo.log("Call onRender: " + this.xtype);
101         
102         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
103         
104         if(this.el){
105             if (this.el.attr('xtype')) {
106                 this.el.attr('xtypex', this.el.attr('xtype'));
107                 this.el.dom.removeAttribute('xtype');
108                 
109                 this.initEvents();
110             }
111             
112             return;
113         }
114         
115          
116         
117         var cfg = Roo.apply({},  this.getAutoCreate());
118         
119         cfg.id = this.id || Roo.id();
120         
121         // fill in the extra attributes 
122         if (this.xattr && typeof(this.xattr) =='object') {
123             for (var i in this.xattr) {
124                 cfg[i] = this.xattr[i];
125             }
126         }
127         
128         if(this.dataId){
129             cfg.dataId = this.dataId;
130         }
131         
132         if (this.cls) {
133             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
134         }
135         
136         if (this.style) { // fixme needs to support more complex style data.
137             cfg.style = this.style;
138         }
139         
140         if(this.name){
141             cfg.name = this.name;
142         }
143         
144         this.el = ct.createChild(cfg, position);
145         
146         if (this.tooltip) {
147             this.tooltipEl().attr('tooltip', this.tooltip);
148         }
149         
150         if(this.tabIndex !== undefined){
151             this.el.dom.setAttribute('tabIndex', this.tabIndex);
152         }
153         
154         this.initEvents();
155         
156     },
157     /**
158      * Fetch the element to add children to
159      * @return {Roo.Element} defaults to this.el
160      */
161     getChildContainer : function()
162     {
163         return this.el;
164     },
165     /**
166      * Fetch the element to display the tooltip on.
167      * @return {Roo.Element} defaults to this.el
168      */
169     tooltipEl : function()
170     {
171         return this.el;
172     },
173     /**
174      * This is really a wrapper for addxtypeChild
175      * it handles stuff relating to flexy:foreach / flexy:if
176      * = some of our projects use a flat rendering of the output, and try and overlay it with dynamic data.
177      *  -- this is a bit of a nightmare... and is even more confusing to debug..
178      *
179      *  
180      *
181      */
182     addxtype  : function(tree,cntr)
183     {
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         var cn = Roo.factory(tree); // this is posibly the first of two times that the ctor get's called...
187         cn.parentType = this.xtype; //??
188         cn.parentId = this.id;
189         if (typeof(cn.container_method) == 'string') {
190             cntr = cn.container_method;
191         }
192         
193         
194         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
195         
196         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
197         
198         var build_from_html =  Roo.XComponent.build_from_html;
199           
200         var is_body  = (tree.xtype == 'Body') ;
201           
202         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
203           
204         var self_cntr_el = Roo.get(this[cntr](false));
205         
206         // do not try and build conditional elements 
207         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208             return false;
209         }
210         
211         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
212             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
213                 //return this.addxtypeChild(tree,cntr, is_body);
214                 return this.addxtypeChild(tree, cntr, is_body);
215             }
216             
217             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
218                 
219             if(echild){
220                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
221             }
222             
223             Roo.log('skipping render');
224             return cn;
225             
226         }
227         
228         var ret = false;
229         if (!build_from_html) {
230             return false;
231         }
232         
233         // this i think handles overlaying multiple children of the same type
234         // with the sam eelement.. - which might be buggy..
235         while (true) {
236             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
237             
238             if (!echild) {
239                 break;
240             }
241             
242             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
243                 break;
244             }
245             
246             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
247         }
248        
249         return ret;
250     },
251     /**
252      * add a child to this element
253      *   - turn the child.cfg into a child_instance
254      *   - call child_instance.render( this { getContainerMethod()} )
255      *   - loop through the children, and call addxtype.. (reall this) on newly created child.
256      *  
257      */
258     
259     addxtypeChild : function (tree, cntr, is_body)
260     {
261         Roo.debug && Roo.log('addxtypeChild:' + cntr);
262         var cn = this;
263         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
264         
265         
266         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
267                     (typeof(tree['flexy:foreach']) != 'undefined');
268           
269     
270         
271         skip_children = false;
272         // render the element if it's not BODY.
273         if (!is_body) {
274             
275             // if parent was disabled, then do not try and create the children..
276             if(!this[cntr](true)){
277                 tree.items = [];
278                 return tree;
279             }
280            
281             cn = Roo.factory(tree);
282            
283             cn.parentType = this.xtype; //??
284             cn.parentId = this.id;
285             
286             var build_from_html =  Roo.XComponent.build_from_html;
287             
288             
289             // does the container contain child eleemnts with 'xtype' attributes.
290             // that match this xtype..
291             // note - when we render we create these as well..
292             // so we should check to see if body has xtype set.
293             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
294                
295                 var self_cntr_el = Roo.get(this[cntr](false));
296                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
297                 if (echild) { 
298                     //Roo.log(Roo.XComponent.build_from_html);
299                     //Roo.log("got echild:");
300                     //Roo.log(echild);
301                 }
302                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
303                 // and are not displayed -this causes this to use up the wrong element when matching.
304                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
305                 
306                 
307                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
308                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
309                   
310                   
311                   
312                     cn.el = echild;
313                   //  Roo.log("GOT");
314                     //echild.dom.removeAttribute('xtype');
315                 } else {
316                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
317                     Roo.debug && Roo.log(self_cntr_el);
318                     Roo.debug && Roo.log(echild);
319                     Roo.debug && Roo.log(cn);
320                 }
321             }
322            
323             
324            
325             // if object has flexy:if - then it may or may not be rendered.
326             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
327                 // skip a flexy if element.
328                 Roo.debug && Roo.log('skipping render');
329                 Roo.debug && Roo.log(tree);
330                 if (!cn.el) {
331                     Roo.debug && Roo.log('skipping all children');
332                     skip_children = true;
333                 }
334                 
335              } else {
336                  
337                 // actually if flexy:foreach is found, we really want to create 
338                 // multiple copies here...
339                 //Roo.log('render');
340                 //Roo.log(this[cntr]());
341                 // some elements do not have render methods.. like the layouts...
342                 /*
343                 if(this[cntr](true) === false){
344                     cn.items = [];
345                     return cn;
346                 }
347                 */
348                 cn.render && cn.render(this[cntr](true));
349                 
350              }
351             // then add the element..
352         }
353         
354         
355         
356         
357         
358         cn.addxtypeChildren(tree.items, skip_children);
359         delete tree.items;
360         return cn;
361     },
362     
363     /**
364      *  add a number of children to this object,
365      *     which in turn calls render...
366      *  
367      */
368     
369     addxtypeChildren: function(child_array, skip_children)
370     {
371         var nitems = [];
372         if (!child_array || !child_array.length ) {
373             this.items = nitems;
374             return;
375         }
376         
377         for(var i =0;i < child_array.length;i++) {
378             if (skip_children) {
379                 break;
380             }
381             //  Roo.log(['add child', items[i]]);
382             nitems.push(this.addxtype(Roo.apply({}, child_array[i])));
383         }
384         this.items = nitems;
385         
386         this.fireEvent('childrenrendered', this);
387          
388     },
389     
390     
391     
392     
393     
394     
395     /**
396      * xAddChildren - the 'sub-compentized' version of the above idea..
397      */
398     xAddChildren: function(child_array, skip_children)
399     {
400         var nitems = [];
401         if (!child_array || !child_array.length ) {
402             this.items = nitems;
403             return;
404         }
405         
406         for(var i =0;i < child_array.length;i++) {
407             if (skip_children) {
408                 break;
409             }
410             //  Roo.log(['add child', items[i]]);
411             nitems.push(this.xAdd(Roo.apply({}, child_array[i])));
412         }
413         this.items = nitems;
414         
415         this.fireEvent('childrenrendered', this);
416          
417     },
418     
419     
420     /**
421      * add a child to this element
422      *   - turn the child.cfg into a child_instance
423      *   - call child_instance.render( this { getContainerMethod()} )
424      *   - loop through the children, and call addxtype.. (reall this) on newly created child.
425      *  
426      *
427      * 
428      */
429     
430     xAdd : function (tree, cntr, is_body)
431     {
432         Roo.debug && Roo.log('xadd:' + cntr);
433         
434         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
435          
436         var parent_ctnr = this[cntr](true);
437         if(parent_ctnr === false){
438             return false; // getChildContainer an return false explicitly to block children being added?
439         }
440         if(!parent_ctnr ){
441             throw new Exception("could not find parent Container for item");
442         }
443            
444         var cn = Roo.factory(tree);
445         // at this point items[] array may be set..
446         // constructors should not really be building their children?
447         cn.parentType = this.xtype; //??
448         cn.parentId = this.id;
449             
450             
451         cn.render && cn.render(parent_ctnr);
452                 
453         cn.xAddChildren(tree.items);
454         delete tree.items; // not really needed?
455         return cn;
456     },
457     
458     
459     /**
460      * Set the element that will be used to show or hide
461      */
462     setVisibilityEl : function(el)
463     {
464         this.visibilityEl = el;
465     },
466     
467      /**
468      * Get the element that will be used to show or hide
469      */
470     getVisibilityEl : function()
471     {
472         if (typeof(this.visibilityEl) == 'object') {
473             return this.visibilityEl;
474         }
475         
476         if (typeof(this.visibilityEl) == 'string') {
477             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
478         }
479         
480         return this.getEl();
481     },
482     
483     /**
484      * Show a component - removes 'hidden' class
485      */
486     show : function()
487     {
488         if(!this.getVisibilityEl()){
489             return;
490         }
491          
492         this.getVisibilityEl().removeClass(['hidden','d-none']);
493         
494         this.fireEvent('show', this);
495         
496         
497     },
498     /**
499      * Hide a component - adds 'hidden' class
500      */
501     hide: function()
502     {
503         if(!this.getVisibilityEl()){
504             return;
505         }
506         
507         this.getVisibilityEl().addClass(['hidden','d-none']);
508         
509         this.fireEvent('hide', this);
510         
511     }
512 });
513
514  /*
515  * - LGPL
516  *
517  * Body
518  *
519  */
520
521 /**
522  * @class Roo.bootstrap.Body
523  * @extends Roo.bootstrap.Component
524  * Bootstrap Body class
525  *
526  * @constructor
527  * Create a new body
528  * @param {Object} config The config object
529  * @cfg {DomElement} do_render - if this is set, then the constructor will try and initialize render, using this as the start point
530  
531  */
532
533 Roo.bootstrap.Body = function(config){
534
535     config = config || {};
536
537     Roo.bootstrap.Body.superclass.constructor.call(this, config);
538     this.el = Roo.get(config.el ? config.el : document.body );
539     if (this.cls && this.cls.length) {
540         Roo.get(document.body).addClass(this.cls);
541     }
542     if (config.do_render) {
543         this.onRender(config.do_render, '');
544         this.xAddChildren(config.items);
545     }
546     
547 };
548
549 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
550
551     do_render : false,
552     is_body : true,// just to make sure it's constructed?
553
554     autoCreate : {
555         cls: 'container'
556     },
557     onRender : function(ct, position)
558     {
559         if (!this.do_render) {
560             return;
561         }
562         this.el = Roo.get(this.do_render);
563          
564     }
565
566
567
568
569 });
570 /*
571  * - LGPL
572  *
573  * button group
574  * 
575  */
576
577
578 /**
579  * @class Roo.bootstrap.ButtonGroup
580  * @extends Roo.bootstrap.Component
581  * Bootstrap ButtonGroup class
582  * @cfg {String} size lg | sm | xs (default empty normal)
583  * @cfg {String} align vertical | justified  (default none)
584  * @cfg {String} direction up | down (default down)
585  * @cfg {Boolean} toolbar false | true
586  * @cfg {Boolean} btn true | false
587  * 
588  * 
589  * @constructor
590  * Create a new Input
591  * @param {Object} config The config object
592  */
593
594 Roo.bootstrap.ButtonGroup = function(config){
595     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
596 };
597
598 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
599     
600     size: '',
601     align: '',
602     direction: '',
603     toolbar: false,
604     btn: true,
605
606     getAutoCreate : function(){
607         var cfg = {
608             cls: 'btn-group',
609             html : null
610         };
611         
612         cfg.html = this.html || cfg.html;
613         
614         if (this.toolbar) {
615             cfg = {
616                 cls: 'btn-toolbar',
617                 html: null
618             };
619             
620             return cfg;
621         }
622         
623         if (['vertical','justified'].indexOf(this.align)!==-1) {
624             cfg.cls = 'btn-group-' + this.align;
625             
626             if (this.align == 'justified') {
627                 console.log(this.items);
628             }
629         }
630         
631         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
632             cfg.cls += ' btn-group-' + this.size;
633         }
634         
635         if (this.direction == 'up') {
636             cfg.cls += ' dropup' ;
637         }
638         
639         return cfg;
640     },
641     /**
642      * Add a button to the group (similar to NavItem API.)
643      */
644     addItem : function(cfg)
645     {
646         var cn = new Roo.bootstrap.Button(cfg);
647         //this.register(cn);
648         cn.parentId = this.id;
649         cn.onRender(this.el, null);
650         return cn;
651     }
652    
653 });
654
655  /*
656  * - LGPL
657  *
658  * button
659  * 
660  */
661
662 /**
663  * @class Roo.bootstrap.Button
664  * @extends Roo.bootstrap.Component
665  * Bootstrap Button class
666  * @cfg {String} html The button content
667  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
668  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
669  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
670  * @cfg {String} size ( lg | sm | xs)
671  * @cfg {String} tag ( a | input | submit)
672  * @cfg {String} href empty or href
673  * @cfg {Boolean} disabled default false;
674  * @cfg {Boolean} isClose default false;
675  * @cfg {String} glyphicon depricated - use fa
676  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
677  * @cfg {String} badge text for badge
678  * @cfg {String} theme (default|glow)  
679  * @cfg {Boolean} inverse dark themed version
680  * @cfg {Boolean} toggle is it a slidy toggle button
681  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
682  * @cfg {String} ontext text for on slidy toggle state
683  * @cfg {String} offtext text for off slidy toggle state
684  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
685  * @cfg {Boolean} removeClass remove the standard class..
686  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
687  * 
688  * @constructor
689  * Create a new button
690  * @param {Object} config The config object
691  */
692
693
694 Roo.bootstrap.Button = function(config){
695     Roo.bootstrap.Button.superclass.constructor.call(this, config);
696     this.weightClass = ["btn-default btn-outline-secondary", 
697                        "btn-primary", 
698                        "btn-success", 
699                        "btn-info", 
700                        "btn-warning",
701                        "btn-danger",
702                        "btn-link"
703                       ],  
704     this.addEvents({
705         // raw events
706         /**
707          * @event click
708          * When a butotn is pressed
709          * @param {Roo.bootstrap.Button} btn
710          * @param {Roo.EventObject} e
711          */
712         "click" : true,
713          /**
714          * @event toggle
715          * After the button has been toggles
716          * @param {Roo.bootstrap.Button} btn
717          * @param {Roo.EventObject} e
718          * @param {boolean} pressed (also available as button.pressed)
719          */
720         "toggle" : true
721     });
722 };
723
724 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
725     html: false,
726     active: false,
727     weight: '',
728     badge_weight: '',
729     outline : false,
730     size: '',
731     tag: 'button',
732     href: '',
733     disabled: false,
734     isClose: false,
735     glyphicon: '',
736     fa: '',
737     badge: '',
738     theme: 'default',
739     inverse: false,
740     
741     toggle: false,
742     ontext: 'ON',
743     offtext: 'OFF',
744     defaulton: true,
745     preventDefault: true,
746     removeClass: false,
747     name: false,
748     target: false,
749      
750     pressed : null,
751      
752     
753     getAutoCreate : function(){
754         
755         var cfg = {
756             tag : 'button',
757             cls : 'roo-button',
758             html: ''
759         };
760         
761         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
762             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
763             this.tag = 'button';
764         } else {
765             cfg.tag = this.tag;
766         }
767         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
768         
769         if (this.toggle == true) {
770             cfg={
771                 tag: 'div',
772                 cls: 'slider-frame roo-button',
773                 cn: [
774                     {
775                         tag: 'span',
776                         'data-on-text':'ON',
777                         'data-off-text':'OFF',
778                         cls: 'slider-button',
779                         html: this.offtext
780                     }
781                 ]
782             };
783             
784             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
785                 cfg.cls += ' '+this.weight;
786             }
787             
788             return cfg;
789         }
790         
791         if (this.isClose) {
792             cfg.cls += ' close';
793             
794             cfg["aria-hidden"] = true;
795             
796             cfg.html = "&times;";
797             
798             return cfg;
799         }
800         
801          
802         if (this.theme==='default') {
803             cfg.cls = 'btn roo-button';
804             
805             //if (this.parentType != 'Navbar') {
806             this.weight = this.weight.length ?  this.weight : 'default';
807             //}
808             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
809                 
810                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
811                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
812                 cfg.cls += ' btn-' + outline + weight;
813                 if (this.weight == 'default') {
814                     // BC
815                     cfg.cls += ' btn-' + this.weight;
816                 }
817             }
818         } else if (this.theme==='glow') {
819             
820             cfg.tag = 'a';
821             cfg.cls = 'btn-glow roo-button';
822             
823             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
824                 
825                 cfg.cls += ' ' + this.weight;
826             }
827         }
828    
829         
830         if (this.inverse) {
831             this.cls += ' inverse';
832         }
833         
834         
835         if (this.active || this.pressed === true) {
836             cfg.cls += ' active';
837         }
838         
839         if (this.disabled) {
840             cfg.disabled = 'disabled';
841         }
842         
843         if (this.items) {
844             Roo.log('changing to ul' );
845             cfg.tag = 'ul';
846             this.glyphicon = 'caret';
847             if (Roo.bootstrap.version == 4) {
848                 this.fa = 'caret-down';
849             }
850             
851         }
852         
853         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
854          
855         //gsRoo.log(this.parentType);
856         if (this.parentType === 'Navbar' && !this.parent().bar) {
857             Roo.log('changing to li?');
858             
859             cfg.tag = 'li';
860             
861             cfg.cls = '';
862             cfg.cn =  [{
863                 tag : 'a',
864                 cls : 'roo-button',
865                 html : this.html,
866                 href : this.href || '#'
867             }];
868             if (this.menu) {
869                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
870                 cfg.cls += ' dropdown';
871             }   
872             
873             delete cfg.html;
874             
875         }
876         
877        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
878         
879         if (this.glyphicon) {
880             cfg.html = ' ' + cfg.html;
881             
882             cfg.cn = [
883                 {
884                     tag: 'span',
885                     cls: 'glyphicon glyphicon-' + this.glyphicon
886                 }
887             ];
888         }
889         if (this.fa) {
890             cfg.html = ' ' + cfg.html;
891             
892             cfg.cn = [
893                 {
894                     tag: 'i',
895                     cls: 'fa fas fa-' + this.fa
896                 }
897             ];
898         }
899         
900         if (this.badge) {
901             cfg.html += ' ';
902             
903             cfg.tag = 'a';
904             
905 //            cfg.cls='btn roo-button';
906             
907             cfg.href=this.href;
908             
909             var value = cfg.html;
910             
911             if(this.glyphicon){
912                 value = {
913                     tag: 'span',
914                     cls: 'glyphicon glyphicon-' + this.glyphicon,
915                     html: this.html
916                 };
917             }
918             if(this.fa){
919                 value = {
920                     tag: 'i',
921                     cls: 'fa fas fa-' + this.fa,
922                     html: this.html
923                 };
924             }
925             
926             var bw = this.badge_weight.length ? this.badge_weight :
927                 (this.weight.length ? this.weight : 'secondary');
928             bw = bw == 'default' ? 'secondary' : bw;
929             
930             cfg.cn = [
931                 value,
932                 {
933                     tag: 'span',
934                     cls: 'badge badge-' + bw,
935                     html: this.badge
936                 }
937             ];
938             
939             cfg.html='';
940         }
941         
942         if (this.menu) {
943             cfg.cls += ' dropdown';
944             cfg.html = typeof(cfg.html) != 'undefined' ?
945                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
946         }
947         
948         if (cfg.tag !== 'a' && this.href !== '') {
949             throw "Tag must be a to set href.";
950         } else if (this.href.length > 0) {
951             cfg.href = this.href;
952         }
953         
954         if(this.removeClass){
955             cfg.cls = '';
956         }
957         
958         if(this.target){
959             cfg.target = this.target;
960         }
961         
962         return cfg;
963     },
964     initEvents: function() {
965        // Roo.log('init events?');
966 //        Roo.log(this.el.dom);
967         // add the menu...
968         
969         if (typeof (this.menu) != 'undefined') {
970             this.menu.parentType = this.xtype;
971             this.menu.triggerEl = this.el;
972             this.addxtype(Roo.apply({}, this.menu));
973         }
974
975
976        if (this.el.hasClass('roo-button')) {
977             this.el.on('click', this.onClick, this);
978        } else {
979             this.el.select('.roo-button').on('click', this.onClick, this);
980        }
981        
982        if(this.removeClass){
983            this.el.on('click', this.onClick, this);
984        }
985        
986        this.el.enableDisplayMode();
987         
988     },
989     onClick : function(e)
990     {
991         if (this.disabled) {
992             return;
993         }
994         
995         Roo.log('button on click ');
996         if(this.preventDefault){
997             e.preventDefault();
998         }
999         
1000         if (this.pressed === true || this.pressed === false) {
1001             this.toggleActive(e);
1002         }
1003         
1004         
1005         this.fireEvent('click', this, e);
1006     },
1007     
1008     /**
1009      * Enables this button
1010      */
1011     enable : function()
1012     {
1013         this.disabled = false;
1014         this.el.removeClass('disabled');
1015     },
1016     
1017     /**
1018      * Disable this button
1019      */
1020     disable : function()
1021     {
1022         this.disabled = true;
1023         this.el.addClass('disabled');
1024     },
1025      /**
1026      * sets the active state on/off, 
1027      * @param {Boolean} state (optional) Force a particular state
1028      */
1029     setActive : function(v) {
1030         
1031         this.el[v ? 'addClass' : 'removeClass']('active');
1032         this.pressed = v;
1033     },
1034      /**
1035      * toggles the current active state 
1036      */
1037     toggleActive : function(e)
1038     {
1039         this.setActive(!this.pressed);
1040         this.fireEvent('toggle', this, e, !this.pressed);
1041     },
1042      /**
1043      * get the current active state
1044      * @return {boolean} true if it's active
1045      */
1046     isActive : function()
1047     {
1048         return this.el.hasClass('active');
1049     },
1050     /**
1051      * set the text of the first selected button
1052      */
1053     setText : function(str)
1054     {
1055         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1056     },
1057     /**
1058      * get the text of the first selected button
1059      */
1060     getText : function()
1061     {
1062         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1063     },
1064     
1065     setWeight : function(str)
1066     {
1067         this.el.removeClass(this.weightClass);
1068         this.weight = str;
1069         var outline = this.outline ? 'outline-' : '';
1070         if (str == 'default') {
1071             this.el.addClass('btn-default btn-outline-secondary');        
1072             return;
1073         }
1074         this.el.addClass('btn-' + outline + str);        
1075     }
1076     
1077     
1078 });
1079
1080  /*
1081  * - LGPL
1082  *
1083  * column
1084  * 
1085  */
1086
1087 /**
1088  * @class Roo.bootstrap.Column
1089  * @extends Roo.bootstrap.Component
1090  * Bootstrap Column class
1091  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1092  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1093  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1094  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1095  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1096  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1097  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1098  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1099  *
1100  * 
1101  * @cfg {Boolean} hidden (true|false) hide the element
1102  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1103  * @cfg {String} fa (ban|check|...) font awesome icon
1104  * @cfg {Number} fasize (1|2|....) font awsome size
1105
1106  * @cfg {String} icon (info-sign|check|...) glyphicon name
1107
1108  * @cfg {String} html content of column.
1109  * 
1110  * @constructor
1111  * Create a new Column
1112  * @param {Object} config The config object
1113  */
1114
1115 Roo.bootstrap.Column = function(config){
1116     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1117 };
1118
1119 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1120     
1121     xs: false,
1122     sm: false,
1123     md: false,
1124     lg: false,
1125     xsoff: false,
1126     smoff: false,
1127     mdoff: false,
1128     lgoff: false,
1129     html: '',
1130     offset: 0,
1131     alert: false,
1132     fa: false,
1133     icon : false,
1134     hidden : false,
1135     fasize : 1,
1136     
1137     getAutoCreate : function(){
1138         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1139         
1140         cfg = {
1141             tag: 'div',
1142             cls: 'column'
1143         };
1144         
1145         var settings=this;
1146         ['xs','sm','md','lg'].map(function(size){
1147             //Roo.log( size + ':' + settings[size]);
1148             
1149             if (settings[size+'off'] !== false) {
1150                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1151             }
1152             
1153             if (settings[size] === false) {
1154                 return;
1155             }
1156             
1157             if (!settings[size]) { // 0 = hidden
1158                 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1159                 return;
1160             }
1161             cfg.cls += ' col-' + size + '-' + settings[size] + (
1162                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1163             );
1164             
1165         });
1166         
1167         if (this.hidden) {
1168             cfg.cls += ' hidden';
1169         }
1170         
1171         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1172             cfg.cls +=' alert alert-' + this.alert;
1173         }
1174         
1175         
1176         if (this.html.length) {
1177             cfg.html = this.html;
1178         }
1179         if (this.fa) {
1180             var fasize = '';
1181             if (this.fasize > 1) {
1182                 fasize = ' fa-' + this.fasize + 'x';
1183             }
1184             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1185             
1186             
1187         }
1188         if (this.icon) {
1189             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1190         }
1191         
1192         return cfg;
1193     }
1194    
1195 });
1196
1197  
1198
1199  /*
1200  * - LGPL
1201  *
1202  * page container.
1203  * 
1204  */
1205
1206
1207 /**
1208  * @class Roo.bootstrap.Container
1209  * @extends Roo.bootstrap.Component
1210  * Bootstrap Container class
1211  * @cfg {Boolean} jumbotron is it a jumbotron element
1212  * @cfg {String} html content of element
1213  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1214  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1215  * @cfg {String} header content of header (for panel)
1216  * @cfg {String} footer content of footer (for panel)
1217  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1218  * @cfg {String} tag (header|aside|section) type of HTML tag.
1219  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1220  * @cfg {String} fa font awesome icon
1221  * @cfg {String} icon (info-sign|check|...) glyphicon name
1222  * @cfg {Boolean} hidden (true|false) hide the element
1223  * @cfg {Boolean} expandable (true|false) default false
1224  * @cfg {Boolean} expanded (true|false) default true
1225  * @cfg {String} rheader contet on the right of header
1226  * @cfg {Boolean} clickable (true|false) default false
1227
1228  *     
1229  * @constructor
1230  * Create a new Container
1231  * @param {Object} config The config object
1232  */
1233
1234 Roo.bootstrap.Container = function(config){
1235     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1236     
1237     this.addEvents({
1238         // raw events
1239          /**
1240          * @event expand
1241          * After the panel has been expand
1242          * 
1243          * @param {Roo.bootstrap.Container} this
1244          */
1245         "expand" : true,
1246         /**
1247          * @event collapse
1248          * After the panel has been collapsed
1249          * 
1250          * @param {Roo.bootstrap.Container} this
1251          */
1252         "collapse" : true,
1253         /**
1254          * @event click
1255          * When a element is chick
1256          * @param {Roo.bootstrap.Container} this
1257          * @param {Roo.EventObject} e
1258          */
1259         "click" : true
1260     });
1261 };
1262
1263 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1264     
1265     jumbotron : false,
1266     well: '',
1267     panel : '',
1268     header: '',
1269     footer : '',
1270     sticky: '',
1271     tag : false,
1272     alert : false,
1273     fa: false,
1274     icon : false,
1275     expandable : false,
1276     rheader : '',
1277     expanded : true,
1278     clickable: false,
1279   
1280      
1281     getChildContainer : function() {
1282         
1283         if(!this.el){
1284             return false;
1285         }
1286         
1287         if (this.panel.length) {
1288             return this.el.select('.panel-body',true).first();
1289         }
1290         
1291         return this.el;
1292     },
1293     
1294     
1295     getAutoCreate : function(){
1296         
1297         var cfg = {
1298             tag : this.tag || 'div',
1299             html : '',
1300             cls : ''
1301         };
1302         if (this.jumbotron) {
1303             cfg.cls = 'jumbotron';
1304         }
1305         
1306         
1307         
1308         // - this is applied by the parent..
1309         //if (this.cls) {
1310         //    cfg.cls = this.cls + '';
1311         //}
1312         
1313         if (this.sticky.length) {
1314             
1315             var bd = Roo.get(document.body);
1316             if (!bd.hasClass('bootstrap-sticky')) {
1317                 bd.addClass('bootstrap-sticky');
1318                 Roo.select('html',true).setStyle('height', '100%');
1319             }
1320              
1321             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1322         }
1323         
1324         
1325         if (this.well.length) {
1326             switch (this.well) {
1327                 case 'lg':
1328                 case 'sm':
1329                     cfg.cls +=' well well-' +this.well;
1330                     break;
1331                 default:
1332                     cfg.cls +=' well';
1333                     break;
1334             }
1335         }
1336         
1337         if (this.hidden) {
1338             cfg.cls += ' hidden';
1339         }
1340         
1341         
1342         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1343             cfg.cls +=' alert alert-' + this.alert;
1344         }
1345         
1346         var body = cfg;
1347         
1348         if (this.panel.length) {
1349             cfg.cls += ' panel panel-' + this.panel;
1350             cfg.cn = [];
1351             if (this.header.length) {
1352                 
1353                 var h = [];
1354                 
1355                 if(this.expandable){
1356                     
1357                     cfg.cls = cfg.cls + ' expandable';
1358                     
1359                     h.push({
1360                         tag: 'i',
1361                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1362                     });
1363                     
1364                 }
1365                 
1366                 h.push(
1367                     {
1368                         tag: 'span',
1369                         cls : 'panel-title',
1370                         html : (this.expandable ? '&nbsp;' : '') + this.header
1371                     },
1372                     {
1373                         tag: 'span',
1374                         cls: 'panel-header-right',
1375                         html: this.rheader
1376                     }
1377                 );
1378                 
1379                 cfg.cn.push({
1380                     cls : 'panel-heading',
1381                     style : this.expandable ? 'cursor: pointer' : '',
1382                     cn : h
1383                 });
1384                 
1385             }
1386             
1387             body = false;
1388             cfg.cn.push({
1389                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1390                 html : this.html
1391             });
1392             
1393             
1394             if (this.footer.length) {
1395                 cfg.cn.push({
1396                     cls : 'panel-footer',
1397                     html : this.footer
1398                     
1399                 });
1400             }
1401             
1402         }
1403         
1404         if (body) {
1405             body.html = this.html || cfg.html;
1406             // prefix with the icons..
1407             if (this.fa) {
1408                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1409             }
1410             if (this.icon) {
1411                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1412             }
1413             
1414             
1415         }
1416         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1417             cfg.cls =  'container';
1418         }
1419         
1420         return cfg;
1421     },
1422     
1423     initEvents: function() 
1424     {
1425         if(this.expandable){
1426             var headerEl = this.headerEl();
1427         
1428             if(headerEl){
1429                 headerEl.on('click', this.onToggleClick, this);
1430             }
1431         }
1432         
1433         if(this.clickable){
1434             this.el.on('click', this.onClick, this);
1435         }
1436         
1437     },
1438     
1439     onToggleClick : function()
1440     {
1441         var headerEl = this.headerEl();
1442         
1443         if(!headerEl){
1444             return;
1445         }
1446         
1447         if(this.expanded){
1448             this.collapse();
1449             return;
1450         }
1451         
1452         this.expand();
1453     },
1454     
1455     expand : function()
1456     {
1457         if(this.fireEvent('expand', this)) {
1458             
1459             this.expanded = true;
1460             
1461             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1462             
1463             this.el.select('.panel-body',true).first().removeClass('hide');
1464             
1465             var toggleEl = this.toggleEl();
1466
1467             if(!toggleEl){
1468                 return;
1469             }
1470
1471             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1472         }
1473         
1474     },
1475     
1476     collapse : function()
1477     {
1478         if(this.fireEvent('collapse', this)) {
1479             
1480             this.expanded = false;
1481             
1482             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1483             this.el.select('.panel-body',true).first().addClass('hide');
1484         
1485             var toggleEl = this.toggleEl();
1486
1487             if(!toggleEl){
1488                 return;
1489             }
1490
1491             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1492         }
1493     },
1494     
1495     toggleEl : function()
1496     {
1497         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1498             return;
1499         }
1500         
1501         return this.el.select('.panel-heading .fa',true).first();
1502     },
1503     
1504     headerEl : function()
1505     {
1506         if(!this.el || !this.panel.length || !this.header.length){
1507             return;
1508         }
1509         
1510         return this.el.select('.panel-heading',true).first()
1511     },
1512     
1513     bodyEl : function()
1514     {
1515         if(!this.el || !this.panel.length){
1516             return;
1517         }
1518         
1519         return this.el.select('.panel-body',true).first()
1520     },
1521     
1522     titleEl : function()
1523     {
1524         if(!this.el || !this.panel.length || !this.header.length){
1525             return;
1526         }
1527         
1528         return this.el.select('.panel-title',true).first();
1529     },
1530     
1531     setTitle : function(v)
1532     {
1533         var titleEl = this.titleEl();
1534         
1535         if(!titleEl){
1536             return;
1537         }
1538         
1539         titleEl.dom.innerHTML = v;
1540     },
1541     
1542     getTitle : function()
1543     {
1544         
1545         var titleEl = this.titleEl();
1546         
1547         if(!titleEl){
1548             return '';
1549         }
1550         
1551         return titleEl.dom.innerHTML;
1552     },
1553     
1554     setRightTitle : function(v)
1555     {
1556         var t = this.el.select('.panel-header-right',true).first();
1557         
1558         if(!t){
1559             return;
1560         }
1561         
1562         t.dom.innerHTML = v;
1563     },
1564     
1565     onClick : function(e)
1566     {
1567         e.preventDefault();
1568         
1569         this.fireEvent('click', this, e);
1570     }
1571 });
1572
1573  /*
1574  * - LGPL
1575  *
1576  * image
1577  * 
1578  */
1579
1580
1581 /**
1582  * @class Roo.bootstrap.Img
1583  * @extends Roo.bootstrap.Component
1584  * Bootstrap Img class
1585  * @cfg {Boolean} imgResponsive false | true
1586  * @cfg {String} border rounded | circle | thumbnail
1587  * @cfg {String} src image source
1588  * @cfg {String} alt image alternative text
1589  * @cfg {String} href a tag href
1590  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1591  * @cfg {String} xsUrl xs image source
1592  * @cfg {String} smUrl sm image source
1593  * @cfg {String} mdUrl md image source
1594  * @cfg {String} lgUrl lg image source
1595  * 
1596  * @constructor
1597  * Create a new Input
1598  * @param {Object} config The config object
1599  */
1600
1601 Roo.bootstrap.Img = function(config){
1602     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1603     
1604     this.addEvents({
1605         // img events
1606         /**
1607          * @event click
1608          * The img click event for the img.
1609          * @param {Roo.EventObject} e
1610          */
1611         "click" : true
1612     });
1613 };
1614
1615 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1616     
1617     imgResponsive: true,
1618     border: '',
1619     src: 'about:blank',
1620     href: false,
1621     target: false,
1622     xsUrl: '',
1623     smUrl: '',
1624     mdUrl: '',
1625     lgUrl: '',
1626
1627     getAutoCreate : function()
1628     {   
1629         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1630             return this.createSingleImg();
1631         }
1632         
1633         var cfg = {
1634             tag: 'div',
1635             cls: 'roo-image-responsive-group',
1636             cn: []
1637         };
1638         var _this = this;
1639         
1640         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1641             
1642             if(!_this[size + 'Url']){
1643                 return;
1644             }
1645             
1646             var img = {
1647                 tag: 'img',
1648                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1649                 html: _this.html || cfg.html,
1650                 src: _this[size + 'Url']
1651             };
1652             
1653             img.cls += ' roo-image-responsive-' + size;
1654             
1655             var s = ['xs', 'sm', 'md', 'lg'];
1656             
1657             s.splice(s.indexOf(size), 1);
1658             
1659             Roo.each(s, function(ss){
1660                 img.cls += ' hidden-' + ss;
1661             });
1662             
1663             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1664                 cfg.cls += ' img-' + _this.border;
1665             }
1666             
1667             if(_this.alt){
1668                 cfg.alt = _this.alt;
1669             }
1670             
1671             if(_this.href){
1672                 var a = {
1673                     tag: 'a',
1674                     href: _this.href,
1675                     cn: [
1676                         img
1677                     ]
1678                 };
1679
1680                 if(this.target){
1681                     a.target = _this.target;
1682                 }
1683             }
1684             
1685             cfg.cn.push((_this.href) ? a : img);
1686             
1687         });
1688         
1689         return cfg;
1690     },
1691     
1692     createSingleImg : function()
1693     {
1694         var cfg = {
1695             tag: 'img',
1696             cls: (this.imgResponsive) ? 'img-responsive' : '',
1697             html : null,
1698             src : 'about:blank'  // just incase src get's set to undefined?!?
1699         };
1700         
1701         cfg.html = this.html || cfg.html;
1702         
1703         cfg.src = this.src || cfg.src;
1704         
1705         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1706             cfg.cls += ' img-' + this.border;
1707         }
1708         
1709         if(this.alt){
1710             cfg.alt = this.alt;
1711         }
1712         
1713         if(this.href){
1714             var a = {
1715                 tag: 'a',
1716                 href: this.href,
1717                 cn: [
1718                     cfg
1719                 ]
1720             };
1721             
1722             if(this.target){
1723                 a.target = this.target;
1724             }
1725             
1726         }
1727         
1728         return (this.href) ? a : cfg;
1729     },
1730     
1731     initEvents: function() 
1732     {
1733         if(!this.href){
1734             this.el.on('click', this.onClick, this);
1735         }
1736         
1737     },
1738     
1739     onClick : function(e)
1740     {
1741         Roo.log('img onclick');
1742         this.fireEvent('click', this, e);
1743     },
1744     /**
1745      * Sets the url of the image - used to update it
1746      * @param {String} url the url of the image
1747      */
1748     
1749     setSrc : function(url)
1750     {
1751         this.src =  url;
1752         
1753         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1754             this.el.dom.src =  url;
1755             return;
1756         }
1757         
1758         this.el.select('img', true).first().dom.src =  url;
1759     }
1760     
1761     
1762    
1763 });
1764
1765  /*
1766  * - LGPL
1767  *
1768  * image
1769  * 
1770  */
1771
1772
1773 /**
1774  * @class Roo.bootstrap.Link
1775  * @extends Roo.bootstrap.Component
1776  * Bootstrap Link Class
1777  * @cfg {String} alt image alternative text
1778  * @cfg {String} href a tag href
1779  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1780  * @cfg {String} html the content of the link.
1781  * @cfg {String} anchor name for the anchor link
1782  * @cfg {String} fa - favicon
1783
1784  * @cfg {Boolean} preventDefault (true | false) default false
1785
1786  * 
1787  * @constructor
1788  * Create a new Input
1789  * @param {Object} config The config object
1790  */
1791
1792 Roo.bootstrap.Link = function(config){
1793     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1794     
1795     this.addEvents({
1796         // img events
1797         /**
1798          * @event click
1799          * The img click event for the img.
1800          * @param {Roo.EventObject} e
1801          */
1802         "click" : true
1803     });
1804 };
1805
1806 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1807     
1808     href: false,
1809     target: false,
1810     preventDefault: false,
1811     anchor : false,
1812     alt : false,
1813     fa: false,
1814
1815
1816     getAutoCreate : function()
1817     {
1818         var html = this.html || '';
1819         
1820         if (this.fa !== false) {
1821             html = '<i class="fa fa-' + this.fa + '"></i>';
1822         }
1823         var cfg = {
1824             tag: 'a'
1825         };
1826         // anchor's do not require html/href...
1827         if (this.anchor === false) {
1828             cfg.html = html;
1829             cfg.href = this.href || '#';
1830         } else {
1831             cfg.name = this.anchor;
1832             if (this.html !== false || this.fa !== false) {
1833                 cfg.html = html;
1834             }
1835             if (this.href !== false) {
1836                 cfg.href = this.href;
1837             }
1838         }
1839         
1840         if(this.alt !== false){
1841             cfg.alt = this.alt;
1842         }
1843         
1844         
1845         if(this.target !== false) {
1846             cfg.target = this.target;
1847         }
1848         
1849         return cfg;
1850     },
1851     
1852     initEvents: function() {
1853         
1854         if(!this.href || this.preventDefault){
1855             this.el.on('click', this.onClick, this);
1856         }
1857     },
1858     
1859     onClick : function(e)
1860     {
1861         if(this.preventDefault){
1862             e.preventDefault();
1863         }
1864         //Roo.log('img onclick');
1865         this.fireEvent('click', this, e);
1866     }
1867    
1868 });
1869
1870  /*
1871  * - LGPL
1872  *
1873  * header
1874  * 
1875  */
1876
1877 /**
1878  * @class Roo.bootstrap.Header
1879  * @extends Roo.bootstrap.Component
1880  * Bootstrap Header class
1881  * @cfg {String} html content of header
1882  * @cfg {Number} level (1|2|3|4|5|6) default 1
1883  * 
1884  * @constructor
1885  * Create a new Header
1886  * @param {Object} config The config object
1887  */
1888
1889
1890 Roo.bootstrap.Header  = function(config){
1891     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1892 };
1893
1894 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1895     
1896     //href : false,
1897     html : false,
1898     level : 1,
1899     
1900     
1901     
1902     getAutoCreate : function(){
1903         
1904         
1905         
1906         var cfg = {
1907             tag: 'h' + (1 *this.level),
1908             html: this.html || ''
1909         } ;
1910         
1911         return cfg;
1912     }
1913    
1914 });
1915
1916  
1917
1918  /*
1919  * Based on:
1920  * Ext JS Library 1.1.1
1921  * Copyright(c) 2006-2007, Ext JS, LLC.
1922  *
1923  * Originally Released Under LGPL - original licence link has changed is not relivant.
1924  *
1925  * Fork - LGPL
1926  * <script type="text/javascript">
1927  */
1928  
1929 /**
1930  * @class Roo.bootstrap.MenuMgr
1931  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1932  * @singleton
1933  */
1934 Roo.bootstrap.MenuMgr = function(){
1935    var menus, active, groups = {}, attached = false, lastShow = new Date();
1936
1937    // private - called when first menu is created
1938    function init(){
1939        menus = {};
1940        active = new Roo.util.MixedCollection();
1941        Roo.get(document).addKeyListener(27, function(){
1942            if(active.length > 0){
1943                hideAll();
1944            }
1945        });
1946    }
1947
1948    // private
1949    function hideAll(){
1950        if(active && active.length > 0){
1951            var c = active.clone();
1952            c.each(function(m){
1953                m.hide();
1954            });
1955        }
1956    }
1957
1958    // private
1959    function onHide(m){
1960        active.remove(m);
1961        if(active.length < 1){
1962            Roo.get(document).un("mouseup", onMouseDown);
1963             
1964            attached = false;
1965        }
1966    }
1967
1968    // private
1969    function onShow(m){
1970        var last = active.last();
1971        lastShow = new Date();
1972        active.add(m);
1973        if(!attached){
1974           Roo.get(document).on("mouseup", onMouseDown);
1975            
1976            attached = true;
1977        }
1978        if(m.parentMenu){
1979           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1980           m.parentMenu.activeChild = m;
1981        }else if(last && last.isVisible()){
1982           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1983        }
1984    }
1985
1986    // private
1987    function onBeforeHide(m){
1988        if(m.activeChild){
1989            m.activeChild.hide();
1990        }
1991        if(m.autoHideTimer){
1992            clearTimeout(m.autoHideTimer);
1993            delete m.autoHideTimer;
1994        }
1995    }
1996
1997    // private
1998    function onBeforeShow(m){
1999        var pm = m.parentMenu;
2000        if(!pm && !m.allowOtherMenus){
2001            hideAll();
2002        }else if(pm && pm.activeChild && active != m){
2003            pm.activeChild.hide();
2004        }
2005    }
2006
2007    // private this should really trigger on mouseup..
2008    function onMouseDown(e){
2009         Roo.log("on Mouse Up");
2010         
2011         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2012             Roo.log("MenuManager hideAll");
2013             hideAll();
2014             e.stopEvent();
2015         }
2016         
2017         
2018    }
2019
2020    // private
2021    function onBeforeCheck(mi, state){
2022        if(state){
2023            var g = groups[mi.group];
2024            for(var i = 0, l = g.length; i < l; i++){
2025                if(g[i] != mi){
2026                    g[i].setChecked(false);
2027                }
2028            }
2029        }
2030    }
2031
2032    return {
2033
2034        /**
2035         * Hides all menus that are currently visible
2036         */
2037        hideAll : function(){
2038             hideAll();  
2039        },
2040
2041        // private
2042        register : function(menu){
2043            if(!menus){
2044                init();
2045            }
2046            menus[menu.id] = menu;
2047            menu.on("beforehide", onBeforeHide);
2048            menu.on("hide", onHide);
2049            menu.on("beforeshow", onBeforeShow);
2050            menu.on("show", onShow);
2051            var g = menu.group;
2052            if(g && menu.events["checkchange"]){
2053                if(!groups[g]){
2054                    groups[g] = [];
2055                }
2056                groups[g].push(menu);
2057                menu.on("checkchange", onCheck);
2058            }
2059        },
2060
2061         /**
2062          * Returns a {@link Roo.menu.Menu} object
2063          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2064          * be used to generate and return a new Menu instance.
2065          */
2066        get : function(menu){
2067            if(typeof menu == "string"){ // menu id
2068                return menus[menu];
2069            }else if(menu.events){  // menu instance
2070                return menu;
2071            }
2072            /*else if(typeof menu.length == 'number'){ // array of menu items?
2073                return new Roo.bootstrap.Menu({items:menu});
2074            }else{ // otherwise, must be a config
2075                return new Roo.bootstrap.Menu(menu);
2076            }
2077            */
2078            return false;
2079        },
2080
2081        // private
2082        unregister : function(menu){
2083            delete menus[menu.id];
2084            menu.un("beforehide", onBeforeHide);
2085            menu.un("hide", onHide);
2086            menu.un("beforeshow", onBeforeShow);
2087            menu.un("show", onShow);
2088            var g = menu.group;
2089            if(g && menu.events["checkchange"]){
2090                groups[g].remove(menu);
2091                menu.un("checkchange", onCheck);
2092            }
2093        },
2094
2095        // private
2096        registerCheckable : function(menuItem){
2097            var g = menuItem.group;
2098            if(g){
2099                if(!groups[g]){
2100                    groups[g] = [];
2101                }
2102                groups[g].push(menuItem);
2103                menuItem.on("beforecheckchange", onBeforeCheck);
2104            }
2105        },
2106
2107        // private
2108        unregisterCheckable : function(menuItem){
2109            var g = menuItem.group;
2110            if(g){
2111                groups[g].remove(menuItem);
2112                menuItem.un("beforecheckchange", onBeforeCheck);
2113            }
2114        }
2115    };
2116 }();/*
2117  * - LGPL
2118  *
2119  * menu
2120  * 
2121  */
2122
2123 /**
2124  * @class Roo.bootstrap.Menu
2125  * @extends Roo.bootstrap.Component
2126  * Bootstrap Menu class - container for MenuItems
2127  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2128  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2129  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2130  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2131  * 
2132  * @constructor
2133  * Create a new Menu
2134  * @param {Object} config The config object
2135  */
2136
2137
2138 Roo.bootstrap.Menu = function(config){
2139     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2140     if (this.registerMenu && this.type != 'treeview')  {
2141         Roo.bootstrap.MenuMgr.register(this);
2142     }
2143     
2144     
2145     this.addEvents({
2146         /**
2147          * @event beforeshow
2148          * Fires before this menu is displayed (return false to block)
2149          * @param {Roo.menu.Menu} this
2150          */
2151         beforeshow : true,
2152         /**
2153          * @event beforehide
2154          * Fires before this menu is hidden (return false to block)
2155          * @param {Roo.menu.Menu} this
2156          */
2157         beforehide : true,
2158         /**
2159          * @event show
2160          * Fires after this menu is displayed
2161          * @param {Roo.menu.Menu} this
2162          */
2163         show : true,
2164         /**
2165          * @event hide
2166          * Fires after this menu is hidden
2167          * @param {Roo.menu.Menu} this
2168          */
2169         hide : true,
2170         /**
2171          * @event click
2172          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2173          * @param {Roo.menu.Menu} this
2174          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2175          * @param {Roo.EventObject} e
2176          */
2177         click : true,
2178         /**
2179          * @event mouseover
2180          * Fires when the mouse is hovering over this menu
2181          * @param {Roo.menu.Menu} this
2182          * @param {Roo.EventObject} e
2183          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2184          */
2185         mouseover : true,
2186         /**
2187          * @event mouseout
2188          * Fires when the mouse exits this menu
2189          * @param {Roo.menu.Menu} this
2190          * @param {Roo.EventObject} e
2191          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2192          */
2193         mouseout : true,
2194         /**
2195          * @event itemclick
2196          * Fires when a menu item contained in this menu is clicked
2197          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2198          * @param {Roo.EventObject} e
2199          */
2200         itemclick: true
2201     });
2202     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2203 };
2204
2205 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2206     
2207    /// html : false,
2208     //align : '',
2209     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2210     type: false,
2211     /**
2212      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2213      */
2214     registerMenu : true,
2215     
2216     menuItems :false, // stores the menu items..
2217     
2218     hidden:true,
2219         
2220     parentMenu : false,
2221     
2222     stopEvent : true,
2223     
2224     isLink : false,
2225     
2226     getChildContainer : function() {
2227         return this.el;  
2228     },
2229     
2230     getAutoCreate : function(){
2231          
2232         //if (['right'].indexOf(this.align)!==-1) {
2233         //    cfg.cn[1].cls += ' pull-right'
2234         //}
2235         
2236         
2237         var cfg = {
2238             tag : 'ul',
2239             cls : 'dropdown-menu' ,
2240             style : 'z-index:1000'
2241             
2242         };
2243         
2244         if (this.type === 'submenu') {
2245             cfg.cls = 'submenu active';
2246         }
2247         if (this.type === 'treeview') {
2248             cfg.cls = 'treeview-menu';
2249         }
2250         
2251         return cfg;
2252     },
2253     initEvents : function() {
2254         
2255        // Roo.log("ADD event");
2256        // Roo.log(this.triggerEl.dom);
2257         
2258         this.triggerEl.on('click', this.onTriggerClick, this);
2259         
2260         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2261         
2262         
2263         if (this.triggerEl.hasClass('nav-item')) {
2264             // dropdown toggle on the 'a' in BS4?
2265             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2266         } else {
2267             this.triggerEl.addClass('dropdown-toggle');
2268         }
2269         if (Roo.isTouch) {
2270             this.el.on('touchstart'  , this.onTouch, this);
2271         }
2272         this.el.on('click' , this.onClick, this);
2273
2274         this.el.on("mouseover", this.onMouseOver, this);
2275         this.el.on("mouseout", this.onMouseOut, this);
2276         
2277     },
2278     
2279     findTargetItem : function(e)
2280     {
2281         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2282         if(!t){
2283             return false;
2284         }
2285         //Roo.log(t);         Roo.log(t.id);
2286         if(t && t.id){
2287             //Roo.log(this.menuitems);
2288             return this.menuitems.get(t.id);
2289             
2290             //return this.items.get(t.menuItemId);
2291         }
2292         
2293         return false;
2294     },
2295     
2296     onTouch : function(e) 
2297     {
2298         Roo.log("menu.onTouch");
2299         //e.stopEvent(); this make the user popdown broken
2300         this.onClick(e);
2301     },
2302     
2303     onClick : function(e)
2304     {
2305         Roo.log("menu.onClick");
2306         
2307         var t = this.findTargetItem(e);
2308         if(!t || t.isContainer){
2309             return;
2310         }
2311         Roo.log(e);
2312         /*
2313         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2314             if(t == this.activeItem && t.shouldDeactivate(e)){
2315                 this.activeItem.deactivate();
2316                 delete this.activeItem;
2317                 return;
2318             }
2319             if(t.canActivate){
2320                 this.setActiveItem(t, true);
2321             }
2322             return;
2323             
2324             
2325         }
2326         */
2327        
2328         Roo.log('pass click event');
2329         
2330         t.onClick(e);
2331         
2332         this.fireEvent("click", this, t, e);
2333         
2334         var _this = this;
2335         
2336         if(!t.href.length || t.href == '#'){
2337             (function() { _this.hide(); }).defer(100);
2338         }
2339         
2340     },
2341     
2342     onMouseOver : function(e){
2343         var t  = this.findTargetItem(e);
2344         //Roo.log(t);
2345         //if(t){
2346         //    if(t.canActivate && !t.disabled){
2347         //        this.setActiveItem(t, true);
2348         //    }
2349         //}
2350         
2351         this.fireEvent("mouseover", this, e, t);
2352     },
2353     isVisible : function(){
2354         return !this.hidden;
2355     },
2356     onMouseOut : function(e){
2357         var t  = this.findTargetItem(e);
2358         
2359         //if(t ){
2360         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2361         //        this.activeItem.deactivate();
2362         //        delete this.activeItem;
2363         //    }
2364         //}
2365         this.fireEvent("mouseout", this, e, t);
2366     },
2367     
2368     
2369     /**
2370      * Displays this menu relative to another element
2371      * @param {String/HTMLElement/Roo.Element} element The element to align to
2372      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2373      * the element (defaults to this.defaultAlign)
2374      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2375      */
2376     show : function(el, pos, parentMenu)
2377     {
2378         if (false === this.fireEvent("beforeshow", this)) {
2379             Roo.log("show canceled");
2380             return;
2381         }
2382         this.parentMenu = parentMenu;
2383         if(!this.el){
2384             this.render();
2385         }
2386         
2387         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2388     },
2389      /**
2390      * Displays this menu at a specific xy position
2391      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2392      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2393      */
2394     showAt : function(xy, parentMenu, /* private: */_e){
2395         this.parentMenu = parentMenu;
2396         if(!this.el){
2397             this.render();
2398         }
2399         if(_e !== false){
2400             this.fireEvent("beforeshow", this);
2401             //xy = this.el.adjustForConstraints(xy);
2402         }
2403         
2404         //this.el.show();
2405         this.hideMenuItems();
2406         this.hidden = false;
2407         this.triggerEl.addClass('open');
2408         this.el.addClass('show');
2409         
2410         // reassign x when hitting right
2411         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2412             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2413         }
2414         
2415         // reassign y when hitting bottom
2416         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2417             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2418         }
2419         
2420         // but the list may align on trigger left or trigger top... should it be a properity?
2421         
2422         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2423             this.el.setXY(xy);
2424         }
2425         
2426         this.focus();
2427         this.fireEvent("show", this);
2428     },
2429     
2430     focus : function(){
2431         return;
2432         if(!this.hidden){
2433             this.doFocus.defer(50, this);
2434         }
2435     },
2436
2437     doFocus : function(){
2438         if(!this.hidden){
2439             this.focusEl.focus();
2440         }
2441     },
2442
2443     /**
2444      * Hides this menu and optionally all parent menus
2445      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2446      */
2447     hide : function(deep)
2448     {
2449         if (false === this.fireEvent("beforehide", this)) {
2450             Roo.log("hide canceled");
2451             return;
2452         }
2453         this.hideMenuItems();
2454         if(this.el && this.isVisible()){
2455            
2456             if(this.activeItem){
2457                 this.activeItem.deactivate();
2458                 this.activeItem = null;
2459             }
2460             this.triggerEl.removeClass('open');;
2461             this.el.removeClass('show');
2462             this.hidden = true;
2463             this.fireEvent("hide", this);
2464         }
2465         if(deep === true && this.parentMenu){
2466             this.parentMenu.hide(true);
2467         }
2468     },
2469     
2470     onTriggerClick : function(e)
2471     {
2472         Roo.log('trigger click');
2473         
2474         var target = e.getTarget();
2475         
2476         Roo.log(target.nodeName.toLowerCase());
2477         
2478         if(target.nodeName.toLowerCase() === 'i'){
2479             e.preventDefault();
2480         }
2481         
2482     },
2483     
2484     onTriggerPress  : function(e)
2485     {
2486         Roo.log('trigger press');
2487         //Roo.log(e.getTarget());
2488        // Roo.log(this.triggerEl.dom);
2489        
2490         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2491         var pel = Roo.get(e.getTarget());
2492         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2493             Roo.log('is treeview or dropdown?');
2494             return;
2495         }
2496         
2497         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2498             return;
2499         }
2500         
2501         if (this.isVisible()) {
2502             Roo.log('hide');
2503             this.hide();
2504         } else {
2505             Roo.log('show');
2506             this.show(this.triggerEl, '?', false);
2507         }
2508         
2509         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2510             e.stopEvent();
2511         }
2512         
2513     },
2514        
2515     
2516     hideMenuItems : function()
2517     {
2518         Roo.log("hide Menu Items");
2519         if (!this.el) { 
2520             return;
2521         }
2522         
2523         this.el.select('.open',true).each(function(aa) {
2524             
2525             aa.removeClass('open');
2526          
2527         });
2528     },
2529     addxtypeChild : function (tree, cntr) {
2530         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2531           
2532         this.menuitems.add(comp);
2533         return comp;
2534
2535     },
2536     getEl : function()
2537     {
2538         Roo.log(this.el);
2539         return this.el;
2540     },
2541     
2542     clear : function()
2543     {
2544         this.getEl().dom.innerHTML = '';
2545         this.menuitems.clear();
2546     }
2547 });
2548
2549  
2550  /*
2551  * - LGPL
2552  *
2553  * menu item
2554  * 
2555  */
2556
2557
2558 /**
2559  * @class Roo.bootstrap.MenuItem
2560  * @extends Roo.bootstrap.Component
2561  * Bootstrap MenuItem class
2562  * @cfg {String} html the menu label
2563  * @cfg {String} href the link
2564  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2565  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2566  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2567  * @cfg {String} fa favicon to show on left of menu item.
2568  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2569  * 
2570  * 
2571  * @constructor
2572  * Create a new MenuItem
2573  * @param {Object} config The config object
2574  */
2575
2576
2577 Roo.bootstrap.MenuItem = function(config){
2578     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2579     this.addEvents({
2580         // raw events
2581         /**
2582          * @event click
2583          * The raw click event for the entire grid.
2584          * @param {Roo.bootstrap.MenuItem} this
2585          * @param {Roo.EventObject} e
2586          */
2587         "click" : true
2588     });
2589 };
2590
2591 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2592     
2593     href : false,
2594     html : false,
2595     preventDefault: false,
2596     isContainer : false,
2597     active : false,
2598     fa: false,
2599     
2600     getAutoCreate : function(){
2601         
2602         if(this.isContainer){
2603             return {
2604                 tag: 'li',
2605                 cls: 'dropdown-menu-item '
2606             };
2607         }
2608         var ctag = {
2609             tag: 'span',
2610             html: 'Link'
2611         };
2612         
2613         var anc = {
2614             tag : 'a',
2615             cls : 'dropdown-item',
2616             href : '#',
2617             cn : [  ]
2618         };
2619         
2620         if (this.fa !== false) {
2621             anc.cn.push({
2622                 tag : 'i',
2623                 cls : 'fa fa-' + this.fa
2624             });
2625         }
2626         
2627         anc.cn.push(ctag);
2628         
2629         
2630         var cfg= {
2631             tag: 'li',
2632             cls: 'dropdown-menu-item',
2633             cn: [ anc ]
2634         };
2635         if (this.parent().type == 'treeview') {
2636             cfg.cls = 'treeview-menu';
2637         }
2638         if (this.active) {
2639             cfg.cls += ' active';
2640         }
2641         
2642         
2643         
2644         anc.href = this.href || cfg.cn[0].href ;
2645         ctag.html = this.html || cfg.cn[0].html ;
2646         return cfg;
2647     },
2648     
2649     initEvents: function()
2650     {
2651         if (this.parent().type == 'treeview') {
2652             this.el.select('a').on('click', this.onClick, this);
2653         }
2654         
2655         if (this.menu) {
2656             this.menu.parentType = this.xtype;
2657             this.menu.triggerEl = this.el;
2658             this.menu = this.addxtype(Roo.apply({}, this.menu));
2659         }
2660         
2661     },
2662     onClick : function(e)
2663     {
2664         Roo.log('item on click ');
2665         
2666         if(this.preventDefault){
2667             e.preventDefault();
2668         }
2669         //this.parent().hideMenuItems();
2670         
2671         this.fireEvent('click', this, e);
2672     },
2673     getEl : function()
2674     {
2675         return this.el;
2676     } 
2677 });
2678
2679  
2680
2681  /*
2682  * - LGPL
2683  *
2684  * menu separator
2685  * 
2686  */
2687
2688
2689 /**
2690  * @class Roo.bootstrap.MenuSeparator
2691  * @extends Roo.bootstrap.Component
2692  * Bootstrap MenuSeparator class
2693  * 
2694  * @constructor
2695  * Create a new MenuItem
2696  * @param {Object} config The config object
2697  */
2698
2699
2700 Roo.bootstrap.MenuSeparator = function(config){
2701     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2702 };
2703
2704 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2705     
2706     getAutoCreate : function(){
2707         var cfg = {
2708             cls: 'divider',
2709             tag : 'li'
2710         };
2711         
2712         return cfg;
2713     }
2714    
2715 });
2716
2717  
2718
2719  
2720 /*
2721 * Licence: LGPL
2722 */
2723
2724 /**
2725  * @class Roo.bootstrap.Modal
2726  * @extends Roo.bootstrap.Component
2727  * Bootstrap Modal class
2728  * @cfg {String} title Title of dialog
2729  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2730  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2731  * @cfg {Boolean} specificTitle default false
2732  * @cfg {Array} buttons Array of buttons or standard button set..
2733  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2734  * @cfg {Boolean} animate default true
2735  * @cfg {Boolean} allow_close default true
2736  * @cfg {Boolean} fitwindow default false
2737  * @cfg {String} size (sm|lg) default empty
2738  * @cfg {Number} max_width set the max width of modal
2739  *
2740  *
2741  * @constructor
2742  * Create a new Modal Dialog
2743  * @param {Object} config The config object
2744  */
2745
2746 Roo.bootstrap.Modal = function(config){
2747     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2748     this.addEvents({
2749         // raw events
2750         /**
2751          * @event btnclick
2752          * The raw btnclick event for the button
2753          * @param {Roo.EventObject} e
2754          */
2755         "btnclick" : true,
2756         /**
2757          * @event resize
2758          * Fire when dialog resize
2759          * @param {Roo.bootstrap.Modal} this
2760          * @param {Roo.EventObject} e
2761          */
2762         "resize" : true
2763     });
2764     this.buttons = this.buttons || [];
2765
2766     if (this.tmpl) {
2767         this.tmpl = Roo.factory(this.tmpl);
2768     }
2769
2770 };
2771
2772 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2773
2774     title : 'test dialog',
2775
2776     buttons : false,
2777
2778     // set on load...
2779
2780     html: false,
2781
2782     tmp: false,
2783
2784     specificTitle: false,
2785
2786     buttonPosition: 'right',
2787
2788     allow_close : true,
2789
2790     animate : true,
2791
2792     fitwindow: false,
2793     
2794      // private
2795     dialogEl: false,
2796     bodyEl:  false,
2797     footerEl:  false,
2798     titleEl:  false,
2799     closeEl:  false,
2800
2801     size: '',
2802     
2803     max_width: 0,
2804     
2805     max_height: 0,
2806     
2807     fit_content: false,
2808
2809     onRender : function(ct, position)
2810     {
2811         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2812
2813         if(!this.el){
2814             var cfg = Roo.apply({},  this.getAutoCreate());
2815             cfg.id = Roo.id();
2816             //if(!cfg.name){
2817             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2818             //}
2819             //if (!cfg.name.length) {
2820             //    delete cfg.name;
2821            // }
2822             if (this.cls) {
2823                 cfg.cls += ' ' + this.cls;
2824             }
2825             if (this.style) {
2826                 cfg.style = this.style;
2827             }
2828             this.el = Roo.get(document.body).createChild(cfg, position);
2829         }
2830         //var type = this.el.dom.type;
2831
2832
2833         if(this.tabIndex !== undefined){
2834             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2835         }
2836
2837         this.dialogEl = this.el.select('.modal-dialog',true).first();
2838         this.bodyEl = this.el.select('.modal-body',true).first();
2839         this.closeEl = this.el.select('.modal-header .close', true).first();
2840         this.headerEl = this.el.select('.modal-header',true).first();
2841         this.titleEl = this.el.select('.modal-title',true).first();
2842         this.footerEl = this.el.select('.modal-footer',true).first();
2843
2844         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2845         
2846         //this.el.addClass("x-dlg-modal");
2847
2848         if (this.buttons.length) {
2849             Roo.each(this.buttons, function(bb) {
2850                 var b = Roo.apply({}, bb);
2851                 b.xns = b.xns || Roo.bootstrap;
2852                 b.xtype = b.xtype || 'Button';
2853                 if (typeof(b.listeners) == 'undefined') {
2854                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2855                 }
2856
2857                 var btn = Roo.factory(b);
2858
2859                 btn.render(this.getButtonContainer());
2860
2861             },this);
2862         }
2863         // render the children.
2864         var nitems = [];
2865
2866         if(typeof(this.items) != 'undefined'){
2867             var items = this.items;
2868             delete this.items;
2869
2870             for(var i =0;i < items.length;i++) {
2871                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2872             }
2873         }
2874
2875         this.items = nitems;
2876
2877         // where are these used - they used to be body/close/footer
2878
2879
2880         this.initEvents();
2881         //this.el.addClass([this.fieldClass, this.cls]);
2882
2883     },
2884
2885     getAutoCreate : function()
2886     {
2887         var bdy = {
2888                 cls : 'modal-body',
2889                 html : this.html || ''
2890         };
2891
2892         var title = {
2893             tag: 'h4',
2894             cls : 'modal-title',
2895             html : this.title
2896         };
2897
2898         if(this.specificTitle){
2899             title = this.title;
2900
2901         }
2902
2903         var header = [];
2904         if (this.allow_close && Roo.bootstrap.version == 3) {
2905             header.push({
2906                 tag: 'button',
2907                 cls : 'close',
2908                 html : '&times'
2909             });
2910         }
2911
2912         header.push(title);
2913
2914         if (this.allow_close && Roo.bootstrap.version == 4) {
2915             header.push({
2916                 tag: 'button',
2917                 cls : 'close',
2918                 html : '&times'
2919             });
2920         }
2921         
2922         var size = '';
2923
2924         if(this.size.length){
2925             size = 'modal-' + this.size;
2926         }
2927         
2928         var footer = Roo.bootstrap.version == 3 ?
2929             {
2930                 cls : 'modal-footer',
2931                 cn : [
2932                     {
2933                         tag: 'div',
2934                         cls: 'btn-' + this.buttonPosition
2935                     }
2936                 ]
2937
2938             } :
2939             {  // BS4 uses mr-auto on left buttons....
2940                 cls : 'modal-footer'
2941             };
2942
2943             
2944
2945         
2946         
2947         var modal = {
2948             cls: "modal",
2949              cn : [
2950                 {
2951                     cls: "modal-dialog " + size,
2952                     cn : [
2953                         {
2954                             cls : "modal-content",
2955                             cn : [
2956                                 {
2957                                     cls : 'modal-header',
2958                                     cn : header
2959                                 },
2960                                 bdy,
2961                                 footer
2962                             ]
2963
2964                         }
2965                     ]
2966
2967                 }
2968             ]
2969         };
2970
2971         if(this.animate){
2972             modal.cls += ' fade';
2973         }
2974
2975         return modal;
2976
2977     },
2978     getChildContainer : function() {
2979
2980          return this.bodyEl;
2981
2982     },
2983     getButtonContainer : function() {
2984         
2985          return Roo.bootstrap.version == 4 ?
2986             this.el.select('.modal-footer',true).first()
2987             : this.el.select('.modal-footer div',true).first();
2988
2989     },
2990     initEvents : function()
2991     {
2992         if (this.allow_close) {
2993             this.closeEl.on('click', this.hide, this);
2994         }
2995         Roo.EventManager.onWindowResize(this.resize, this, true);
2996
2997
2998     },
2999   
3000
3001     resize : function()
3002     {
3003         this.maskEl.setSize(
3004             Roo.lib.Dom.getViewWidth(true),
3005             Roo.lib.Dom.getViewHeight(true)
3006         );
3007         
3008         if (this.fitwindow) {
3009             
3010            
3011             this.setSize(
3012                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3013                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3014             );
3015             return;
3016         }
3017         
3018         if(this.max_width !== 0) {
3019             
3020             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3021             
3022             if(this.height) {
3023                 this.setSize(w, this.height);
3024                 return;
3025             }
3026             
3027             if(this.max_height) {
3028                 this.setSize(w,Math.min(
3029                     this.max_height,
3030                     Roo.lib.Dom.getViewportHeight(true) - 60
3031                 ));
3032                 
3033                 return;
3034             }
3035             
3036             if(!this.fit_content) {
3037                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3038                 return;
3039             }
3040             
3041             this.setSize(w, Math.min(
3042                 60 +
3043                 this.headerEl.getHeight() + 
3044                 this.footerEl.getHeight() + 
3045                 this.getChildHeight(this.bodyEl.dom.childNodes),
3046                 Roo.lib.Dom.getViewportHeight(true) - 60)
3047             );
3048         }
3049         
3050     },
3051
3052     setSize : function(w,h)
3053     {
3054         if (!w && !h) {
3055             return;
3056         }
3057         
3058         this.resizeTo(w,h);
3059     },
3060
3061     show : function() {
3062
3063         if (!this.rendered) {
3064             this.render();
3065         }
3066
3067         //this.el.setStyle('display', 'block');
3068         this.el.removeClass('hideing');
3069         this.el.dom.style.display='block';
3070         
3071         Roo.get(document.body).addClass('modal-open');
3072  
3073         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3074             
3075             (function(){
3076                 this.el.addClass('show');
3077                 this.el.addClass('in');
3078             }).defer(50, this);
3079         }else{
3080             this.el.addClass('show');
3081             this.el.addClass('in');
3082         }
3083
3084         // not sure how we can show data in here..
3085         //if (this.tmpl) {
3086         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3087         //}
3088
3089         Roo.get(document.body).addClass("x-body-masked");
3090         
3091         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3092         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3093         this.maskEl.dom.style.display = 'block';
3094         this.maskEl.addClass('show');
3095         
3096         
3097         this.resize();
3098         
3099         this.fireEvent('show', this);
3100
3101         // set zindex here - otherwise it appears to be ignored...
3102         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3103
3104         (function () {
3105             this.items.forEach( function(e) {
3106                 e.layout ? e.layout() : false;
3107
3108             });
3109         }).defer(100,this);
3110
3111     },
3112     hide : function()
3113     {
3114         if(this.fireEvent("beforehide", this) !== false){
3115             
3116             this.maskEl.removeClass('show');
3117             
3118             this.maskEl.dom.style.display = '';
3119             Roo.get(document.body).removeClass("x-body-masked");
3120             this.el.removeClass('in');
3121             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3122
3123             if(this.animate){ // why
3124                 this.el.addClass('hideing');
3125                 this.el.removeClass('show');
3126                 (function(){
3127                     if (!this.el.hasClass('hideing')) {
3128                         return; // it's been shown again...
3129                     }
3130                     
3131                     this.el.dom.style.display='';
3132
3133                     Roo.get(document.body).removeClass('modal-open');
3134                     this.el.removeClass('hideing');
3135                 }).defer(150,this);
3136                 
3137             }else{
3138                 this.el.removeClass('show');
3139                 this.el.dom.style.display='';
3140                 Roo.get(document.body).removeClass('modal-open');
3141
3142             }
3143             this.fireEvent('hide', this);
3144         }
3145     },
3146     isVisible : function()
3147     {
3148         
3149         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3150         
3151     },
3152
3153     addButton : function(str, cb)
3154     {
3155
3156
3157         var b = Roo.apply({}, { html : str } );
3158         b.xns = b.xns || Roo.bootstrap;
3159         b.xtype = b.xtype || 'Button';
3160         if (typeof(b.listeners) == 'undefined') {
3161             b.listeners = { click : cb.createDelegate(this)  };
3162         }
3163
3164         var btn = Roo.factory(b);
3165
3166         btn.render(this.getButtonContainer());
3167
3168         return btn;
3169
3170     },
3171
3172     setDefaultButton : function(btn)
3173     {
3174         //this.el.select('.modal-footer').()
3175     },
3176
3177     resizeTo: function(w,h)
3178     {
3179         this.dialogEl.setWidth(w);
3180         
3181         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3182
3183         this.bodyEl.setHeight(h - diff);
3184         
3185         this.fireEvent('resize', this);
3186     },
3187     
3188     setContentSize  : function(w, h)
3189     {
3190
3191     },
3192     onButtonClick: function(btn,e)
3193     {
3194         //Roo.log([a,b,c]);
3195         this.fireEvent('btnclick', btn.name, e);
3196     },
3197      /**
3198      * Set the title of the Dialog
3199      * @param {String} str new Title
3200      */
3201     setTitle: function(str) {
3202         this.titleEl.dom.innerHTML = str;
3203     },
3204     /**
3205      * Set the body of the Dialog
3206      * @param {String} str new Title
3207      */
3208     setBody: function(str) {
3209         this.bodyEl.dom.innerHTML = str;
3210     },
3211     /**
3212      * Set the body of the Dialog using the template
3213      * @param {Obj} data - apply this data to the template and replace the body contents.
3214      */
3215     applyBody: function(obj)
3216     {
3217         if (!this.tmpl) {
3218             Roo.log("Error - using apply Body without a template");
3219             //code
3220         }
3221         this.tmpl.overwrite(this.bodyEl, obj);
3222     },
3223     
3224     getChildHeight : function(child_nodes)
3225     {
3226         if(
3227             !child_nodes ||
3228             child_nodes.length == 0
3229         ) {
3230             return;
3231         }
3232         
3233         var child_height = 0;
3234         
3235         for(var i = 0; i < child_nodes.length; i++) {
3236             
3237             /*
3238             * for modal with tabs...
3239             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3240                 
3241                 var layout_childs = child_nodes[i].childNodes;
3242                 
3243                 for(var j = 0; j < layout_childs.length; j++) {
3244                     
3245                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3246                         
3247                         var layout_body_childs = layout_childs[j].childNodes;
3248                         
3249                         for(var k = 0; k < layout_body_childs.length; k++) {
3250                             
3251                             if(layout_body_childs[k].classList.contains('navbar')) {
3252                                 child_height += layout_body_childs[k].offsetHeight;
3253                                 continue;
3254                             }
3255                             
3256                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3257                                 
3258                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3259                                 
3260                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3261                                     
3262                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3263                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3264                                         continue;
3265                                     }
3266                                     
3267                                 }
3268                                 
3269                             }
3270                             
3271                         }
3272                     }
3273                 }
3274                 continue;
3275             }
3276             */
3277             
3278             child_height += child_nodes[i].offsetHeight;
3279             // Roo.log(child_nodes[i].offsetHeight);
3280         }
3281         
3282         return child_height;
3283     }
3284
3285 });
3286
3287
3288 Roo.apply(Roo.bootstrap.Modal,  {
3289     /**
3290          * Button config that displays a single OK button
3291          * @type Object
3292          */
3293         OK :  [{
3294             name : 'ok',
3295             weight : 'primary',
3296             html : 'OK'
3297         }],
3298         /**
3299          * Button config that displays Yes and No buttons
3300          * @type Object
3301          */
3302         YESNO : [
3303             {
3304                 name  : 'no',
3305                 html : 'No'
3306             },
3307             {
3308                 name  :'yes',
3309                 weight : 'primary',
3310                 html : 'Yes'
3311             }
3312         ],
3313
3314         /**
3315          * Button config that displays OK and Cancel buttons
3316          * @type Object
3317          */
3318         OKCANCEL : [
3319             {
3320                name : 'cancel',
3321                 html : 'Cancel'
3322             },
3323             {
3324                 name : 'ok',
3325                 weight : 'primary',
3326                 html : 'OK'
3327             }
3328         ],
3329         /**
3330          * Button config that displays Yes, No and Cancel buttons
3331          * @type Object
3332          */
3333         YESNOCANCEL : [
3334             {
3335                 name : 'yes',
3336                 weight : 'primary',
3337                 html : 'Yes'
3338             },
3339             {
3340                 name : 'no',
3341                 html : 'No'
3342             },
3343             {
3344                 name : 'cancel',
3345                 html : 'Cancel'
3346             }
3347         ],
3348         
3349         zIndex : 10001
3350 });
3351 /*
3352  * - LGPL
3353  *
3354  * messagebox - can be used as a replace
3355  * 
3356  */
3357 /**
3358  * @class Roo.MessageBox
3359  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3360  * Example usage:
3361  *<pre><code>
3362 // Basic alert:
3363 Roo.Msg.alert('Status', 'Changes saved successfully.');
3364
3365 // Prompt for user data:
3366 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3367     if (btn == 'ok'){
3368         // process text value...
3369     }
3370 });
3371
3372 // Show a dialog using config options:
3373 Roo.Msg.show({
3374    title:'Save Changes?',
3375    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3376    buttons: Roo.Msg.YESNOCANCEL,
3377    fn: processResult,
3378    animEl: 'elId'
3379 });
3380 </code></pre>
3381  * @singleton
3382  */
3383 Roo.bootstrap.MessageBox = function(){
3384     var dlg, opt, mask, waitTimer;
3385     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3386     var buttons, activeTextEl, bwidth;
3387
3388     
3389     // private
3390     var handleButton = function(button){
3391         dlg.hide();
3392         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3393     };
3394
3395     // private
3396     var handleHide = function(){
3397         if(opt && opt.cls){
3398             dlg.el.removeClass(opt.cls);
3399         }
3400         //if(waitTimer){
3401         //    Roo.TaskMgr.stop(waitTimer);
3402         //    waitTimer = null;
3403         //}
3404     };
3405
3406     // private
3407     var updateButtons = function(b){
3408         var width = 0;
3409         if(!b){
3410             buttons["ok"].hide();
3411             buttons["cancel"].hide();
3412             buttons["yes"].hide();
3413             buttons["no"].hide();
3414             dlg.footerEl.hide();
3415             
3416             return width;
3417         }
3418         dlg.footerEl.show();
3419         for(var k in buttons){
3420             if(typeof buttons[k] != "function"){
3421                 if(b[k]){
3422                     buttons[k].show();
3423                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3424                     width += buttons[k].el.getWidth()+15;
3425                 }else{
3426                     buttons[k].hide();
3427                 }
3428             }
3429         }
3430         return width;
3431     };
3432
3433     // private
3434     var handleEsc = function(d, k, e){
3435         if(opt && opt.closable !== false){
3436             dlg.hide();
3437         }
3438         if(e){
3439             e.stopEvent();
3440         }
3441     };
3442
3443     return {
3444         /**
3445          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3446          * @return {Roo.BasicDialog} The BasicDialog element
3447          */
3448         getDialog : function(){
3449            if(!dlg){
3450                 dlg = new Roo.bootstrap.Modal( {
3451                     //draggable: true,
3452                     //resizable:false,
3453                     //constraintoviewport:false,
3454                     //fixedcenter:true,
3455                     //collapsible : false,
3456                     //shim:true,
3457                     //modal: true,
3458                 //    width: 'auto',
3459                   //  height:100,
3460                     //buttonAlign:"center",
3461                     closeClick : function(){
3462                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3463                             handleButton("no");
3464                         }else{
3465                             handleButton("cancel");
3466                         }
3467                     }
3468                 });
3469                 dlg.render();
3470                 dlg.on("hide", handleHide);
3471                 mask = dlg.mask;
3472                 //dlg.addKeyListener(27, handleEsc);
3473                 buttons = {};
3474                 this.buttons = buttons;
3475                 var bt = this.buttonText;
3476                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3477                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3478                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3479                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3480                 //Roo.log(buttons);
3481                 bodyEl = dlg.bodyEl.createChild({
3482
3483                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3484                         '<textarea class="roo-mb-textarea"></textarea>' +
3485                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3486                 });
3487                 msgEl = bodyEl.dom.firstChild;
3488                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3489                 textboxEl.enableDisplayMode();
3490                 textboxEl.addKeyListener([10,13], function(){
3491                     if(dlg.isVisible() && opt && opt.buttons){
3492                         if(opt.buttons.ok){
3493                             handleButton("ok");
3494                         }else if(opt.buttons.yes){
3495                             handleButton("yes");
3496                         }
3497                     }
3498                 });
3499                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3500                 textareaEl.enableDisplayMode();
3501                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3502                 progressEl.enableDisplayMode();
3503                 
3504                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3505                 var pf = progressEl.dom.firstChild;
3506                 if (pf) {
3507                     pp = Roo.get(pf.firstChild);
3508                     pp.setHeight(pf.offsetHeight);
3509                 }
3510                 
3511             }
3512             return dlg;
3513         },
3514
3515         /**
3516          * Updates the message box body text
3517          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3518          * the XHTML-compliant non-breaking space character '&amp;#160;')
3519          * @return {Roo.MessageBox} This message box
3520          */
3521         updateText : function(text)
3522         {
3523             if(!dlg.isVisible() && !opt.width){
3524                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3525                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3526             }
3527             msgEl.innerHTML = text || '&#160;';
3528       
3529             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3530             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3531             var w = Math.max(
3532                     Math.min(opt.width || cw , this.maxWidth), 
3533                     Math.max(opt.minWidth || this.minWidth, bwidth)
3534             );
3535             if(opt.prompt){
3536                 activeTextEl.setWidth(w);
3537             }
3538             if(dlg.isVisible()){
3539                 dlg.fixedcenter = false;
3540             }
3541             // to big, make it scroll. = But as usual stupid IE does not support
3542             // !important..
3543             
3544             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3545                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3546                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3547             } else {
3548                 bodyEl.dom.style.height = '';
3549                 bodyEl.dom.style.overflowY = '';
3550             }
3551             if (cw > w) {
3552                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3553             } else {
3554                 bodyEl.dom.style.overflowX = '';
3555             }
3556             
3557             dlg.setContentSize(w, bodyEl.getHeight());
3558             if(dlg.isVisible()){
3559                 dlg.fixedcenter = true;
3560             }
3561             return this;
3562         },
3563
3564         /**
3565          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3566          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3567          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3568          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3569          * @return {Roo.MessageBox} This message box
3570          */
3571         updateProgress : function(value, text){
3572             if(text){
3573                 this.updateText(text);
3574             }
3575             
3576             if (pp) { // weird bug on my firefox - for some reason this is not defined
3577                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3578                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3579             }
3580             return this;
3581         },        
3582
3583         /**
3584          * Returns true if the message box is currently displayed
3585          * @return {Boolean} True if the message box is visible, else false
3586          */
3587         isVisible : function(){
3588             return dlg && dlg.isVisible();  
3589         },
3590
3591         /**
3592          * Hides the message box if it is displayed
3593          */
3594         hide : function(){
3595             if(this.isVisible()){
3596                 dlg.hide();
3597             }  
3598         },
3599
3600         /**
3601          * Displays a new message box, or reinitializes an existing message box, based on the config options
3602          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3603          * The following config object properties are supported:
3604          * <pre>
3605 Property    Type             Description
3606 ----------  ---------------  ------------------------------------------------------------------------------------
3607 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3608                                    closes (defaults to undefined)
3609 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3610                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3611 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3612                                    progress and wait dialogs will ignore this property and always hide the
3613                                    close button as they can only be closed programmatically.
3614 cls               String           A custom CSS class to apply to the message box element
3615 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3616                                    displayed (defaults to 75)
3617 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3618                                    function will be btn (the name of the button that was clicked, if applicable,
3619                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3620                                    Progress and wait dialogs will ignore this option since they do not respond to
3621                                    user actions and can only be closed programmatically, so any required function
3622                                    should be called by the same code after it closes the dialog.
3623 icon              String           A CSS class that provides a background image to be used as an icon for
3624                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3625 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3626 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3627 modal             Boolean          False to allow user interaction with the page while the message box is
3628                                    displayed (defaults to true)
3629 msg               String           A string that will replace the existing message box body text (defaults
3630                                    to the XHTML-compliant non-breaking space character '&#160;')
3631 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3632 progress          Boolean          True to display a progress bar (defaults to false)
3633 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3634 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3635 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3636 title             String           The title text
3637 value             String           The string value to set into the active textbox element if displayed
3638 wait              Boolean          True to display a progress bar (defaults to false)
3639 width             Number           The width of the dialog in pixels
3640 </pre>
3641          *
3642          * Example usage:
3643          * <pre><code>
3644 Roo.Msg.show({
3645    title: 'Address',
3646    msg: 'Please enter your address:',
3647    width: 300,
3648    buttons: Roo.MessageBox.OKCANCEL,
3649    multiline: true,
3650    fn: saveAddress,
3651    animEl: 'addAddressBtn'
3652 });
3653 </code></pre>
3654          * @param {Object} config Configuration options
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         show : function(options)
3658         {
3659             
3660             // this causes nightmares if you show one dialog after another
3661             // especially on callbacks..
3662              
3663             if(this.isVisible()){
3664                 
3665                 this.hide();
3666                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3667                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3668                 Roo.log("New Dialog Message:" +  options.msg )
3669                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3670                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3671                 
3672             }
3673             var d = this.getDialog();
3674             opt = options;
3675             d.setTitle(opt.title || "&#160;");
3676             d.closeEl.setDisplayed(opt.closable !== false);
3677             activeTextEl = textboxEl;
3678             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3679             if(opt.prompt){
3680                 if(opt.multiline){
3681                     textboxEl.hide();
3682                     textareaEl.show();
3683                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3684                         opt.multiline : this.defaultTextHeight);
3685                     activeTextEl = textareaEl;
3686                 }else{
3687                     textboxEl.show();
3688                     textareaEl.hide();
3689                 }
3690             }else{
3691                 textboxEl.hide();
3692                 textareaEl.hide();
3693             }
3694             progressEl.setDisplayed(opt.progress === true);
3695             if (opt.progress) {
3696                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3697             }
3698             this.updateProgress(0);
3699             activeTextEl.dom.value = opt.value || "";
3700             if(opt.prompt){
3701                 dlg.setDefaultButton(activeTextEl);
3702             }else{
3703                 var bs = opt.buttons;
3704                 var db = null;
3705                 if(bs && bs.ok){
3706                     db = buttons["ok"];
3707                 }else if(bs && bs.yes){
3708                     db = buttons["yes"];
3709                 }
3710                 dlg.setDefaultButton(db);
3711             }
3712             bwidth = updateButtons(opt.buttons);
3713             this.updateText(opt.msg);
3714             if(opt.cls){
3715                 d.el.addClass(opt.cls);
3716             }
3717             d.proxyDrag = opt.proxyDrag === true;
3718             d.modal = opt.modal !== false;
3719             d.mask = opt.modal !== false ? mask : false;
3720             if(!d.isVisible()){
3721                 // force it to the end of the z-index stack so it gets a cursor in FF
3722                 document.body.appendChild(dlg.el.dom);
3723                 d.animateTarget = null;
3724                 d.show(options.animEl);
3725             }
3726             return this;
3727         },
3728
3729         /**
3730          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3731          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3732          * and closing the message box when the process is complete.
3733          * @param {String} title The title bar text
3734          * @param {String} msg The message box body text
3735          * @return {Roo.MessageBox} This message box
3736          */
3737         progress : function(title, msg){
3738             this.show({
3739                 title : title,
3740                 msg : msg,
3741                 buttons: false,
3742                 progress:true,
3743                 closable:false,
3744                 minWidth: this.minProgressWidth,
3745                 modal : true
3746             });
3747             return this;
3748         },
3749
3750         /**
3751          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3752          * If a callback function is passed it will be called after the user clicks the button, and the
3753          * id of the button that was clicked will be passed as the only parameter to the callback
3754          * (could also be the top-right close button).
3755          * @param {String} title The title bar text
3756          * @param {String} msg The message box body text
3757          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3758          * @param {Object} scope (optional) The scope of the callback function
3759          * @return {Roo.MessageBox} This message box
3760          */
3761         alert : function(title, msg, fn, scope)
3762         {
3763             this.show({
3764                 title : title,
3765                 msg : msg,
3766                 buttons: this.OK,
3767                 fn: fn,
3768                 closable : false,
3769                 scope : scope,
3770                 modal : true
3771             });
3772             return this;
3773         },
3774
3775         /**
3776          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3777          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3778          * You are responsible for closing the message box when the process is complete.
3779          * @param {String} msg The message box body text
3780          * @param {String} title (optional) The title bar text
3781          * @return {Roo.MessageBox} This message box
3782          */
3783         wait : function(msg, title){
3784             this.show({
3785                 title : title,
3786                 msg : msg,
3787                 buttons: false,
3788                 closable:false,
3789                 progress:true,
3790                 modal:true,
3791                 width:300,
3792                 wait:true
3793             });
3794             waitTimer = Roo.TaskMgr.start({
3795                 run: function(i){
3796                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3797                 },
3798                 interval: 1000
3799             });
3800             return this;
3801         },
3802
3803         /**
3804          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3805          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3806          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3807          * @param {String} title The title bar text
3808          * @param {String} msg The message box body text
3809          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3810          * @param {Object} scope (optional) The scope of the callback function
3811          * @return {Roo.MessageBox} This message box
3812          */
3813         confirm : function(title, msg, fn, scope){
3814             this.show({
3815                 title : title,
3816                 msg : msg,
3817                 buttons: this.YESNO,
3818                 fn: fn,
3819                 scope : scope,
3820                 modal : true
3821             });
3822             return this;
3823         },
3824
3825         /**
3826          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3827          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3828          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3829          * (could also be the top-right close button) and the text that was entered will be passed as the two
3830          * parameters to the callback.
3831          * @param {String} title The title bar text
3832          * @param {String} msg The message box body text
3833          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3834          * @param {Object} scope (optional) The scope of the callback function
3835          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3836          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3837          * @return {Roo.MessageBox} This message box
3838          */
3839         prompt : function(title, msg, fn, scope, multiline){
3840             this.show({
3841                 title : title,
3842                 msg : msg,
3843                 buttons: this.OKCANCEL,
3844                 fn: fn,
3845                 minWidth:250,
3846                 scope : scope,
3847                 prompt:true,
3848                 multiline: multiline,
3849                 modal : true
3850             });
3851             return this;
3852         },
3853
3854         /**
3855          * Button config that displays a single OK button
3856          * @type Object
3857          */
3858         OK : {ok:true},
3859         /**
3860          * Button config that displays Yes and No buttons
3861          * @type Object
3862          */
3863         YESNO : {yes:true, no:true},
3864         /**
3865          * Button config that displays OK and Cancel buttons
3866          * @type Object
3867          */
3868         OKCANCEL : {ok:true, cancel:true},
3869         /**
3870          * Button config that displays Yes, No and Cancel buttons
3871          * @type Object
3872          */
3873         YESNOCANCEL : {yes:true, no:true, cancel:true},
3874
3875         /**
3876          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3877          * @type Number
3878          */
3879         defaultTextHeight : 75,
3880         /**
3881          * The maximum width in pixels of the message box (defaults to 600)
3882          * @type Number
3883          */
3884         maxWidth : 600,
3885         /**
3886          * The minimum width in pixels of the message box (defaults to 100)
3887          * @type Number
3888          */
3889         minWidth : 100,
3890         /**
3891          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3892          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3893          * @type Number
3894          */
3895         minProgressWidth : 250,
3896         /**
3897          * An object containing the default button text strings that can be overriden for localized language support.
3898          * Supported properties are: ok, cancel, yes and no.
3899          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3900          * @type Object
3901          */
3902         buttonText : {
3903             ok : "OK",
3904             cancel : "Cancel",
3905             yes : "Yes",
3906             no : "No"
3907         }
3908     };
3909 }();
3910
3911 /**
3912  * Shorthand for {@link Roo.MessageBox}
3913  */
3914 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3915 Roo.Msg = Roo.Msg || Roo.MessageBox;
3916 /*
3917  * - LGPL
3918  *
3919  * navbar
3920  * 
3921  */
3922
3923 /**
3924  * @class Roo.bootstrap.Navbar
3925  * @extends Roo.bootstrap.Component
3926  * Bootstrap Navbar class
3927
3928  * @constructor
3929  * Create a new Navbar
3930  * @param {Object} config The config object
3931  */
3932
3933
3934 Roo.bootstrap.Navbar = function(config){
3935     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3936     this.addEvents({
3937         // raw events
3938         /**
3939          * @event beforetoggle
3940          * Fire before toggle the menu
3941          * @param {Roo.EventObject} e
3942          */
3943         "beforetoggle" : true
3944     });
3945 };
3946
3947 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3948     
3949     
3950    
3951     // private
3952     navItems : false,
3953     loadMask : false,
3954     
3955     
3956     getAutoCreate : function(){
3957         
3958         
3959         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3960         
3961     },
3962     
3963     initEvents :function ()
3964     {
3965         //Roo.log(this.el.select('.navbar-toggle',true));
3966         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3967         
3968         var mark = {
3969             tag: "div",
3970             cls:"x-dlg-mask"
3971         };
3972         
3973         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3974         
3975         var size = this.el.getSize();
3976         this.maskEl.setSize(size.width, size.height);
3977         this.maskEl.enableDisplayMode("block");
3978         this.maskEl.hide();
3979         
3980         if(this.loadMask){
3981             this.maskEl.show();
3982         }
3983     },
3984     
3985     
3986     getChildContainer : function()
3987     {
3988         if (this.el && this.el.select('.collapse').getCount()) {
3989             return this.el.select('.collapse',true).first();
3990         }
3991         
3992         return this.el;
3993     },
3994     
3995     mask : function()
3996     {
3997         this.maskEl.show();
3998     },
3999     
4000     unmask : function()
4001     {
4002         this.maskEl.hide();
4003     },
4004     onToggle : function()
4005     {
4006         
4007         if(this.fireEvent('beforetoggle', this) === false){
4008             return;
4009         }
4010         var ce = this.el.select('.navbar-collapse',true).first();
4011       
4012         if (!ce.hasClass('show')) {
4013            this.expand();
4014         } else {
4015             this.collapse();
4016         }
4017         
4018         
4019     
4020     },
4021     /**
4022      * Expand the navbar pulldown 
4023      */
4024     expand : function ()
4025     {
4026        
4027         var ce = this.el.select('.navbar-collapse',true).first();
4028         if (ce.hasClass('collapsing')) {
4029             return;
4030         }
4031         ce.dom.style.height = '';
4032                // show it...
4033         ce.addClass('in'); // old...
4034         ce.removeClass('collapse');
4035         ce.addClass('show');
4036         var h = ce.getHeight();
4037         Roo.log(h);
4038         ce.removeClass('show');
4039         // at this point we should be able to see it..
4040         ce.addClass('collapsing');
4041         
4042         ce.setHeight(0); // resize it ...
4043         ce.on('transitionend', function() {
4044             //Roo.log('done transition');
4045             ce.removeClass('collapsing');
4046             ce.addClass('show');
4047             ce.removeClass('collapse');
4048
4049             ce.dom.style.height = '';
4050         }, this, { single: true} );
4051         ce.setHeight(h);
4052         ce.dom.scrollTop = 0;
4053     },
4054     /**
4055      * Collapse the navbar pulldown 
4056      */
4057     collapse : function()
4058     {
4059          var ce = this.el.select('.navbar-collapse',true).first();
4060        
4061         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4062             // it's collapsed or collapsing..
4063             return;
4064         }
4065         ce.removeClass('in'); // old...
4066         ce.setHeight(ce.getHeight());
4067         ce.removeClass('show');
4068         ce.addClass('collapsing');
4069         
4070         ce.on('transitionend', function() {
4071             ce.dom.style.height = '';
4072             ce.removeClass('collapsing');
4073             ce.addClass('collapse');
4074         }, this, { single: true} );
4075         ce.setHeight(0);
4076     }
4077     
4078     
4079     
4080 });
4081
4082
4083
4084  
4085
4086  /*
4087  * - LGPL
4088  *
4089  * navbar
4090  * 
4091  */
4092
4093 /**
4094  * @class Roo.bootstrap.NavSimplebar
4095  * @extends Roo.bootstrap.Navbar
4096  * Bootstrap Sidebar class
4097  *
4098  * @cfg {Boolean} inverse is inverted color
4099  * 
4100  * @cfg {String} type (nav | pills | tabs)
4101  * @cfg {Boolean} arrangement stacked | justified
4102  * @cfg {String} align (left | right) alignment
4103  * 
4104  * @cfg {Boolean} main (true|false) main nav bar? default false
4105  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4106  * 
4107  * @cfg {String} tag (header|footer|nav|div) default is nav 
4108
4109  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4110  * 
4111  * 
4112  * @constructor
4113  * Create a new Sidebar
4114  * @param {Object} config The config object
4115  */
4116
4117
4118 Roo.bootstrap.NavSimplebar = function(config){
4119     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4120 };
4121
4122 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4123     
4124     inverse: false,
4125     
4126     type: false,
4127     arrangement: '',
4128     align : false,
4129     
4130     weight : 'light',
4131     
4132     main : false,
4133     
4134     
4135     tag : false,
4136     
4137     
4138     getAutoCreate : function(){
4139         
4140         
4141         var cfg = {
4142             tag : this.tag || 'div',
4143             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4144         };
4145         if (['light','white'].indexOf(this.weight) > -1) {
4146             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4147         }
4148         cfg.cls += ' bg-' + this.weight;
4149         
4150         if (this.inverse) {
4151             cfg.cls += ' navbar-inverse';
4152             
4153         }
4154         
4155         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4156         
4157         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4158             return cfg;
4159         }
4160         
4161         
4162     
4163         
4164         cfg.cn = [
4165             {
4166                 cls: 'nav nav-' + this.xtype,
4167                 tag : 'ul'
4168             }
4169         ];
4170         
4171          
4172         this.type = this.type || 'nav';
4173         if (['tabs','pills'].indexOf(this.type) != -1) {
4174             cfg.cn[0].cls += ' nav-' + this.type
4175         
4176         
4177         } else {
4178             if (this.type!=='nav') {
4179                 Roo.log('nav type must be nav/tabs/pills')
4180             }
4181             cfg.cn[0].cls += ' navbar-nav'
4182         }
4183         
4184         
4185         
4186         
4187         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4188             cfg.cn[0].cls += ' nav-' + this.arrangement;
4189         }
4190         
4191         
4192         if (this.align === 'right') {
4193             cfg.cn[0].cls += ' navbar-right';
4194         }
4195         
4196         
4197         
4198         
4199         return cfg;
4200     
4201         
4202     }
4203     
4204     
4205     
4206 });
4207
4208
4209
4210  
4211
4212  
4213        /*
4214  * - LGPL
4215  *
4216  * navbar
4217  * navbar-fixed-top
4218  * navbar-expand-md  fixed-top 
4219  */
4220
4221 /**
4222  * @class Roo.bootstrap.NavHeaderbar
4223  * @extends Roo.bootstrap.NavSimplebar
4224  * Bootstrap Sidebar class
4225  *
4226  * @cfg {String} brand what is brand
4227  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4228  * @cfg {String} brand_href href of the brand
4229  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4230  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4231  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4232  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4233  * 
4234  * @constructor
4235  * Create a new Sidebar
4236  * @param {Object} config The config object
4237  */
4238
4239
4240 Roo.bootstrap.NavHeaderbar = function(config){
4241     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4242       
4243 };
4244
4245 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4246     
4247     position: '',
4248     brand: '',
4249     brand_href: false,
4250     srButton : true,
4251     autohide : false,
4252     desktopCenter : false,
4253    
4254     
4255     getAutoCreate : function(){
4256         
4257         var   cfg = {
4258             tag: this.nav || 'nav',
4259             cls: 'navbar navbar-expand-md',
4260             role: 'navigation',
4261             cn: []
4262         };
4263         
4264         var cn = cfg.cn;
4265         if (this.desktopCenter) {
4266             cn.push({cls : 'container', cn : []});
4267             cn = cn[0].cn;
4268         }
4269         
4270         if(this.srButton){
4271             var btn = {
4272                 tag: 'button',
4273                 type: 'button',
4274                 cls: 'navbar-toggle navbar-toggler',
4275                 'data-toggle': 'collapse',
4276                 cn: [
4277                     {
4278                         tag: 'span',
4279                         cls: 'sr-only',
4280                         html: 'Toggle navigation'
4281                     },
4282                     {
4283                         tag: 'span',
4284                         cls: 'icon-bar navbar-toggler-icon'
4285                     },
4286                     {
4287                         tag: 'span',
4288                         cls: 'icon-bar'
4289                     },
4290                     {
4291                         tag: 'span',
4292                         cls: 'icon-bar'
4293                     }
4294                 ]
4295             };
4296             
4297             cn.push( Roo.bootstrap.version == 4 ? btn : {
4298                 tag: 'div',
4299                 cls: 'navbar-header',
4300                 cn: [
4301                     btn
4302                 ]
4303             });
4304         }
4305         
4306         cn.push({
4307             tag: 'div',
4308             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4309             cn : []
4310         });
4311         
4312         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4313         
4314         if (['light','white'].indexOf(this.weight) > -1) {
4315             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4316         }
4317         cfg.cls += ' bg-' + this.weight;
4318         
4319         
4320         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4321             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4322             
4323             // tag can override this..
4324             
4325             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4326         }
4327         
4328         if (this.brand !== '') {
4329             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4330             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4331                 tag: 'a',
4332                 href: this.brand_href ? this.brand_href : '#',
4333                 cls: 'navbar-brand',
4334                 cn: [
4335                 this.brand
4336                 ]
4337             });
4338         }
4339         
4340         if(this.main){
4341             cfg.cls += ' main-nav';
4342         }
4343         
4344         
4345         return cfg;
4346
4347         
4348     },
4349     getHeaderChildContainer : function()
4350     {
4351         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4352             return this.el.select('.navbar-header',true).first();
4353         }
4354         
4355         return this.getChildContainer();
4356     },
4357     
4358     
4359     initEvents : function()
4360     {
4361         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4362         
4363         if (this.autohide) {
4364             
4365             var prevScroll = 0;
4366             var ft = this.el;
4367             
4368             Roo.get(document).on('scroll',function(e) {
4369                 var ns = Roo.get(document).getScroll().top;
4370                 var os = prevScroll;
4371                 prevScroll = ns;
4372                 
4373                 if(ns > os){
4374                     ft.removeClass('slideDown');
4375                     ft.addClass('slideUp');
4376                     return;
4377                 }
4378                 ft.removeClass('slideUp');
4379                 ft.addClass('slideDown');
4380                  
4381               
4382           },this);
4383         }
4384     }    
4385     
4386 });
4387
4388
4389
4390  
4391
4392  /*
4393  * - LGPL
4394  *
4395  * navbar
4396  * 
4397  */
4398
4399 /**
4400  * @class Roo.bootstrap.NavSidebar
4401  * @extends Roo.bootstrap.Navbar
4402  * Bootstrap Sidebar class
4403  * 
4404  * @constructor
4405  * Create a new Sidebar
4406  * @param {Object} config The config object
4407  */
4408
4409
4410 Roo.bootstrap.NavSidebar = function(config){
4411     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4412 };
4413
4414 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4415     
4416     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4417     
4418     getAutoCreate : function(){
4419         
4420         
4421         return  {
4422             tag: 'div',
4423             cls: 'sidebar sidebar-nav'
4424         };
4425     
4426         
4427     }
4428     
4429     
4430     
4431 });
4432
4433
4434
4435  
4436
4437  /*
4438  * - LGPL
4439  *
4440  * nav group
4441  * 
4442  */
4443
4444 /**
4445  * @class Roo.bootstrap.NavGroup
4446  * @extends Roo.bootstrap.Component
4447  * Bootstrap NavGroup class
4448  * @cfg {String} align (left|right)
4449  * @cfg {Boolean} inverse
4450  * @cfg {String} type (nav|pills|tab) default nav
4451  * @cfg {String} navId - reference Id for navbar.
4452
4453  * 
4454  * @constructor
4455  * Create a new nav group
4456  * @param {Object} config The config object
4457  */
4458
4459 Roo.bootstrap.NavGroup = function(config){
4460     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4461     this.navItems = [];
4462    
4463     Roo.bootstrap.NavGroup.register(this);
4464      this.addEvents({
4465         /**
4466              * @event changed
4467              * Fires when the active item changes
4468              * @param {Roo.bootstrap.NavGroup} this
4469              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4470              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4471          */
4472         'changed': true
4473      });
4474     
4475 };
4476
4477 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4478     
4479     align: '',
4480     inverse: false,
4481     form: false,
4482     type: 'nav',
4483     navId : '',
4484     // private
4485     
4486     navItems : false, 
4487     
4488     getAutoCreate : function()
4489     {
4490         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4491         
4492         cfg = {
4493             tag : 'ul',
4494             cls: 'nav' 
4495         };
4496         if (Roo.bootstrap.version == 4) {
4497             if (['tabs','pills'].indexOf(this.type) != -1) {
4498                 cfg.cls += ' nav-' + this.type; 
4499             } else {
4500                 // trying to remove so header bar can right align top?
4501                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4502                     // do not use on header bar... 
4503                     cfg.cls += ' navbar-nav';
4504                 }
4505             }
4506             
4507         } else {
4508             if (['tabs','pills'].indexOf(this.type) != -1) {
4509                 cfg.cls += ' nav-' + this.type
4510             } else {
4511                 if (this.type !== 'nav') {
4512                     Roo.log('nav type must be nav/tabs/pills')
4513                 }
4514                 cfg.cls += ' navbar-nav'
4515             }
4516         }
4517         
4518         if (this.parent() && this.parent().sidebar) {
4519             cfg = {
4520                 tag: 'ul',
4521                 cls: 'dashboard-menu sidebar-menu'
4522             };
4523             
4524             return cfg;
4525         }
4526         
4527         if (this.form === true) {
4528             cfg = {
4529                 tag: 'form',
4530                 cls: 'navbar-form form-inline'
4531             };
4532             //nav navbar-right ml-md-auto
4533             if (this.align === 'right') {
4534                 cfg.cls += ' navbar-right ml-md-auto';
4535             } else {
4536                 cfg.cls += ' navbar-left';
4537             }
4538         }
4539         
4540         if (this.align === 'right') {
4541             cfg.cls += ' navbar-right ml-md-auto';
4542         } else {
4543             cfg.cls += ' mr-auto';
4544         }
4545         
4546         if (this.inverse) {
4547             cfg.cls += ' navbar-inverse';
4548             
4549         }
4550         
4551         
4552         return cfg;
4553     },
4554     /**
4555     * sets the active Navigation item
4556     * @param {Roo.bootstrap.NavItem} the new current navitem
4557     */
4558     setActiveItem : function(item)
4559     {
4560         var prev = false;
4561         Roo.each(this.navItems, function(v){
4562             if (v == item) {
4563                 return ;
4564             }
4565             if (v.isActive()) {
4566                 v.setActive(false, true);
4567                 prev = v;
4568                 
4569             }
4570             
4571         });
4572
4573         item.setActive(true, true);
4574         this.fireEvent('changed', this, item, prev);
4575         
4576         
4577     },
4578     /**
4579     * gets the active Navigation item
4580     * @return {Roo.bootstrap.NavItem} the current navitem
4581     */
4582     getActive : function()
4583     {
4584         
4585         var prev = false;
4586         Roo.each(this.navItems, function(v){
4587             
4588             if (v.isActive()) {
4589                 prev = v;
4590                 
4591             }
4592             
4593         });
4594         return prev;
4595     },
4596     
4597     indexOfNav : function()
4598     {
4599         
4600         var prev = false;
4601         Roo.each(this.navItems, function(v,i){
4602             
4603             if (v.isActive()) {
4604                 prev = i;
4605                 
4606             }
4607             
4608         });
4609         return prev;
4610     },
4611     /**
4612     * adds a Navigation item
4613     * @param {Roo.bootstrap.NavItem} the navitem to add
4614     */
4615     addItem : function(cfg)
4616     {
4617         if (this.form && Roo.bootstrap.version == 4) {
4618             cfg.tag = 'div';
4619         }
4620         var cn = new Roo.bootstrap.NavItem(cfg);
4621         this.register(cn);
4622         cn.parentId = this.id;
4623         cn.onRender(this.el, null);
4624         return cn;
4625     },
4626     /**
4627     * register a Navigation item
4628     * @param {Roo.bootstrap.NavItem} the navitem to add
4629     */
4630     register : function(item)
4631     {
4632         this.navItems.push( item);
4633         item.navId = this.navId;
4634     
4635     },
4636     
4637     /**
4638     * clear all the Navigation item
4639     */
4640    
4641     clearAll : function()
4642     {
4643         this.navItems = [];
4644         this.el.dom.innerHTML = '';
4645     },
4646     
4647     getNavItem: function(tabId)
4648     {
4649         var ret = false;
4650         Roo.each(this.navItems, function(e) {
4651             if (e.tabId == tabId) {
4652                ret =  e;
4653                return false;
4654             }
4655             return true;
4656             
4657         });
4658         return ret;
4659     },
4660     
4661     setActiveNext : function()
4662     {
4663         var i = this.indexOfNav(this.getActive());
4664         if (i > this.navItems.length) {
4665             return;
4666         }
4667         this.setActiveItem(this.navItems[i+1]);
4668     },
4669     setActivePrev : function()
4670     {
4671         var i = this.indexOfNav(this.getActive());
4672         if (i  < 1) {
4673             return;
4674         }
4675         this.setActiveItem(this.navItems[i-1]);
4676     },
4677     clearWasActive : function(except) {
4678         Roo.each(this.navItems, function(e) {
4679             if (e.tabId != except.tabId && e.was_active) {
4680                e.was_active = false;
4681                return false;
4682             }
4683             return true;
4684             
4685         });
4686     },
4687     getWasActive : function ()
4688     {
4689         var r = false;
4690         Roo.each(this.navItems, function(e) {
4691             if (e.was_active) {
4692                r = e;
4693                return false;
4694             }
4695             return true;
4696             
4697         });
4698         return r;
4699     }
4700     
4701     
4702 });
4703
4704  
4705 Roo.apply(Roo.bootstrap.NavGroup, {
4706     
4707     groups: {},
4708      /**
4709     * register a Navigation Group
4710     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4711     */
4712     register : function(navgrp)
4713     {
4714         this.groups[navgrp.navId] = navgrp;
4715         
4716     },
4717     /**
4718     * fetch a Navigation Group based on the navigation ID
4719     * @param {string} the navgroup to add
4720     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4721     */
4722     get: function(navId) {
4723         if (typeof(this.groups[navId]) == 'undefined') {
4724             return false;
4725             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4726         }
4727         return this.groups[navId] ;
4728     }
4729     
4730     
4731     
4732 });
4733
4734  /*
4735  * - LGPL
4736  *
4737  * row
4738  * 
4739  */
4740
4741 /**
4742  * @class Roo.bootstrap.NavItem
4743  * @extends Roo.bootstrap.Component
4744  * Bootstrap Navbar.NavItem class
4745  * @cfg {String} href  link to
4746  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4747
4748  * @cfg {String} html content of button
4749  * @cfg {String} badge text inside badge
4750  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4751  * @cfg {String} glyphicon DEPRICATED - use fa
4752  * @cfg {String} icon DEPRICATED - use fa
4753  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4754  * @cfg {Boolean} active Is item active
4755  * @cfg {Boolean} disabled Is item disabled
4756  
4757  * @cfg {Boolean} preventDefault (true | false) default false
4758  * @cfg {String} tabId the tab that this item activates.
4759  * @cfg {String} tagtype (a|span) render as a href or span?
4760  * @cfg {Boolean} animateRef (true|false) link to element default false  
4761   
4762  * @constructor
4763  * Create a new Navbar Item
4764  * @param {Object} config The config object
4765  */
4766 Roo.bootstrap.NavItem = function(config){
4767     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4768     this.addEvents({
4769         // raw events
4770         /**
4771          * @event click
4772          * The raw click event for the entire grid.
4773          * @param {Roo.EventObject} e
4774          */
4775         "click" : true,
4776          /**
4777             * @event changed
4778             * Fires when the active item active state changes
4779             * @param {Roo.bootstrap.NavItem} this
4780             * @param {boolean} state the new state
4781              
4782          */
4783         'changed': true,
4784         /**
4785             * @event scrollto
4786             * Fires when scroll to element
4787             * @param {Roo.bootstrap.NavItem} this
4788             * @param {Object} options
4789             * @param {Roo.EventObject} e
4790              
4791          */
4792         'scrollto': true
4793     });
4794    
4795 };
4796
4797 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4798     
4799     href: false,
4800     html: '',
4801     badge: '',
4802     icon: false,
4803     fa : false,
4804     glyphicon: false,
4805     active: false,
4806     preventDefault : false,
4807     tabId : false,
4808     tagtype : 'a',
4809     tag: 'li',
4810     disabled : false,
4811     animateRef : false,
4812     was_active : false,
4813     button_weight : '',
4814     button_outline : false,
4815     
4816     navLink: false,
4817     
4818     getAutoCreate : function(){
4819          
4820         var cfg = {
4821             tag: this.tag,
4822             cls: 'nav-item'
4823         };
4824         
4825         if (this.active) {
4826             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4827         }
4828         if (this.disabled) {
4829             cfg.cls += ' disabled';
4830         }
4831         
4832         // BS4 only?
4833         if (this.button_weight.length) {
4834             cfg.tag = this.href ? 'a' : 'button';
4835             cfg.html = this.html || '';
4836             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4837             if (this.href) {
4838                 cfg.href = this.href;
4839             }
4840             if (this.fa) {
4841                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4842             }
4843             
4844             // menu .. should add dropdown-menu class - so no need for carat..
4845             
4846             if (this.badge !== '') {
4847                  
4848                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4849             }
4850             return cfg;
4851         }
4852         
4853         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4854             cfg.cn = [
4855                 {
4856                     tag: this.tagtype,
4857                     href : this.href || "#",
4858                     html: this.html || ''
4859                 }
4860             ];
4861             if (this.tagtype == 'a') {
4862                 cfg.cn[0].cls = 'nav-link';
4863             }
4864             if (this.icon) {
4865                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4866             }
4867             if (this.fa) {
4868                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4869             }
4870             if(this.glyphicon) {
4871                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4872             }
4873             
4874             if (this.menu) {
4875                 
4876                 cfg.cn[0].html += " <span class='caret'></span>";
4877              
4878             }
4879             
4880             if (this.badge !== '') {
4881                  
4882                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4883             }
4884         }
4885         
4886         
4887         
4888         return cfg;
4889     },
4890     onRender : function(ct, position)
4891     {
4892        // Roo.log("Call onRender: " + this.xtype);
4893         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4894             this.tag = 'div';
4895         }
4896         
4897         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4898         this.navLink = this.el.select('.nav-link',true).first();
4899         return ret;
4900     },
4901       
4902     
4903     initEvents: function() 
4904     {
4905         if (typeof (this.menu) != 'undefined') {
4906             this.menu.parentType = this.xtype;
4907             this.menu.triggerEl = this.el;
4908             this.menu = this.addxtype(Roo.apply({}, this.menu));
4909         }
4910         
4911         this.el.select('a',true).on('click', this.onClick, this);
4912         
4913         if(this.tagtype == 'span'){
4914             this.el.select('span',true).on('click', this.onClick, this);
4915         }
4916        
4917         // at this point parent should be available..
4918         this.parent().register(this);
4919     },
4920     
4921     onClick : function(e)
4922     {
4923         if (e.getTarget('.dropdown-menu-item')) {
4924             // did you click on a menu itemm.... - then don't trigger onclick..
4925             return;
4926         }
4927         
4928         if(
4929                 this.preventDefault || 
4930                 this.href == '#' 
4931         ){
4932             Roo.log("NavItem - prevent Default?");
4933             e.preventDefault();
4934         }
4935         
4936         if (this.disabled) {
4937             return;
4938         }
4939         
4940         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4941         if (tg && tg.transition) {
4942             Roo.log("waiting for the transitionend");
4943             return;
4944         }
4945         
4946         
4947         
4948         //Roo.log("fire event clicked");
4949         if(this.fireEvent('click', this, e) === false){
4950             return;
4951         };
4952         
4953         if(this.tagtype == 'span'){
4954             return;
4955         }
4956         
4957         //Roo.log(this.href);
4958         var ael = this.el.select('a',true).first();
4959         //Roo.log(ael);
4960         
4961         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4962             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4963             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4964                 return; // ignore... - it's a 'hash' to another page.
4965             }
4966             Roo.log("NavItem - prevent Default?");
4967             e.preventDefault();
4968             this.scrollToElement(e);
4969         }
4970         
4971         
4972         var p =  this.parent();
4973    
4974         if (['tabs','pills'].indexOf(p.type)!==-1) {
4975             if (typeof(p.setActiveItem) !== 'undefined') {
4976                 p.setActiveItem(this);
4977             }
4978         }
4979         
4980         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4981         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4982             // remove the collapsed menu expand...
4983             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
4984         }
4985     },
4986     
4987     isActive: function () {
4988         return this.active
4989     },
4990     setActive : function(state, fire, is_was_active)
4991     {
4992         if (this.active && !state && this.navId) {
4993             this.was_active = true;
4994             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4995             if (nv) {
4996                 nv.clearWasActive(this);
4997             }
4998             
4999         }
5000         this.active = state;
5001         
5002         if (!state ) {
5003             this.el.removeClass('active');
5004             this.navLink ? this.navLink.removeClass('active') : false;
5005         } else if (!this.el.hasClass('active')) {
5006             
5007             this.el.addClass('active');
5008             if (Roo.bootstrap.version == 4 && this.navLink ) {
5009                 this.navLink.addClass('active');
5010             }
5011             
5012         }
5013         if (fire) {
5014             this.fireEvent('changed', this, state);
5015         }
5016         
5017         // show a panel if it's registered and related..
5018         
5019         if (!this.navId || !this.tabId || !state || is_was_active) {
5020             return;
5021         }
5022         
5023         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5024         if (!tg) {
5025             return;
5026         }
5027         var pan = tg.getPanelByName(this.tabId);
5028         if (!pan) {
5029             return;
5030         }
5031         // if we can not flip to new panel - go back to old nav highlight..
5032         if (false == tg.showPanel(pan)) {
5033             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5034             if (nv) {
5035                 var onav = nv.getWasActive();
5036                 if (onav) {
5037                     onav.setActive(true, false, true);
5038                 }
5039             }
5040             
5041         }
5042         
5043         
5044         
5045     },
5046      // this should not be here...
5047     setDisabled : function(state)
5048     {
5049         this.disabled = state;
5050         if (!state ) {
5051             this.el.removeClass('disabled');
5052         } else if (!this.el.hasClass('disabled')) {
5053             this.el.addClass('disabled');
5054         }
5055         
5056     },
5057     
5058     /**
5059      * Fetch the element to display the tooltip on.
5060      * @return {Roo.Element} defaults to this.el
5061      */
5062     tooltipEl : function()
5063     {
5064         return this.el.select('' + this.tagtype + '', true).first();
5065     },
5066     
5067     scrollToElement : function(e)
5068     {
5069         var c = document.body;
5070         
5071         /*
5072          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5073          */
5074         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5075             c = document.documentElement;
5076         }
5077         
5078         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5079         
5080         if(!target){
5081             return;
5082         }
5083
5084         var o = target.calcOffsetsTo(c);
5085         
5086         var options = {
5087             target : target,
5088             value : o[1]
5089         };
5090         
5091         this.fireEvent('scrollto', this, options, e);
5092         
5093         Roo.get(c).scrollTo('top', options.value, true);
5094         
5095         return;
5096     }
5097 });
5098  
5099
5100  /*
5101  * - LGPL
5102  *
5103  * sidebar item
5104  *
5105  *  li
5106  *    <span> icon </span>
5107  *    <span> text </span>
5108  *    <span>badge </span>
5109  */
5110
5111 /**
5112  * @class Roo.bootstrap.NavSidebarItem
5113  * @extends Roo.bootstrap.NavItem
5114  * Bootstrap Navbar.NavSidebarItem class
5115  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5116  * {Boolean} open is the menu open
5117  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5118  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5119  * {String} buttonSize (sm|md|lg)the extra classes for the button
5120  * {Boolean} showArrow show arrow next to the text (default true)
5121  * @constructor
5122  * Create a new Navbar Button
5123  * @param {Object} config The config object
5124  */
5125 Roo.bootstrap.NavSidebarItem = function(config){
5126     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5127     this.addEvents({
5128         // raw events
5129         /**
5130          * @event click
5131          * The raw click event for the entire grid.
5132          * @param {Roo.EventObject} e
5133          */
5134         "click" : true,
5135          /**
5136             * @event changed
5137             * Fires when the active item active state changes
5138             * @param {Roo.bootstrap.NavSidebarItem} this
5139             * @param {boolean} state the new state
5140              
5141          */
5142         'changed': true
5143     });
5144    
5145 };
5146
5147 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5148     
5149     badgeWeight : 'default',
5150     
5151     open: false,
5152     
5153     buttonView : false,
5154     
5155     buttonWeight : 'default',
5156     
5157     buttonSize : 'md',
5158     
5159     showArrow : true,
5160     
5161     getAutoCreate : function(){
5162         
5163         
5164         var a = {
5165                 tag: 'a',
5166                 href : this.href || '#',
5167                 cls: '',
5168                 html : '',
5169                 cn : []
5170         };
5171         
5172         if(this.buttonView){
5173             a = {
5174                 tag: 'button',
5175                 href : this.href || '#',
5176                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5177                 html : this.html,
5178                 cn : []
5179             };
5180         }
5181         
5182         var cfg = {
5183             tag: 'li',
5184             cls: '',
5185             cn: [ a ]
5186         };
5187         
5188         if (this.active) {
5189             cfg.cls += ' active';
5190         }
5191         
5192         if (this.disabled) {
5193             cfg.cls += ' disabled';
5194         }
5195         if (this.open) {
5196             cfg.cls += ' open x-open';
5197         }
5198         // left icon..
5199         if (this.glyphicon || this.icon) {
5200             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5201             a.cn.push({ tag : 'i', cls : c }) ;
5202         }
5203         
5204         if(!this.buttonView){
5205             var span = {
5206                 tag: 'span',
5207                 html : this.html || ''
5208             };
5209
5210             a.cn.push(span);
5211             
5212         }
5213         
5214         if (this.badge !== '') {
5215             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5216         }
5217         
5218         if (this.menu) {
5219             
5220             if(this.showArrow){
5221                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5222             }
5223             
5224             a.cls += ' dropdown-toggle treeview' ;
5225         }
5226         
5227         return cfg;
5228     },
5229     
5230     initEvents : function()
5231     { 
5232         if (typeof (this.menu) != 'undefined') {
5233             this.menu.parentType = this.xtype;
5234             this.menu.triggerEl = this.el;
5235             this.menu = this.addxtype(Roo.apply({}, this.menu));
5236         }
5237         
5238         this.el.on('click', this.onClick, this);
5239         
5240         if(this.badge !== ''){
5241             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5242         }
5243         
5244     },
5245     
5246     onClick : function(e)
5247     {
5248         if(this.disabled){
5249             e.preventDefault();
5250             return;
5251         }
5252         
5253         if(this.preventDefault){
5254             e.preventDefault();
5255         }
5256         
5257         this.fireEvent('click', this, e);
5258     },
5259     
5260     disable : function()
5261     {
5262         this.setDisabled(true);
5263     },
5264     
5265     enable : function()
5266     {
5267         this.setDisabled(false);
5268     },
5269     
5270     setDisabled : function(state)
5271     {
5272         if(this.disabled == state){
5273             return;
5274         }
5275         
5276         this.disabled = state;
5277         
5278         if (state) {
5279             this.el.addClass('disabled');
5280             return;
5281         }
5282         
5283         this.el.removeClass('disabled');
5284         
5285         return;
5286     },
5287     
5288     setActive : function(state)
5289     {
5290         if(this.active == state){
5291             return;
5292         }
5293         
5294         this.active = state;
5295         
5296         if (state) {
5297             this.el.addClass('active');
5298             return;
5299         }
5300         
5301         this.el.removeClass('active');
5302         
5303         return;
5304     },
5305     
5306     isActive: function () 
5307     {
5308         return this.active;
5309     },
5310     
5311     setBadge : function(str)
5312     {
5313         if(!this.badgeEl){
5314             return;
5315         }
5316         
5317         this.badgeEl.dom.innerHTML = str;
5318     }
5319     
5320    
5321      
5322  
5323 });
5324  
5325
5326  /*
5327  * - LGPL
5328  *
5329  * row
5330  * 
5331  */
5332
5333 /**
5334  * @class Roo.bootstrap.Row
5335  * @extends Roo.bootstrap.Component
5336  * Bootstrap Row class (contains columns...)
5337  * 
5338  * @constructor
5339  * Create a new Row
5340  * @param {Object} config The config object
5341  */
5342
5343 Roo.bootstrap.Row = function(config){
5344     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5345 };
5346
5347 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5348     
5349     getAutoCreate : function(){
5350        return {
5351             cls: 'row clearfix'
5352        };
5353     }
5354     
5355     
5356 });
5357
5358  
5359
5360  /*
5361  * - LGPL
5362  *
5363  * element
5364  * 
5365  */
5366
5367 /**
5368  * @class Roo.bootstrap.Element
5369  * @extends Roo.bootstrap.Component
5370  * Bootstrap Element class
5371  * @cfg {String} html contents of the element
5372  * @cfg {String} tag tag of the element
5373  * @cfg {String} cls class of the element
5374  * @cfg {Boolean} preventDefault (true|false) default false
5375  * @cfg {Boolean} clickable (true|false) default false
5376  * 
5377  * @constructor
5378  * Create a new Element
5379  * @param {Object} config The config object
5380  */
5381
5382 Roo.bootstrap.Element = function(config){
5383     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5384     
5385     this.addEvents({
5386         // raw events
5387         /**
5388          * @event click
5389          * When a element is chick
5390          * @param {Roo.bootstrap.Element} this
5391          * @param {Roo.EventObject} e
5392          */
5393         "click" : true
5394     });
5395 };
5396
5397 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5398     
5399     tag: 'div',
5400     cls: '',
5401     html: '',
5402     preventDefault: false, 
5403     clickable: false,
5404     
5405     getAutoCreate : function(){
5406         
5407         var cfg = {
5408             tag: this.tag,
5409             // cls: this.cls, double assign in parent class Component.js :: onRender
5410             html: this.html
5411         };
5412         
5413         return cfg;
5414     },
5415     
5416     initEvents: function() 
5417     {
5418         Roo.bootstrap.Element.superclass.initEvents.call(this);
5419         
5420         if(this.clickable){
5421             this.el.on('click', this.onClick, this);
5422         }
5423         
5424     },
5425     
5426     onClick : function(e)
5427     {
5428         if(this.preventDefault){
5429             e.preventDefault();
5430         }
5431         
5432         this.fireEvent('click', this, e);
5433     },
5434     
5435     getValue : function()
5436     {
5437         return this.el.dom.innerHTML;
5438     },
5439     
5440     setValue : function(value)
5441     {
5442         this.el.dom.innerHTML = value;
5443     }
5444    
5445 });
5446
5447  
5448
5449  /*
5450  * - LGPL
5451  *
5452  * pagination
5453  * 
5454  */
5455
5456 /**
5457  * @class Roo.bootstrap.Pagination
5458  * @extends Roo.bootstrap.Component
5459  * Bootstrap Pagination class
5460  * @cfg {String} size xs | sm | md | lg
5461  * @cfg {Boolean} inverse false | true
5462  * 
5463  * @constructor
5464  * Create a new Pagination
5465  * @param {Object} config The config object
5466  */
5467
5468 Roo.bootstrap.Pagination = function(config){
5469     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5470 };
5471
5472 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5473     
5474     cls: false,
5475     size: false,
5476     inverse: false,
5477     
5478     getAutoCreate : function(){
5479         var cfg = {
5480             tag: 'ul',
5481                 cls: 'pagination'
5482         };
5483         if (this.inverse) {
5484             cfg.cls += ' inverse';
5485         }
5486         if (this.html) {
5487             cfg.html=this.html;
5488         }
5489         if (this.cls) {
5490             cfg.cls += " " + this.cls;
5491         }
5492         return cfg;
5493     }
5494    
5495 });
5496
5497  
5498
5499  /*
5500  * - LGPL
5501  *
5502  * Pagination item
5503  * 
5504  */
5505
5506
5507 /**
5508  * @class Roo.bootstrap.PaginationItem
5509  * @extends Roo.bootstrap.Component
5510  * Bootstrap PaginationItem class
5511  * @cfg {String} html text
5512  * @cfg {String} href the link
5513  * @cfg {Boolean} preventDefault (true | false) default true
5514  * @cfg {Boolean} active (true | false) default false
5515  * @cfg {Boolean} disabled default false
5516  * 
5517  * 
5518  * @constructor
5519  * Create a new PaginationItem
5520  * @param {Object} config The config object
5521  */
5522
5523
5524 Roo.bootstrap.PaginationItem = function(config){
5525     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5526     this.addEvents({
5527         // raw events
5528         /**
5529          * @event click
5530          * The raw click event for the entire grid.
5531          * @param {Roo.EventObject} e
5532          */
5533         "click" : true
5534     });
5535 };
5536
5537 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5538     
5539     href : false,
5540     html : false,
5541     preventDefault: true,
5542     active : false,
5543     cls : false,
5544     disabled: false,
5545     
5546     getAutoCreate : function(){
5547         var cfg= {
5548             tag: 'li',
5549             cn: [
5550                 {
5551                     tag : 'a',
5552                     href : this.href ? this.href : '#',
5553                     html : this.html ? this.html : ''
5554                 }
5555             ]
5556         };
5557         
5558         if(this.cls){
5559             cfg.cls = this.cls;
5560         }
5561         
5562         if(this.disabled){
5563             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5564         }
5565         
5566         if(this.active){
5567             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5568         }
5569         
5570         return cfg;
5571     },
5572     
5573     initEvents: function() {
5574         
5575         this.el.on('click', this.onClick, this);
5576         
5577     },
5578     onClick : function(e)
5579     {
5580         Roo.log('PaginationItem on click ');
5581         if(this.preventDefault){
5582             e.preventDefault();
5583         }
5584         
5585         if(this.disabled){
5586             return;
5587         }
5588         
5589         this.fireEvent('click', this, e);
5590     }
5591    
5592 });
5593
5594  
5595
5596  /*
5597  * - LGPL
5598  *
5599  * slider
5600  * 
5601  */
5602
5603
5604 /**
5605  * @class Roo.bootstrap.Slider
5606  * @extends Roo.bootstrap.Component
5607  * Bootstrap Slider class
5608  *    
5609  * @constructor
5610  * Create a new Slider
5611  * @param {Object} config The config object
5612  */
5613
5614 Roo.bootstrap.Slider = function(config){
5615     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5616 };
5617
5618 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5619     
5620     getAutoCreate : function(){
5621         
5622         var cfg = {
5623             tag: 'div',
5624             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5625             cn: [
5626                 {
5627                     tag: 'a',
5628                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5629                 }
5630             ]
5631         };
5632         
5633         return cfg;
5634     }
5635    
5636 });
5637
5638  /*
5639  * Based on:
5640  * Ext JS Library 1.1.1
5641  * Copyright(c) 2006-2007, Ext JS, LLC.
5642  *
5643  * Originally Released Under LGPL - original licence link has changed is not relivant.
5644  *
5645  * Fork - LGPL
5646  * <script type="text/javascript">
5647  */
5648  
5649
5650 /**
5651  * @class Roo.grid.ColumnModel
5652  * @extends Roo.util.Observable
5653  * This is the default implementation of a ColumnModel used by the Grid. It defines
5654  * the columns in the grid.
5655  * <br>Usage:<br>
5656  <pre><code>
5657  var colModel = new Roo.grid.ColumnModel([
5658         {header: "Ticker", width: 60, sortable: true, locked: true},
5659         {header: "Company Name", width: 150, sortable: true},
5660         {header: "Market Cap.", width: 100, sortable: true},
5661         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5662         {header: "Employees", width: 100, sortable: true, resizable: false}
5663  ]);
5664  </code></pre>
5665  * <p>
5666  
5667  * The config options listed for this class are options which may appear in each
5668  * individual column definition.
5669  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5670  * @constructor
5671  * @param {Object} config An Array of column config objects. See this class's
5672  * config objects for details.
5673 */
5674 Roo.grid.ColumnModel = function(config){
5675         /**
5676      * The config passed into the constructor
5677      */
5678     this.config = config;
5679     this.lookup = {};
5680
5681     // if no id, create one
5682     // if the column does not have a dataIndex mapping,
5683     // map it to the order it is in the config
5684     for(var i = 0, len = config.length; i < len; i++){
5685         var c = config[i];
5686         if(typeof c.dataIndex == "undefined"){
5687             c.dataIndex = i;
5688         }
5689         if(typeof c.renderer == "string"){
5690             c.renderer = Roo.util.Format[c.renderer];
5691         }
5692         if(typeof c.id == "undefined"){
5693             c.id = Roo.id();
5694         }
5695         if(c.editor && c.editor.xtype){
5696             c.editor  = Roo.factory(c.editor, Roo.grid);
5697         }
5698         if(c.editor && c.editor.isFormField){
5699             c.editor = new Roo.grid.GridEditor(c.editor);
5700         }
5701         this.lookup[c.id] = c;
5702     }
5703
5704     /**
5705      * The width of columns which have no width specified (defaults to 100)
5706      * @type Number
5707      */
5708     this.defaultWidth = 100;
5709
5710     /**
5711      * Default sortable of columns which have no sortable specified (defaults to false)
5712      * @type Boolean
5713      */
5714     this.defaultSortable = false;
5715
5716     this.addEvents({
5717         /**
5718              * @event widthchange
5719              * Fires when the width of a column changes.
5720              * @param {ColumnModel} this
5721              * @param {Number} columnIndex The column index
5722              * @param {Number} newWidth The new width
5723              */
5724             "widthchange": true,
5725         /**
5726              * @event headerchange
5727              * Fires when the text of a header changes.
5728              * @param {ColumnModel} this
5729              * @param {Number} columnIndex The column index
5730              * @param {Number} newText The new header text
5731              */
5732             "headerchange": true,
5733         /**
5734              * @event hiddenchange
5735              * Fires when a column is hidden or "unhidden".
5736              * @param {ColumnModel} this
5737              * @param {Number} columnIndex The column index
5738              * @param {Boolean} hidden true if hidden, false otherwise
5739              */
5740             "hiddenchange": true,
5741             /**
5742          * @event columnmoved
5743          * Fires when a column is moved.
5744          * @param {ColumnModel} this
5745          * @param {Number} oldIndex
5746          * @param {Number} newIndex
5747          */
5748         "columnmoved" : true,
5749         /**
5750          * @event columlockchange
5751          * Fires when a column's locked state is changed
5752          * @param {ColumnModel} this
5753          * @param {Number} colIndex
5754          * @param {Boolean} locked true if locked
5755          */
5756         "columnlockchange" : true
5757     });
5758     Roo.grid.ColumnModel.superclass.constructor.call(this);
5759 };
5760 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5761     /**
5762      * @cfg {String} header The header text to display in the Grid view.
5763      */
5764     /**
5765      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5766      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5767      * specified, the column's index is used as an index into the Record's data Array.
5768      */
5769     /**
5770      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5771      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5772      */
5773     /**
5774      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5775      * Defaults to the value of the {@link #defaultSortable} property.
5776      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5777      */
5778     /**
5779      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5780      */
5781     /**
5782      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5783      */
5784     /**
5785      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5786      */
5787     /**
5788      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5789      */
5790     /**
5791      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5792      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5793      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5794      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5795      */
5796        /**
5797      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5798      */
5799     /**
5800      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5801      */
5802     /**
5803      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5804      */
5805     /**
5806      * @cfg {String} cursor (Optional)
5807      */
5808     /**
5809      * @cfg {String} tooltip (Optional)
5810      */
5811     /**
5812      * @cfg {Number} xs (Optional)
5813      */
5814     /**
5815      * @cfg {Number} sm (Optional)
5816      */
5817     /**
5818      * @cfg {Number} md (Optional)
5819      */
5820     /**
5821      * @cfg {Number} lg (Optional)
5822      */
5823     /**
5824      * Returns the id of the column at the specified index.
5825      * @param {Number} index The column index
5826      * @return {String} the id
5827      */
5828     getColumnId : function(index){
5829         return this.config[index].id;
5830     },
5831
5832     /**
5833      * Returns the column for a specified id.
5834      * @param {String} id The column id
5835      * @return {Object} the column
5836      */
5837     getColumnById : function(id){
5838         return this.lookup[id];
5839     },
5840
5841     
5842     /**
5843      * Returns the column for a specified dataIndex.
5844      * @param {String} dataIndex The column dataIndex
5845      * @return {Object|Boolean} the column or false if not found
5846      */
5847     getColumnByDataIndex: function(dataIndex){
5848         var index = this.findColumnIndex(dataIndex);
5849         return index > -1 ? this.config[index] : false;
5850     },
5851     
5852     /**
5853      * Returns the index for a specified column id.
5854      * @param {String} id The column id
5855      * @return {Number} the index, or -1 if not found
5856      */
5857     getIndexById : function(id){
5858         for(var i = 0, len = this.config.length; i < len; i++){
5859             if(this.config[i].id == id){
5860                 return i;
5861             }
5862         }
5863         return -1;
5864     },
5865     
5866     /**
5867      * Returns the index for a specified column dataIndex.
5868      * @param {String} dataIndex The column dataIndex
5869      * @return {Number} the index, or -1 if not found
5870      */
5871     
5872     findColumnIndex : function(dataIndex){
5873         for(var i = 0, len = this.config.length; i < len; i++){
5874             if(this.config[i].dataIndex == dataIndex){
5875                 return i;
5876             }
5877         }
5878         return -1;
5879     },
5880     
5881     
5882     moveColumn : function(oldIndex, newIndex){
5883         var c = this.config[oldIndex];
5884         this.config.splice(oldIndex, 1);
5885         this.config.splice(newIndex, 0, c);
5886         this.dataMap = null;
5887         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5888     },
5889
5890     isLocked : function(colIndex){
5891         return this.config[colIndex].locked === true;
5892     },
5893
5894     setLocked : function(colIndex, value, suppressEvent){
5895         if(this.isLocked(colIndex) == value){
5896             return;
5897         }
5898         this.config[colIndex].locked = value;
5899         if(!suppressEvent){
5900             this.fireEvent("columnlockchange", this, colIndex, value);
5901         }
5902     },
5903
5904     getTotalLockedWidth : function(){
5905         var totalWidth = 0;
5906         for(var i = 0; i < this.config.length; i++){
5907             if(this.isLocked(i) && !this.isHidden(i)){
5908                 this.totalWidth += this.getColumnWidth(i);
5909             }
5910         }
5911         return totalWidth;
5912     },
5913
5914     getLockedCount : function(){
5915         for(var i = 0, len = this.config.length; i < len; i++){
5916             if(!this.isLocked(i)){
5917                 return i;
5918             }
5919         }
5920         
5921         return this.config.length;
5922     },
5923
5924     /**
5925      * Returns the number of columns.
5926      * @return {Number}
5927      */
5928     getColumnCount : function(visibleOnly){
5929         if(visibleOnly === true){
5930             var c = 0;
5931             for(var i = 0, len = this.config.length; i < len; i++){
5932                 if(!this.isHidden(i)){
5933                     c++;
5934                 }
5935             }
5936             return c;
5937         }
5938         return this.config.length;
5939     },
5940
5941     /**
5942      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5943      * @param {Function} fn
5944      * @param {Object} scope (optional)
5945      * @return {Array} result
5946      */
5947     getColumnsBy : function(fn, scope){
5948         var r = [];
5949         for(var i = 0, len = this.config.length; i < len; i++){
5950             var c = this.config[i];
5951             if(fn.call(scope||this, c, i) === true){
5952                 r[r.length] = c;
5953             }
5954         }
5955         return r;
5956     },
5957
5958     /**
5959      * Returns true if the specified column is sortable.
5960      * @param {Number} col The column index
5961      * @return {Boolean}
5962      */
5963     isSortable : function(col){
5964         if(typeof this.config[col].sortable == "undefined"){
5965             return this.defaultSortable;
5966         }
5967         return this.config[col].sortable;
5968     },
5969
5970     /**
5971      * Returns the rendering (formatting) function defined for the column.
5972      * @param {Number} col The column index.
5973      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5974      */
5975     getRenderer : function(col){
5976         if(!this.config[col].renderer){
5977             return Roo.grid.ColumnModel.defaultRenderer;
5978         }
5979         return this.config[col].renderer;
5980     },
5981
5982     /**
5983      * Sets the rendering (formatting) function for a column.
5984      * @param {Number} col The column index
5985      * @param {Function} fn The function to use to process the cell's raw data
5986      * to return HTML markup for the grid view. The render function is called with
5987      * the following parameters:<ul>
5988      * <li>Data value.</li>
5989      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5990      * <li>css A CSS style string to apply to the table cell.</li>
5991      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5992      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5993      * <li>Row index</li>
5994      * <li>Column index</li>
5995      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5996      */
5997     setRenderer : function(col, fn){
5998         this.config[col].renderer = fn;
5999     },
6000
6001     /**
6002      * Returns the width for the specified column.
6003      * @param {Number} col The column index
6004      * @return {Number}
6005      */
6006     getColumnWidth : function(col){
6007         return this.config[col].width * 1 || this.defaultWidth;
6008     },
6009
6010     /**
6011      * Sets the width for a column.
6012      * @param {Number} col The column index
6013      * @param {Number} width The new width
6014      */
6015     setColumnWidth : function(col, width, suppressEvent){
6016         this.config[col].width = width;
6017         this.totalWidth = null;
6018         if(!suppressEvent){
6019              this.fireEvent("widthchange", this, col, width);
6020         }
6021     },
6022
6023     /**
6024      * Returns the total width of all columns.
6025      * @param {Boolean} includeHidden True to include hidden column widths
6026      * @return {Number}
6027      */
6028     getTotalWidth : function(includeHidden){
6029         if(!this.totalWidth){
6030             this.totalWidth = 0;
6031             for(var i = 0, len = this.config.length; i < len; i++){
6032                 if(includeHidden || !this.isHidden(i)){
6033                     this.totalWidth += this.getColumnWidth(i);
6034                 }
6035             }
6036         }
6037         return this.totalWidth;
6038     },
6039
6040     /**
6041      * Returns the header for the specified column.
6042      * @param {Number} col The column index
6043      * @return {String}
6044      */
6045     getColumnHeader : function(col){
6046         return this.config[col].header;
6047     },
6048
6049     /**
6050      * Sets the header for a column.
6051      * @param {Number} col The column index
6052      * @param {String} header The new header
6053      */
6054     setColumnHeader : function(col, header){
6055         this.config[col].header = header;
6056         this.fireEvent("headerchange", this, col, header);
6057     },
6058
6059     /**
6060      * Returns the tooltip for the specified column.
6061      * @param {Number} col The column index
6062      * @return {String}
6063      */
6064     getColumnTooltip : function(col){
6065             return this.config[col].tooltip;
6066     },
6067     /**
6068      * Sets the tooltip for a column.
6069      * @param {Number} col The column index
6070      * @param {String} tooltip The new tooltip
6071      */
6072     setColumnTooltip : function(col, tooltip){
6073             this.config[col].tooltip = tooltip;
6074     },
6075
6076     /**
6077      * Returns the dataIndex for the specified column.
6078      * @param {Number} col The column index
6079      * @return {Number}
6080      */
6081     getDataIndex : function(col){
6082         return this.config[col].dataIndex;
6083     },
6084
6085     /**
6086      * Sets the dataIndex for a column.
6087      * @param {Number} col The column index
6088      * @param {Number} dataIndex The new dataIndex
6089      */
6090     setDataIndex : function(col, dataIndex){
6091         this.config[col].dataIndex = dataIndex;
6092     },
6093
6094     
6095     
6096     /**
6097      * Returns true if the cell is editable.
6098      * @param {Number} colIndex The column index
6099      * @param {Number} rowIndex The row index - this is nto actually used..?
6100      * @return {Boolean}
6101      */
6102     isCellEditable : function(colIndex, rowIndex){
6103         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6104     },
6105
6106     /**
6107      * Returns the editor defined for the cell/column.
6108      * return false or null to disable editing.
6109      * @param {Number} colIndex The column index
6110      * @param {Number} rowIndex The row index
6111      * @return {Object}
6112      */
6113     getCellEditor : function(colIndex, rowIndex){
6114         return this.config[colIndex].editor;
6115     },
6116
6117     /**
6118      * Sets if a column is editable.
6119      * @param {Number} col The column index
6120      * @param {Boolean} editable True if the column is editable
6121      */
6122     setEditable : function(col, editable){
6123         this.config[col].editable = editable;
6124     },
6125
6126
6127     /**
6128      * Returns true if the column is hidden.
6129      * @param {Number} colIndex The column index
6130      * @return {Boolean}
6131      */
6132     isHidden : function(colIndex){
6133         return this.config[colIndex].hidden;
6134     },
6135
6136
6137     /**
6138      * Returns true if the column width cannot be changed
6139      */
6140     isFixed : function(colIndex){
6141         return this.config[colIndex].fixed;
6142     },
6143
6144     /**
6145      * Returns true if the column can be resized
6146      * @return {Boolean}
6147      */
6148     isResizable : function(colIndex){
6149         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6150     },
6151     /**
6152      * Sets if a column is hidden.
6153      * @param {Number} colIndex The column index
6154      * @param {Boolean} hidden True if the column is hidden
6155      */
6156     setHidden : function(colIndex, hidden){
6157         this.config[colIndex].hidden = hidden;
6158         this.totalWidth = null;
6159         this.fireEvent("hiddenchange", this, colIndex, hidden);
6160     },
6161
6162     /**
6163      * Sets the editor for a column.
6164      * @param {Number} col The column index
6165      * @param {Object} editor The editor object
6166      */
6167     setEditor : function(col, editor){
6168         this.config[col].editor = editor;
6169     }
6170 });
6171
6172 Roo.grid.ColumnModel.defaultRenderer = function(value)
6173 {
6174     if(typeof value == "object") {
6175         return value;
6176     }
6177         if(typeof value == "string" && value.length < 1){
6178             return "&#160;";
6179         }
6180     
6181         return String.format("{0}", value);
6182 };
6183
6184 // Alias for backwards compatibility
6185 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6186 /*
6187  * Based on:
6188  * Ext JS Library 1.1.1
6189  * Copyright(c) 2006-2007, Ext JS, LLC.
6190  *
6191  * Originally Released Under LGPL - original licence link has changed is not relivant.
6192  *
6193  * Fork - LGPL
6194  * <script type="text/javascript">
6195  */
6196  
6197 /**
6198  * @class Roo.LoadMask
6199  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6200  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6201  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6202  * element's UpdateManager load indicator and will be destroyed after the initial load.
6203  * @constructor
6204  * Create a new LoadMask
6205  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6206  * @param {Object} config The config object
6207  */
6208 Roo.LoadMask = function(el, config){
6209     this.el = Roo.get(el);
6210     Roo.apply(this, config);
6211     if(this.store){
6212         this.store.on('beforeload', this.onBeforeLoad, this);
6213         this.store.on('load', this.onLoad, this);
6214         this.store.on('loadexception', this.onLoadException, this);
6215         this.removeMask = false;
6216     }else{
6217         var um = this.el.getUpdateManager();
6218         um.showLoadIndicator = false; // disable the default indicator
6219         um.on('beforeupdate', this.onBeforeLoad, this);
6220         um.on('update', this.onLoad, this);
6221         um.on('failure', this.onLoad, this);
6222         this.removeMask = true;
6223     }
6224 };
6225
6226 Roo.LoadMask.prototype = {
6227     /**
6228      * @cfg {Boolean} removeMask
6229      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6230      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6231      */
6232     /**
6233      * @cfg {String} msg
6234      * The text to display in a centered loading message box (defaults to 'Loading...')
6235      */
6236     msg : 'Loading...',
6237     /**
6238      * @cfg {String} msgCls
6239      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6240      */
6241     msgCls : 'x-mask-loading',
6242
6243     /**
6244      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6245      * @type Boolean
6246      */
6247     disabled: false,
6248
6249     /**
6250      * Disables the mask to prevent it from being displayed
6251      */
6252     disable : function(){
6253        this.disabled = true;
6254     },
6255
6256     /**
6257      * Enables the mask so that it can be displayed
6258      */
6259     enable : function(){
6260         this.disabled = false;
6261     },
6262     
6263     onLoadException : function()
6264     {
6265         Roo.log(arguments);
6266         
6267         if (typeof(arguments[3]) != 'undefined') {
6268             Roo.MessageBox.alert("Error loading",arguments[3]);
6269         } 
6270         /*
6271         try {
6272             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6273                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6274             }   
6275         } catch(e) {
6276             
6277         }
6278         */
6279     
6280         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6281     },
6282     // private
6283     onLoad : function()
6284     {
6285         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6286     },
6287
6288     // private
6289     onBeforeLoad : function(){
6290         if(!this.disabled){
6291             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6292         }
6293     },
6294
6295     // private
6296     destroy : function(){
6297         if(this.store){
6298             this.store.un('beforeload', this.onBeforeLoad, this);
6299             this.store.un('load', this.onLoad, this);
6300             this.store.un('loadexception', this.onLoadException, this);
6301         }else{
6302             var um = this.el.getUpdateManager();
6303             um.un('beforeupdate', this.onBeforeLoad, this);
6304             um.un('update', this.onLoad, this);
6305             um.un('failure', this.onLoad, this);
6306         }
6307     }
6308 };/*
6309  * - LGPL
6310  *
6311  * table
6312  * 
6313  */
6314
6315 /**
6316  * @class Roo.bootstrap.Table
6317  * @extends Roo.bootstrap.Component
6318  * Bootstrap Table class
6319  * @cfg {String} cls table class
6320  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6321  * @cfg {String} bgcolor Specifies the background color for a table
6322  * @cfg {Number} border Specifies whether the table cells should have borders or not
6323  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6324  * @cfg {Number} cellspacing Specifies the space between cells
6325  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6326  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6327  * @cfg {String} sortable Specifies that the table should be sortable
6328  * @cfg {String} summary Specifies a summary of the content of a table
6329  * @cfg {Number} width Specifies the width of a table
6330  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6331  * 
6332  * @cfg {boolean} striped Should the rows be alternative striped
6333  * @cfg {boolean} bordered Add borders to the table
6334  * @cfg {boolean} hover Add hover highlighting
6335  * @cfg {boolean} condensed Format condensed
6336  * @cfg {boolean} responsive Format condensed
6337  * @cfg {Boolean} loadMask (true|false) default false
6338  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6339  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6340  * @cfg {Boolean} rowSelection (true|false) default false
6341  * @cfg {Boolean} cellSelection (true|false) default false
6342  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6343  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6344  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6345  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6346  
6347  * 
6348  * @constructor
6349  * Create a new Table
6350  * @param {Object} config The config object
6351  */
6352
6353 Roo.bootstrap.Table = function(config){
6354     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6355     
6356   
6357     
6358     // BC...
6359     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6360     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6361     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6362     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6363     
6364     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6365     if (this.sm) {
6366         this.sm.grid = this;
6367         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6368         this.sm = this.selModel;
6369         this.sm.xmodule = this.xmodule || false;
6370     }
6371     
6372     if (this.cm && typeof(this.cm.config) == 'undefined') {
6373         this.colModel = new Roo.grid.ColumnModel(this.cm);
6374         this.cm = this.colModel;
6375         this.cm.xmodule = this.xmodule || false;
6376     }
6377     if (this.store) {
6378         this.store= Roo.factory(this.store, Roo.data);
6379         this.ds = this.store;
6380         this.ds.xmodule = this.xmodule || false;
6381          
6382     }
6383     if (this.footer && this.store) {
6384         this.footer.dataSource = this.ds;
6385         this.footer = Roo.factory(this.footer);
6386     }
6387     
6388     /** @private */
6389     this.addEvents({
6390         /**
6391          * @event cellclick
6392          * Fires when a cell is clicked
6393          * @param {Roo.bootstrap.Table} this
6394          * @param {Roo.Element} el
6395          * @param {Number} rowIndex
6396          * @param {Number} columnIndex
6397          * @param {Roo.EventObject} e
6398          */
6399         "cellclick" : true,
6400         /**
6401          * @event celldblclick
6402          * Fires when a cell is double clicked
6403          * @param {Roo.bootstrap.Table} this
6404          * @param {Roo.Element} el
6405          * @param {Number} rowIndex
6406          * @param {Number} columnIndex
6407          * @param {Roo.EventObject} e
6408          */
6409         "celldblclick" : true,
6410         /**
6411          * @event rowclick
6412          * Fires when a row is clicked
6413          * @param {Roo.bootstrap.Table} this
6414          * @param {Roo.Element} el
6415          * @param {Number} rowIndex
6416          * @param {Roo.EventObject} e
6417          */
6418         "rowclick" : true,
6419         /**
6420          * @event rowdblclick
6421          * Fires when a row is double clicked
6422          * @param {Roo.bootstrap.Table} this
6423          * @param {Roo.Element} el
6424          * @param {Number} rowIndex
6425          * @param {Roo.EventObject} e
6426          */
6427         "rowdblclick" : true,
6428         /**
6429          * @event mouseover
6430          * Fires when a mouseover occur
6431          * @param {Roo.bootstrap.Table} this
6432          * @param {Roo.Element} el
6433          * @param {Number} rowIndex
6434          * @param {Number} columnIndex
6435          * @param {Roo.EventObject} e
6436          */
6437         "mouseover" : true,
6438         /**
6439          * @event mouseout
6440          * Fires when a mouseout occur
6441          * @param {Roo.bootstrap.Table} this
6442          * @param {Roo.Element} el
6443          * @param {Number} rowIndex
6444          * @param {Number} columnIndex
6445          * @param {Roo.EventObject} e
6446          */
6447         "mouseout" : true,
6448         /**
6449          * @event rowclass
6450          * Fires when a row is rendered, so you can change add a style to it.
6451          * @param {Roo.bootstrap.Table} this
6452          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6453          */
6454         'rowclass' : true,
6455           /**
6456          * @event rowsrendered
6457          * Fires when all the  rows have been rendered
6458          * @param {Roo.bootstrap.Table} this
6459          */
6460         'rowsrendered' : true,
6461         /**
6462          * @event contextmenu
6463          * The raw contextmenu event for the entire grid.
6464          * @param {Roo.EventObject} e
6465          */
6466         "contextmenu" : true,
6467         /**
6468          * @event rowcontextmenu
6469          * Fires when a row is right clicked
6470          * @param {Roo.bootstrap.Table} this
6471          * @param {Number} rowIndex
6472          * @param {Roo.EventObject} e
6473          */
6474         "rowcontextmenu" : true,
6475         /**
6476          * @event cellcontextmenu
6477          * Fires when a cell is right clicked
6478          * @param {Roo.bootstrap.Table} this
6479          * @param {Number} rowIndex
6480          * @param {Number} cellIndex
6481          * @param {Roo.EventObject} e
6482          */
6483          "cellcontextmenu" : true,
6484          /**
6485          * @event headercontextmenu
6486          * Fires when a header is right clicked
6487          * @param {Roo.bootstrap.Table} this
6488          * @param {Number} columnIndex
6489          * @param {Roo.EventObject} e
6490          */
6491         "headercontextmenu" : true
6492     });
6493 };
6494
6495 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6496     
6497     cls: false,
6498     align: false,
6499     bgcolor: false,
6500     border: false,
6501     cellpadding: false,
6502     cellspacing: false,
6503     frame: false,
6504     rules: false,
6505     sortable: false,
6506     summary: false,
6507     width: false,
6508     striped : false,
6509     scrollBody : false,
6510     bordered: false,
6511     hover:  false,
6512     condensed : false,
6513     responsive : false,
6514     sm : false,
6515     cm : false,
6516     store : false,
6517     loadMask : false,
6518     footerShow : true,
6519     headerShow : true,
6520   
6521     rowSelection : false,
6522     cellSelection : false,
6523     layout : false,
6524     
6525     // Roo.Element - the tbody
6526     mainBody: false,
6527     // Roo.Element - thead element
6528     mainHead: false,
6529     
6530     container: false, // used by gridpanel...
6531     
6532     lazyLoad : false,
6533     
6534     CSS : Roo.util.CSS,
6535     
6536     auto_hide_footer : false,
6537     
6538     getAutoCreate : function()
6539     {
6540         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6541         
6542         cfg = {
6543             tag: 'table',
6544             cls : 'table',
6545             cn : []
6546         };
6547         if (this.scrollBody) {
6548             cfg.cls += ' table-body-fixed';
6549         }    
6550         if (this.striped) {
6551             cfg.cls += ' table-striped';
6552         }
6553         
6554         if (this.hover) {
6555             cfg.cls += ' table-hover';
6556         }
6557         if (this.bordered) {
6558             cfg.cls += ' table-bordered';
6559         }
6560         if (this.condensed) {
6561             cfg.cls += ' table-condensed';
6562         }
6563         if (this.responsive) {
6564             cfg.cls += ' table-responsive';
6565         }
6566         
6567         if (this.cls) {
6568             cfg.cls+=  ' ' +this.cls;
6569         }
6570         
6571         // this lot should be simplifed...
6572         var _t = this;
6573         var cp = [
6574             'align',
6575             'bgcolor',
6576             'border',
6577             'cellpadding',
6578             'cellspacing',
6579             'frame',
6580             'rules',
6581             'sortable',
6582             'summary',
6583             'width'
6584         ].forEach(function(k) {
6585             if (_t[k]) {
6586                 cfg[k] = _t[k];
6587             }
6588         });
6589         
6590         
6591         if (this.layout) {
6592             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6593         }
6594         
6595         if(this.store || this.cm){
6596             if(this.headerShow){
6597                 cfg.cn.push(this.renderHeader());
6598             }
6599             
6600             cfg.cn.push(this.renderBody());
6601             
6602             if(this.footerShow){
6603                 cfg.cn.push(this.renderFooter());
6604             }
6605             // where does this come from?
6606             //cfg.cls+=  ' TableGrid';
6607         }
6608         
6609         return { cn : [ cfg ] };
6610     },
6611     
6612     initEvents : function()
6613     {   
6614         if(!this.store || !this.cm){
6615             return;
6616         }
6617         if (this.selModel) {
6618             this.selModel.initEvents();
6619         }
6620         
6621         
6622         //Roo.log('initEvents with ds!!!!');
6623         
6624         this.mainBody = this.el.select('tbody', true).first();
6625         this.mainHead = this.el.select('thead', true).first();
6626         this.mainFoot = this.el.select('tfoot', true).first();
6627         
6628         
6629         
6630         var _this = this;
6631         
6632         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6633             e.on('click', _this.sort, _this);
6634         });
6635         
6636         this.mainBody.on("click", this.onClick, this);
6637         this.mainBody.on("dblclick", this.onDblClick, this);
6638         
6639         // why is this done????? = it breaks dialogs??
6640         //this.parent().el.setStyle('position', 'relative');
6641         
6642         
6643         if (this.footer) {
6644             this.footer.parentId = this.id;
6645             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6646             
6647             if(this.lazyLoad){
6648                 this.el.select('tfoot tr td').first().addClass('hide');
6649             }
6650         } 
6651         
6652         if(this.loadMask) {
6653             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6654         }
6655         
6656         this.store.on('load', this.onLoad, this);
6657         this.store.on('beforeload', this.onBeforeLoad, this);
6658         this.store.on('update', this.onUpdate, this);
6659         this.store.on('add', this.onAdd, this);
6660         this.store.on("clear", this.clear, this);
6661         
6662         this.el.on("contextmenu", this.onContextMenu, this);
6663         
6664         this.mainBody.on('scroll', this.onBodyScroll, this);
6665         
6666         this.cm.on("headerchange", this.onHeaderChange, this);
6667         
6668         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6669         
6670     },
6671     
6672     onContextMenu : function(e, t)
6673     {
6674         this.processEvent("contextmenu", e);
6675     },
6676     
6677     processEvent : function(name, e)
6678     {
6679         if (name != 'touchstart' ) {
6680             this.fireEvent(name, e);    
6681         }
6682         
6683         var t = e.getTarget();
6684         
6685         var cell = Roo.get(t);
6686         
6687         if(!cell){
6688             return;
6689         }
6690         
6691         if(cell.findParent('tfoot', false, true)){
6692             return;
6693         }
6694         
6695         if(cell.findParent('thead', false, true)){
6696             
6697             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6698                 cell = Roo.get(t).findParent('th', false, true);
6699                 if (!cell) {
6700                     Roo.log("failed to find th in thead?");
6701                     Roo.log(e.getTarget());
6702                     return;
6703                 }
6704             }
6705             
6706             var cellIndex = cell.dom.cellIndex;
6707             
6708             var ename = name == 'touchstart' ? 'click' : name;
6709             this.fireEvent("header" + ename, this, cellIndex, e);
6710             
6711             return;
6712         }
6713         
6714         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6715             cell = Roo.get(t).findParent('td', false, true);
6716             if (!cell) {
6717                 Roo.log("failed to find th in tbody?");
6718                 Roo.log(e.getTarget());
6719                 return;
6720             }
6721         }
6722         
6723         var row = cell.findParent('tr', false, true);
6724         var cellIndex = cell.dom.cellIndex;
6725         var rowIndex = row.dom.rowIndex - 1;
6726         
6727         if(row !== false){
6728             
6729             this.fireEvent("row" + name, this, rowIndex, e);
6730             
6731             if(cell !== false){
6732             
6733                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6734             }
6735         }
6736         
6737     },
6738     
6739     onMouseover : function(e, el)
6740     {
6741         var cell = Roo.get(el);
6742         
6743         if(!cell){
6744             return;
6745         }
6746         
6747         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6748             cell = cell.findParent('td', false, true);
6749         }
6750         
6751         var row = cell.findParent('tr', false, true);
6752         var cellIndex = cell.dom.cellIndex;
6753         var rowIndex = row.dom.rowIndex - 1; // start from 0
6754         
6755         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6756         
6757     },
6758     
6759     onMouseout : function(e, el)
6760     {
6761         var cell = Roo.get(el);
6762         
6763         if(!cell){
6764             return;
6765         }
6766         
6767         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6768             cell = cell.findParent('td', false, true);
6769         }
6770         
6771         var row = cell.findParent('tr', false, true);
6772         var cellIndex = cell.dom.cellIndex;
6773         var rowIndex = row.dom.rowIndex - 1; // start from 0
6774         
6775         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6776         
6777     },
6778     
6779     onClick : function(e, el)
6780     {
6781         var cell = Roo.get(el);
6782         
6783         if(!cell || (!this.cellSelection && !this.rowSelection)){
6784             return;
6785         }
6786         
6787         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6788             cell = cell.findParent('td', false, true);
6789         }
6790         
6791         if(!cell || typeof(cell) == 'undefined'){
6792             return;
6793         }
6794         
6795         var row = cell.findParent('tr', false, true);
6796         
6797         if(!row || typeof(row) == 'undefined'){
6798             return;
6799         }
6800         
6801         var cellIndex = cell.dom.cellIndex;
6802         var rowIndex = this.getRowIndex(row);
6803         
6804         // why??? - should these not be based on SelectionModel?
6805         if(this.cellSelection){
6806             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6807         }
6808         
6809         if(this.rowSelection){
6810             this.fireEvent('rowclick', this, row, rowIndex, e);
6811         }
6812         
6813         
6814     },
6815         
6816     onDblClick : function(e,el)
6817     {
6818         var cell = Roo.get(el);
6819         
6820         if(!cell || (!this.cellSelection && !this.rowSelection)){
6821             return;
6822         }
6823         
6824         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6825             cell = cell.findParent('td', false, true);
6826         }
6827         
6828         if(!cell || typeof(cell) == 'undefined'){
6829             return;
6830         }
6831         
6832         var row = cell.findParent('tr', false, true);
6833         
6834         if(!row || typeof(row) == 'undefined'){
6835             return;
6836         }
6837         
6838         var cellIndex = cell.dom.cellIndex;
6839         var rowIndex = this.getRowIndex(row);
6840         
6841         if(this.cellSelection){
6842             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6843         }
6844         
6845         if(this.rowSelection){
6846             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6847         }
6848     },
6849     
6850     sort : function(e,el)
6851     {
6852         var col = Roo.get(el);
6853         
6854         if(!col.hasClass('sortable')){
6855             return;
6856         }
6857         
6858         var sort = col.attr('sort');
6859         var dir = 'ASC';
6860         
6861         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6862             dir = 'DESC';
6863         }
6864         
6865         this.store.sortInfo = {field : sort, direction : dir};
6866         
6867         if (this.footer) {
6868             Roo.log("calling footer first");
6869             this.footer.onClick('first');
6870         } else {
6871         
6872             this.store.load({ params : { start : 0 } });
6873         }
6874     },
6875     
6876     renderHeader : function()
6877     {
6878         var header = {
6879             tag: 'thead',
6880             cn : []
6881         };
6882         
6883         var cm = this.cm;
6884         this.totalWidth = 0;
6885         
6886         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6887             
6888             var config = cm.config[i];
6889             
6890             var c = {
6891                 tag: 'th',
6892                 cls : 'x-hcol-' + i,
6893                 style : '',
6894                 html: cm.getColumnHeader(i)
6895             };
6896             
6897             var hh = '';
6898             
6899             if(typeof(config.sortable) != 'undefined' && config.sortable){
6900                 c.cls = 'sortable';
6901                 c.html = '<i class="glyphicon"></i>' + c.html;
6902             }
6903             
6904             // could use BS4 hidden-..-down 
6905             
6906             if(typeof(config.lgHeader) != 'undefined'){
6907                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6908             }
6909             
6910             if(typeof(config.mdHeader) != 'undefined'){
6911                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6912             }
6913             
6914             if(typeof(config.smHeader) != 'undefined'){
6915                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6916             }
6917             
6918             if(typeof(config.xsHeader) != 'undefined'){
6919                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6920             }
6921             
6922             if(hh.length){
6923                 c.html = hh;
6924             }
6925             
6926             if(typeof(config.tooltip) != 'undefined'){
6927                 c.tooltip = config.tooltip;
6928             }
6929             
6930             if(typeof(config.colspan) != 'undefined'){
6931                 c.colspan = config.colspan;
6932             }
6933             
6934             if(typeof(config.hidden) != 'undefined' && config.hidden){
6935                 c.style += ' display:none;';
6936             }
6937             
6938             if(typeof(config.dataIndex) != 'undefined'){
6939                 c.sort = config.dataIndex;
6940             }
6941             
6942            
6943             
6944             if(typeof(config.align) != 'undefined' && config.align.length){
6945                 c.style += ' text-align:' + config.align + ';';
6946             }
6947             
6948             if(typeof(config.width) != 'undefined'){
6949                 c.style += ' width:' + config.width + 'px;';
6950                 this.totalWidth += config.width;
6951             } else {
6952                 this.totalWidth += 100; // assume minimum of 100 per column?
6953             }
6954             
6955             if(typeof(config.cls) != 'undefined'){
6956                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6957             }
6958             
6959             ['xs','sm','md','lg'].map(function(size){
6960                 
6961                 if(typeof(config[size]) == 'undefined'){
6962                     return;
6963                 }
6964                  
6965                 if (!config[size]) { // 0 = hidden
6966                     // BS 4 '0' is treated as hide that column and below.
6967                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6968                     return;
6969                 }
6970                 
6971                 c.cls += ' col-' + size + '-' + config[size] + (
6972                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6973                 );
6974                 
6975                 
6976             });
6977             
6978             header.cn.push(c)
6979         }
6980         
6981         return header;
6982     },
6983     
6984     renderBody : function()
6985     {
6986         var body = {
6987             tag: 'tbody',
6988             cn : [
6989                 {
6990                     tag: 'tr',
6991                     cn : [
6992                         {
6993                             tag : 'td',
6994                             colspan :  this.cm.getColumnCount()
6995                         }
6996                     ]
6997                 }
6998             ]
6999         };
7000         
7001         return body;
7002     },
7003     
7004     renderFooter : function()
7005     {
7006         var footer = {
7007             tag: 'tfoot',
7008             cn : [
7009                 {
7010                     tag: 'tr',
7011                     cn : [
7012                         {
7013                             tag : 'td',
7014                             colspan :  this.cm.getColumnCount()
7015                         }
7016                     ]
7017                 }
7018             ]
7019         };
7020         
7021         return footer;
7022     },
7023     
7024     
7025     
7026     onLoad : function()
7027     {
7028 //        Roo.log('ds onload');
7029         this.clear();
7030         
7031         var _this = this;
7032         var cm = this.cm;
7033         var ds = this.store;
7034         
7035         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7036             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7037             if (_this.store.sortInfo) {
7038                     
7039                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7040                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7041                 }
7042                 
7043                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7044                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7045                 }
7046             }
7047         });
7048         
7049         var tbody =  this.mainBody;
7050               
7051         if(ds.getCount() > 0){
7052             ds.data.each(function(d,rowIndex){
7053                 var row =  this.renderRow(cm, ds, rowIndex);
7054                 
7055                 tbody.createChild(row);
7056                 
7057                 var _this = this;
7058                 
7059                 if(row.cellObjects.length){
7060                     Roo.each(row.cellObjects, function(r){
7061                         _this.renderCellObject(r);
7062                     })
7063                 }
7064                 
7065             }, this);
7066         }
7067         
7068         var tfoot = this.el.select('tfoot', true).first();
7069         
7070         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7071             
7072             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7073             
7074             var total = this.ds.getTotalCount();
7075             
7076             if(this.footer.pageSize < total){
7077                 this.mainFoot.show();
7078             }
7079         }
7080         
7081         Roo.each(this.el.select('tbody td', true).elements, function(e){
7082             e.on('mouseover', _this.onMouseover, _this);
7083         });
7084         
7085         Roo.each(this.el.select('tbody td', true).elements, function(e){
7086             e.on('mouseout', _this.onMouseout, _this);
7087         });
7088         this.fireEvent('rowsrendered', this);
7089         
7090         this.autoSize();
7091     },
7092     
7093     
7094     onUpdate : function(ds,record)
7095     {
7096         this.refreshRow(record);
7097         this.autoSize();
7098     },
7099     
7100     onRemove : function(ds, record, index, isUpdate){
7101         if(isUpdate !== true){
7102             this.fireEvent("beforerowremoved", this, index, record);
7103         }
7104         var bt = this.mainBody.dom;
7105         
7106         var rows = this.el.select('tbody > tr', true).elements;
7107         
7108         if(typeof(rows[index]) != 'undefined'){
7109             bt.removeChild(rows[index].dom);
7110         }
7111         
7112 //        if(bt.rows[index]){
7113 //            bt.removeChild(bt.rows[index]);
7114 //        }
7115         
7116         if(isUpdate !== true){
7117             //this.stripeRows(index);
7118             //this.syncRowHeights(index, index);
7119             //this.layout();
7120             this.fireEvent("rowremoved", this, index, record);
7121         }
7122     },
7123     
7124     onAdd : function(ds, records, rowIndex)
7125     {
7126         //Roo.log('on Add called');
7127         // - note this does not handle multiple adding very well..
7128         var bt = this.mainBody.dom;
7129         for (var i =0 ; i < records.length;i++) {
7130             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7131             //Roo.log(records[i]);
7132             //Roo.log(this.store.getAt(rowIndex+i));
7133             this.insertRow(this.store, rowIndex + i, false);
7134             return;
7135         }
7136         
7137     },
7138     
7139     
7140     refreshRow : function(record){
7141         var ds = this.store, index;
7142         if(typeof record == 'number'){
7143             index = record;
7144             record = ds.getAt(index);
7145         }else{
7146             index = ds.indexOf(record);
7147         }
7148         this.insertRow(ds, index, true);
7149         this.autoSize();
7150         this.onRemove(ds, record, index+1, true);
7151         this.autoSize();
7152         //this.syncRowHeights(index, index);
7153         //this.layout();
7154         this.fireEvent("rowupdated", this, index, record);
7155     },
7156     
7157     insertRow : function(dm, rowIndex, isUpdate){
7158         
7159         if(!isUpdate){
7160             this.fireEvent("beforerowsinserted", this, rowIndex);
7161         }
7162             //var s = this.getScrollState();
7163         var row = this.renderRow(this.cm, this.store, rowIndex);
7164         // insert before rowIndex..
7165         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7166         
7167         var _this = this;
7168                 
7169         if(row.cellObjects.length){
7170             Roo.each(row.cellObjects, function(r){
7171                 _this.renderCellObject(r);
7172             })
7173         }
7174             
7175         if(!isUpdate){
7176             this.fireEvent("rowsinserted", this, rowIndex);
7177             //this.syncRowHeights(firstRow, lastRow);
7178             //this.stripeRows(firstRow);
7179             //this.layout();
7180         }
7181         
7182     },
7183     
7184     
7185     getRowDom : function(rowIndex)
7186     {
7187         var rows = this.el.select('tbody > tr', true).elements;
7188         
7189         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7190         
7191     },
7192     // returns the object tree for a tr..
7193   
7194     
7195     renderRow : function(cm, ds, rowIndex) 
7196     {
7197         var d = ds.getAt(rowIndex);
7198         
7199         var row = {
7200             tag : 'tr',
7201             cls : 'x-row-' + rowIndex,
7202             cn : []
7203         };
7204             
7205         var cellObjects = [];
7206         
7207         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7208             var config = cm.config[i];
7209             
7210             var renderer = cm.getRenderer(i);
7211             var value = '';
7212             var id = false;
7213             
7214             if(typeof(renderer) !== 'undefined'){
7215                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7216             }
7217             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7218             // and are rendered into the cells after the row is rendered - using the id for the element.
7219             
7220             if(typeof(value) === 'object'){
7221                 id = Roo.id();
7222                 cellObjects.push({
7223                     container : id,
7224                     cfg : value 
7225                 })
7226             }
7227             
7228             var rowcfg = {
7229                 record: d,
7230                 rowIndex : rowIndex,
7231                 colIndex : i,
7232                 rowClass : ''
7233             };
7234
7235             this.fireEvent('rowclass', this, rowcfg);
7236             
7237             var td = {
7238                 tag: 'td',
7239                 cls : rowcfg.rowClass + ' x-col-' + i,
7240                 style: '',
7241                 html: (typeof(value) === 'object') ? '' : value
7242             };
7243             
7244             if (id) {
7245                 td.id = id;
7246             }
7247             
7248             if(typeof(config.colspan) != 'undefined'){
7249                 td.colspan = config.colspan;
7250             }
7251             
7252             if(typeof(config.hidden) != 'undefined' && config.hidden){
7253                 td.style += ' display:none;';
7254             }
7255             
7256             if(typeof(config.align) != 'undefined' && config.align.length){
7257                 td.style += ' text-align:' + config.align + ';';
7258             }
7259             if(typeof(config.valign) != 'undefined' && config.valign.length){
7260                 td.style += ' vertical-align:' + config.valign + ';';
7261             }
7262             
7263             if(typeof(config.width) != 'undefined'){
7264                 td.style += ' width:' +  config.width + 'px;';
7265             }
7266             
7267             if(typeof(config.cursor) != 'undefined'){
7268                 td.style += ' cursor:' +  config.cursor + ';';
7269             }
7270             
7271             if(typeof(config.cls) != 'undefined'){
7272                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7273             }
7274             
7275             ['xs','sm','md','lg'].map(function(size){
7276                 
7277                 if(typeof(config[size]) == 'undefined'){
7278                     return;
7279                 }
7280                 
7281                 
7282                   
7283                 if (!config[size]) { // 0 = hidden
7284                     // BS 4 '0' is treated as hide that column and below.
7285                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7286                     return;
7287                 }
7288                 
7289                 td.cls += ' col-' + size + '-' + config[size] + (
7290                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7291                 );
7292                  
7293
7294             });
7295             
7296             row.cn.push(td);
7297            
7298         }
7299         
7300         row.cellObjects = cellObjects;
7301         
7302         return row;
7303           
7304     },
7305     
7306     
7307     
7308     onBeforeLoad : function()
7309     {
7310         
7311     },
7312      /**
7313      * Remove all rows
7314      */
7315     clear : function()
7316     {
7317         this.el.select('tbody', true).first().dom.innerHTML = '';
7318     },
7319     /**
7320      * Show or hide a row.
7321      * @param {Number} rowIndex to show or hide
7322      * @param {Boolean} state hide
7323      */
7324     setRowVisibility : function(rowIndex, state)
7325     {
7326         var bt = this.mainBody.dom;
7327         
7328         var rows = this.el.select('tbody > tr', true).elements;
7329         
7330         if(typeof(rows[rowIndex]) == 'undefined'){
7331             return;
7332         }
7333         rows[rowIndex].dom.style.display = state ? '' : 'none';
7334     },
7335     
7336     
7337     getSelectionModel : function(){
7338         if(!this.selModel){
7339             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7340         }
7341         return this.selModel;
7342     },
7343     /*
7344      * Render the Roo.bootstrap object from renderder
7345      */
7346     renderCellObject : function(r)
7347     {
7348         var _this = this;
7349         
7350         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7351         
7352         var t = r.cfg.render(r.container);
7353         
7354         if(r.cfg.cn){
7355             Roo.each(r.cfg.cn, function(c){
7356                 var child = {
7357                     container: t.getChildContainer(),
7358                     cfg: c
7359                 };
7360                 _this.renderCellObject(child);
7361             })
7362         }
7363     },
7364     
7365     getRowIndex : function(row)
7366     {
7367         var rowIndex = -1;
7368         
7369         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7370             if(el != row){
7371                 return;
7372             }
7373             
7374             rowIndex = index;
7375         });
7376         
7377         return rowIndex;
7378     },
7379      /**
7380      * Returns the grid's underlying element = used by panel.Grid
7381      * @return {Element} The element
7382      */
7383     getGridEl : function(){
7384         return this.el;
7385     },
7386      /**
7387      * Forces a resize - used by panel.Grid
7388      * @return {Element} The element
7389      */
7390     autoSize : function()
7391     {
7392         //var ctr = Roo.get(this.container.dom.parentElement);
7393         var ctr = Roo.get(this.el.dom);
7394         
7395         var thd = this.getGridEl().select('thead',true).first();
7396         var tbd = this.getGridEl().select('tbody', true).first();
7397         var tfd = this.getGridEl().select('tfoot', true).first();
7398         
7399         var cw = ctr.getWidth();
7400         
7401         if (tbd) {
7402             
7403             tbd.setSize(ctr.getWidth(),
7404                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7405             );
7406             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7407             cw -= barsize;
7408         }
7409         cw = Math.max(cw, this.totalWidth);
7410         this.getGridEl().select('tr',true).setWidth(cw);
7411         // resize 'expandable coloumn?
7412         
7413         return; // we doe not have a view in this design..
7414         
7415     },
7416     onBodyScroll: function()
7417     {
7418         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7419         if(this.mainHead){
7420             this.mainHead.setStyle({
7421                 'position' : 'relative',
7422                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7423             });
7424         }
7425         
7426         if(this.lazyLoad){
7427             
7428             var scrollHeight = this.mainBody.dom.scrollHeight;
7429             
7430             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7431             
7432             var height = this.mainBody.getHeight();
7433             
7434             if(scrollHeight - height == scrollTop) {
7435                 
7436                 var total = this.ds.getTotalCount();
7437                 
7438                 if(this.footer.cursor + this.footer.pageSize < total){
7439                     
7440                     this.footer.ds.load({
7441                         params : {
7442                             start : this.footer.cursor + this.footer.pageSize,
7443                             limit : this.footer.pageSize
7444                         },
7445                         add : true
7446                     });
7447                 }
7448             }
7449             
7450         }
7451     },
7452     
7453     onHeaderChange : function()
7454     {
7455         var header = this.renderHeader();
7456         var table = this.el.select('table', true).first();
7457         
7458         this.mainHead.remove();
7459         this.mainHead = table.createChild(header, this.mainBody, false);
7460     },
7461     
7462     onHiddenChange : function(colModel, colIndex, hidden)
7463     {
7464         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7465         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7466         
7467         this.CSS.updateRule(thSelector, "display", "");
7468         this.CSS.updateRule(tdSelector, "display", "");
7469         
7470         if(hidden){
7471             this.CSS.updateRule(thSelector, "display", "none");
7472             this.CSS.updateRule(tdSelector, "display", "none");
7473         }
7474         
7475         this.onHeaderChange();
7476         this.onLoad();
7477     },
7478     
7479     setColumnWidth: function(col_index, width)
7480     {
7481         // width = "md-2 xs-2..."
7482         if(!this.colModel.config[col_index]) {
7483             return;
7484         }
7485         
7486         var w = width.split(" ");
7487         
7488         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7489         
7490         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7491         
7492         
7493         for(var j = 0; j < w.length; j++) {
7494             
7495             if(!w[j]) {
7496                 continue;
7497             }
7498             
7499             var size_cls = w[j].split("-");
7500             
7501             if(!Number.isInteger(size_cls[1] * 1)) {
7502                 continue;
7503             }
7504             
7505             if(!this.colModel.config[col_index][size_cls[0]]) {
7506                 continue;
7507             }
7508             
7509             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7510                 continue;
7511             }
7512             
7513             h_row[0].classList.replace(
7514                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7515                 "col-"+size_cls[0]+"-"+size_cls[1]
7516             );
7517             
7518             for(var i = 0; i < rows.length; i++) {
7519                 
7520                 var size_cls = w[j].split("-");
7521                 
7522                 if(!Number.isInteger(size_cls[1] * 1)) {
7523                     continue;
7524                 }
7525                 
7526                 if(!this.colModel.config[col_index][size_cls[0]]) {
7527                     continue;
7528                 }
7529                 
7530                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7531                     continue;
7532                 }
7533                 
7534                 rows[i].classList.replace(
7535                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7536                     "col-"+size_cls[0]+"-"+size_cls[1]
7537                 );
7538             }
7539             
7540             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7541         }
7542     }
7543 });
7544
7545  
7546
7547  /*
7548  * - LGPL
7549  *
7550  * table cell
7551  * 
7552  */
7553
7554 /**
7555  * @class Roo.bootstrap.TableCell
7556  * @extends Roo.bootstrap.Component
7557  * Bootstrap TableCell class
7558  * @cfg {String} html cell contain text
7559  * @cfg {String} cls cell class
7560  * @cfg {String} tag cell tag (td|th) default td
7561  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7562  * @cfg {String} align Aligns the content in a cell
7563  * @cfg {String} axis Categorizes cells
7564  * @cfg {String} bgcolor Specifies the background color of a cell
7565  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7566  * @cfg {Number} colspan Specifies the number of columns a cell should span
7567  * @cfg {String} headers Specifies one or more header cells a cell is related to
7568  * @cfg {Number} height Sets the height of a cell
7569  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7570  * @cfg {Number} rowspan Sets the number of rows a cell should span
7571  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7572  * @cfg {String} valign Vertical aligns the content in a cell
7573  * @cfg {Number} width Specifies the width of a cell
7574  * 
7575  * @constructor
7576  * Create a new TableCell
7577  * @param {Object} config The config object
7578  */
7579
7580 Roo.bootstrap.TableCell = function(config){
7581     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7582 };
7583
7584 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7585     
7586     html: false,
7587     cls: false,
7588     tag: false,
7589     abbr: false,
7590     align: false,
7591     axis: false,
7592     bgcolor: false,
7593     charoff: false,
7594     colspan: false,
7595     headers: false,
7596     height: false,
7597     nowrap: false,
7598     rowspan: false,
7599     scope: false,
7600     valign: false,
7601     width: false,
7602     
7603     
7604     getAutoCreate : function(){
7605         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7606         
7607         cfg = {
7608             tag: 'td'
7609         };
7610         
7611         if(this.tag){
7612             cfg.tag = this.tag;
7613         }
7614         
7615         if (this.html) {
7616             cfg.html=this.html
7617         }
7618         if (this.cls) {
7619             cfg.cls=this.cls
7620         }
7621         if (this.abbr) {
7622             cfg.abbr=this.abbr
7623         }
7624         if (this.align) {
7625             cfg.align=this.align
7626         }
7627         if (this.axis) {
7628             cfg.axis=this.axis
7629         }
7630         if (this.bgcolor) {
7631             cfg.bgcolor=this.bgcolor
7632         }
7633         if (this.charoff) {
7634             cfg.charoff=this.charoff
7635         }
7636         if (this.colspan) {
7637             cfg.colspan=this.colspan
7638         }
7639         if (this.headers) {
7640             cfg.headers=this.headers
7641         }
7642         if (this.height) {
7643             cfg.height=this.height
7644         }
7645         if (this.nowrap) {
7646             cfg.nowrap=this.nowrap
7647         }
7648         if (this.rowspan) {
7649             cfg.rowspan=this.rowspan
7650         }
7651         if (this.scope) {
7652             cfg.scope=this.scope
7653         }
7654         if (this.valign) {
7655             cfg.valign=this.valign
7656         }
7657         if (this.width) {
7658             cfg.width=this.width
7659         }
7660         
7661         
7662         return cfg;
7663     }
7664    
7665 });
7666
7667  
7668
7669  /*
7670  * - LGPL
7671  *
7672  * table row
7673  * 
7674  */
7675
7676 /**
7677  * @class Roo.bootstrap.TableRow
7678  * @extends Roo.bootstrap.Component
7679  * Bootstrap TableRow class
7680  * @cfg {String} cls row class
7681  * @cfg {String} align Aligns the content in a table row
7682  * @cfg {String} bgcolor Specifies a background color for a table row
7683  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7684  * @cfg {String} valign Vertical aligns the content in a table row
7685  * 
7686  * @constructor
7687  * Create a new TableRow
7688  * @param {Object} config The config object
7689  */
7690
7691 Roo.bootstrap.TableRow = function(config){
7692     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7693 };
7694
7695 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7696     
7697     cls: false,
7698     align: false,
7699     bgcolor: false,
7700     charoff: false,
7701     valign: false,
7702     
7703     getAutoCreate : function(){
7704         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7705         
7706         cfg = {
7707             tag: 'tr'
7708         };
7709             
7710         if(this.cls){
7711             cfg.cls = this.cls;
7712         }
7713         if(this.align){
7714             cfg.align = this.align;
7715         }
7716         if(this.bgcolor){
7717             cfg.bgcolor = this.bgcolor;
7718         }
7719         if(this.charoff){
7720             cfg.charoff = this.charoff;
7721         }
7722         if(this.valign){
7723             cfg.valign = this.valign;
7724         }
7725         
7726         return cfg;
7727     }
7728    
7729 });
7730
7731  
7732
7733  /*
7734  * - LGPL
7735  *
7736  * table body
7737  * 
7738  */
7739
7740 /**
7741  * @class Roo.bootstrap.TableBody
7742  * @extends Roo.bootstrap.Component
7743  * Bootstrap TableBody class
7744  * @cfg {String} cls element class
7745  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7746  * @cfg {String} align Aligns the content inside the element
7747  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7748  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7749  * 
7750  * @constructor
7751  * Create a new TableBody
7752  * @param {Object} config The config object
7753  */
7754
7755 Roo.bootstrap.TableBody = function(config){
7756     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7757 };
7758
7759 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7760     
7761     cls: false,
7762     tag: false,
7763     align: false,
7764     charoff: false,
7765     valign: false,
7766     
7767     getAutoCreate : function(){
7768         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7769         
7770         cfg = {
7771             tag: 'tbody'
7772         };
7773             
7774         if (this.cls) {
7775             cfg.cls=this.cls
7776         }
7777         if(this.tag){
7778             cfg.tag = this.tag;
7779         }
7780         
7781         if(this.align){
7782             cfg.align = this.align;
7783         }
7784         if(this.charoff){
7785             cfg.charoff = this.charoff;
7786         }
7787         if(this.valign){
7788             cfg.valign = this.valign;
7789         }
7790         
7791         return cfg;
7792     }
7793     
7794     
7795 //    initEvents : function()
7796 //    {
7797 //        
7798 //        if(!this.store){
7799 //            return;
7800 //        }
7801 //        
7802 //        this.store = Roo.factory(this.store, Roo.data);
7803 //        this.store.on('load', this.onLoad, this);
7804 //        
7805 //        this.store.load();
7806 //        
7807 //    },
7808 //    
7809 //    onLoad: function () 
7810 //    {   
7811 //        this.fireEvent('load', this);
7812 //    }
7813 //    
7814 //   
7815 });
7816
7817  
7818
7819  /*
7820  * Based on:
7821  * Ext JS Library 1.1.1
7822  * Copyright(c) 2006-2007, Ext JS, LLC.
7823  *
7824  * Originally Released Under LGPL - original licence link has changed is not relivant.
7825  *
7826  * Fork - LGPL
7827  * <script type="text/javascript">
7828  */
7829
7830 // as we use this in bootstrap.
7831 Roo.namespace('Roo.form');
7832  /**
7833  * @class Roo.form.Action
7834  * Internal Class used to handle form actions
7835  * @constructor
7836  * @param {Roo.form.BasicForm} el The form element or its id
7837  * @param {Object} config Configuration options
7838  */
7839
7840  
7841  
7842 // define the action interface
7843 Roo.form.Action = function(form, options){
7844     this.form = form;
7845     this.options = options || {};
7846 };
7847 /**
7848  * Client Validation Failed
7849  * @const 
7850  */
7851 Roo.form.Action.CLIENT_INVALID = 'client';
7852 /**
7853  * Server Validation Failed
7854  * @const 
7855  */
7856 Roo.form.Action.SERVER_INVALID = 'server';
7857  /**
7858  * Connect to Server Failed
7859  * @const 
7860  */
7861 Roo.form.Action.CONNECT_FAILURE = 'connect';
7862 /**
7863  * Reading Data from Server Failed
7864  * @const 
7865  */
7866 Roo.form.Action.LOAD_FAILURE = 'load';
7867
7868 Roo.form.Action.prototype = {
7869     type : 'default',
7870     failureType : undefined,
7871     response : undefined,
7872     result : undefined,
7873
7874     // interface method
7875     run : function(options){
7876
7877     },
7878
7879     // interface method
7880     success : function(response){
7881
7882     },
7883
7884     // interface method
7885     handleResponse : function(response){
7886
7887     },
7888
7889     // default connection failure
7890     failure : function(response){
7891         
7892         this.response = response;
7893         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7894         this.form.afterAction(this, false);
7895     },
7896
7897     processResponse : function(response){
7898         this.response = response;
7899         if(!response.responseText){
7900             return true;
7901         }
7902         this.result = this.handleResponse(response);
7903         return this.result;
7904     },
7905
7906     // utility functions used internally
7907     getUrl : function(appendParams){
7908         var url = this.options.url || this.form.url || this.form.el.dom.action;
7909         if(appendParams){
7910             var p = this.getParams();
7911             if(p){
7912                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7913             }
7914         }
7915         return url;
7916     },
7917
7918     getMethod : function(){
7919         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7920     },
7921
7922     getParams : function(){
7923         var bp = this.form.baseParams;
7924         var p = this.options.params;
7925         if(p){
7926             if(typeof p == "object"){
7927                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7928             }else if(typeof p == 'string' && bp){
7929                 p += '&' + Roo.urlEncode(bp);
7930             }
7931         }else if(bp){
7932             p = Roo.urlEncode(bp);
7933         }
7934         return p;
7935     },
7936
7937     createCallback : function(){
7938         return {
7939             success: this.success,
7940             failure: this.failure,
7941             scope: this,
7942             timeout: (this.form.timeout*1000),
7943             upload: this.form.fileUpload ? this.success : undefined
7944         };
7945     }
7946 };
7947
7948 Roo.form.Action.Submit = function(form, options){
7949     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7950 };
7951
7952 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7953     type : 'submit',
7954
7955     haveProgress : false,
7956     uploadComplete : false,
7957     
7958     // uploadProgress indicator.
7959     uploadProgress : function()
7960     {
7961         if (!this.form.progressUrl) {
7962             return;
7963         }
7964         
7965         if (!this.haveProgress) {
7966             Roo.MessageBox.progress("Uploading", "Uploading");
7967         }
7968         if (this.uploadComplete) {
7969            Roo.MessageBox.hide();
7970            return;
7971         }
7972         
7973         this.haveProgress = true;
7974    
7975         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7976         
7977         var c = new Roo.data.Connection();
7978         c.request({
7979             url : this.form.progressUrl,
7980             params: {
7981                 id : uid
7982             },
7983             method: 'GET',
7984             success : function(req){
7985                //console.log(data);
7986                 var rdata = false;
7987                 var edata;
7988                 try  {
7989                    rdata = Roo.decode(req.responseText)
7990                 } catch (e) {
7991                     Roo.log("Invalid data from server..");
7992                     Roo.log(edata);
7993                     return;
7994                 }
7995                 if (!rdata || !rdata.success) {
7996                     Roo.log(rdata);
7997                     Roo.MessageBox.alert(Roo.encode(rdata));
7998                     return;
7999                 }
8000                 var data = rdata.data;
8001                 
8002                 if (this.uploadComplete) {
8003                    Roo.MessageBox.hide();
8004                    return;
8005                 }
8006                    
8007                 if (data){
8008                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8009                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8010                     );
8011                 }
8012                 this.uploadProgress.defer(2000,this);
8013             },
8014        
8015             failure: function(data) {
8016                 Roo.log('progress url failed ');
8017                 Roo.log(data);
8018             },
8019             scope : this
8020         });
8021            
8022     },
8023     
8024     
8025     run : function()
8026     {
8027         // run get Values on the form, so it syncs any secondary forms.
8028         this.form.getValues();
8029         
8030         var o = this.options;
8031         var method = this.getMethod();
8032         var isPost = method == 'POST';
8033         if(o.clientValidation === false || this.form.isValid()){
8034             
8035             if (this.form.progressUrl) {
8036                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8037                     (new Date() * 1) + '' + Math.random());
8038                     
8039             } 
8040             
8041             
8042             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8043                 form:this.form.el.dom,
8044                 url:this.getUrl(!isPost),
8045                 method: method,
8046                 params:isPost ? this.getParams() : null,
8047                 isUpload: this.form.fileUpload,
8048                 formData : this.form.formData
8049             }));
8050             
8051             this.uploadProgress();
8052
8053         }else if (o.clientValidation !== false){ // client validation failed
8054             this.failureType = Roo.form.Action.CLIENT_INVALID;
8055             this.form.afterAction(this, false);
8056         }
8057     },
8058
8059     success : function(response)
8060     {
8061         this.uploadComplete= true;
8062         if (this.haveProgress) {
8063             Roo.MessageBox.hide();
8064         }
8065         
8066         
8067         var result = this.processResponse(response);
8068         if(result === true || result.success){
8069             this.form.afterAction(this, true);
8070             return;
8071         }
8072         if(result.errors){
8073             this.form.markInvalid(result.errors);
8074             this.failureType = Roo.form.Action.SERVER_INVALID;
8075         }
8076         this.form.afterAction(this, false);
8077     },
8078     failure : function(response)
8079     {
8080         this.uploadComplete= true;
8081         if (this.haveProgress) {
8082             Roo.MessageBox.hide();
8083         }
8084         
8085         this.response = response;
8086         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8087         this.form.afterAction(this, false);
8088     },
8089     
8090     handleResponse : function(response){
8091         if(this.form.errorReader){
8092             var rs = this.form.errorReader.read(response);
8093             var errors = [];
8094             if(rs.records){
8095                 for(var i = 0, len = rs.records.length; i < len; i++) {
8096                     var r = rs.records[i];
8097                     errors[i] = r.data;
8098                 }
8099             }
8100             if(errors.length < 1){
8101                 errors = null;
8102             }
8103             return {
8104                 success : rs.success,
8105                 errors : errors
8106             };
8107         }
8108         var ret = false;
8109         try {
8110             ret = Roo.decode(response.responseText);
8111         } catch (e) {
8112             ret = {
8113                 success: false,
8114                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8115                 errors : []
8116             };
8117         }
8118         return ret;
8119         
8120     }
8121 });
8122
8123
8124 Roo.form.Action.Load = function(form, options){
8125     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8126     this.reader = this.form.reader;
8127 };
8128
8129 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8130     type : 'load',
8131
8132     run : function(){
8133         
8134         Roo.Ajax.request(Roo.apply(
8135                 this.createCallback(), {
8136                     method:this.getMethod(),
8137                     url:this.getUrl(false),
8138                     params:this.getParams()
8139         }));
8140     },
8141
8142     success : function(response){
8143         
8144         var result = this.processResponse(response);
8145         if(result === true || !result.success || !result.data){
8146             this.failureType = Roo.form.Action.LOAD_FAILURE;
8147             this.form.afterAction(this, false);
8148             return;
8149         }
8150         this.form.clearInvalid();
8151         this.form.setValues(result.data);
8152         this.form.afterAction(this, true);
8153     },
8154
8155     handleResponse : function(response){
8156         if(this.form.reader){
8157             var rs = this.form.reader.read(response);
8158             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8159             return {
8160                 success : rs.success,
8161                 data : data
8162             };
8163         }
8164         return Roo.decode(response.responseText);
8165     }
8166 });
8167
8168 Roo.form.Action.ACTION_TYPES = {
8169     'load' : Roo.form.Action.Load,
8170     'submit' : Roo.form.Action.Submit
8171 };/*
8172  * - LGPL
8173  *
8174  * form
8175  *
8176  */
8177
8178 /**
8179  * @class Roo.bootstrap.Form
8180  * @extends Roo.bootstrap.Component
8181  * Bootstrap Form class
8182  * @cfg {String} method  GET | POST (default POST)
8183  * @cfg {String} labelAlign top | left (default top)
8184  * @cfg {String} align left  | right - for navbars
8185  * @cfg {Boolean} loadMask load mask when submit (default true)
8186
8187  *
8188  * @constructor
8189  * Create a new Form
8190  * @param {Object} config The config object
8191  */
8192
8193
8194 Roo.bootstrap.Form = function(config){
8195     
8196     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8197     
8198     Roo.bootstrap.Form.popover.apply();
8199     
8200     this.addEvents({
8201         /**
8202          * @event clientvalidation
8203          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8204          * @param {Form} this
8205          * @param {Boolean} valid true if the form has passed client-side validation
8206          */
8207         clientvalidation: true,
8208         /**
8209          * @event beforeaction
8210          * Fires before any action is performed. Return false to cancel the action.
8211          * @param {Form} this
8212          * @param {Action} action The action to be performed
8213          */
8214         beforeaction: true,
8215         /**
8216          * @event actionfailed
8217          * Fires when an action fails.
8218          * @param {Form} this
8219          * @param {Action} action The action that failed
8220          */
8221         actionfailed : true,
8222         /**
8223          * @event actioncomplete
8224          * Fires when an action is completed.
8225          * @param {Form} this
8226          * @param {Action} action The action that completed
8227          */
8228         actioncomplete : true
8229     });
8230 };
8231
8232 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8233
8234      /**
8235      * @cfg {String} method
8236      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8237      */
8238     method : 'POST',
8239     /**
8240      * @cfg {String} url
8241      * The URL to use for form actions if one isn't supplied in the action options.
8242      */
8243     /**
8244      * @cfg {Boolean} fileUpload
8245      * Set to true if this form is a file upload.
8246      */
8247
8248     /**
8249      * @cfg {Object} baseParams
8250      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8251      */
8252
8253     /**
8254      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8255      */
8256     timeout: 30,
8257     /**
8258      * @cfg {Sting} align (left|right) for navbar forms
8259      */
8260     align : 'left',
8261
8262     // private
8263     activeAction : null,
8264
8265     /**
8266      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8267      * element by passing it or its id or mask the form itself by passing in true.
8268      * @type Mixed
8269      */
8270     waitMsgTarget : false,
8271
8272     loadMask : true,
8273     
8274     /**
8275      * @cfg {Boolean} errorMask (true|false) default false
8276      */
8277     errorMask : false,
8278     
8279     /**
8280      * @cfg {Number} maskOffset Default 100
8281      */
8282     maskOffset : 100,
8283     
8284     /**
8285      * @cfg {Boolean} maskBody
8286      */
8287     maskBody : false,
8288
8289     getAutoCreate : function(){
8290
8291         var cfg = {
8292             tag: 'form',
8293             method : this.method || 'POST',
8294             id : this.id || Roo.id(),
8295             cls : ''
8296         };
8297         if (this.parent().xtype.match(/^Nav/)) {
8298             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8299
8300         }
8301
8302         if (this.labelAlign == 'left' ) {
8303             cfg.cls += ' form-horizontal';
8304         }
8305
8306
8307         return cfg;
8308     },
8309     initEvents : function()
8310     {
8311         this.el.on('submit', this.onSubmit, this);
8312         // this was added as random key presses on the form where triggering form submit.
8313         this.el.on('keypress', function(e) {
8314             if (e.getCharCode() != 13) {
8315                 return true;
8316             }
8317             // we might need to allow it for textareas.. and some other items.
8318             // check e.getTarget().
8319
8320             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8321                 return true;
8322             }
8323
8324             Roo.log("keypress blocked");
8325
8326             e.preventDefault();
8327             return false;
8328         });
8329         
8330     },
8331     // private
8332     onSubmit : function(e){
8333         e.stopEvent();
8334     },
8335
8336      /**
8337      * Returns true if client-side validation on the form is successful.
8338      * @return Boolean
8339      */
8340     isValid : function(){
8341         var items = this.getItems();
8342         var valid = true;
8343         var target = false;
8344         
8345         items.each(function(f){
8346             
8347             if(f.validate()){
8348                 return;
8349             }
8350             
8351             Roo.log('invalid field: ' + f.name);
8352             
8353             valid = false;
8354
8355             if(!target && f.el.isVisible(true)){
8356                 target = f;
8357             }
8358            
8359         });
8360         
8361         if(this.errorMask && !valid){
8362             Roo.bootstrap.Form.popover.mask(this, target);
8363         }
8364         
8365         return valid;
8366     },
8367     
8368     /**
8369      * Returns true if any fields in this form have changed since their original load.
8370      * @return Boolean
8371      */
8372     isDirty : function(){
8373         var dirty = false;
8374         var items = this.getItems();
8375         items.each(function(f){
8376            if(f.isDirty()){
8377                dirty = true;
8378                return false;
8379            }
8380            return true;
8381         });
8382         return dirty;
8383     },
8384      /**
8385      * Performs a predefined action (submit or load) or custom actions you define on this form.
8386      * @param {String} actionName The name of the action type
8387      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8388      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8389      * accept other config options):
8390      * <pre>
8391 Property          Type             Description
8392 ----------------  ---------------  ----------------------------------------------------------------------------------
8393 url               String           The url for the action (defaults to the form's url)
8394 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8395 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8396 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8397                                    validate the form on the client (defaults to false)
8398      * </pre>
8399      * @return {BasicForm} this
8400      */
8401     doAction : function(action, options){
8402         if(typeof action == 'string'){
8403             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8404         }
8405         if(this.fireEvent('beforeaction', this, action) !== false){
8406             this.beforeAction(action);
8407             action.run.defer(100, action);
8408         }
8409         return this;
8410     },
8411
8412     // private
8413     beforeAction : function(action){
8414         var o = action.options;
8415         
8416         if(this.loadMask){
8417             
8418             if(this.maskBody){
8419                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8420             } else {
8421                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8422             }
8423         }
8424         // not really supported yet.. ??
8425
8426         //if(this.waitMsgTarget === true){
8427         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8428         //}else if(this.waitMsgTarget){
8429         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8430         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8431         //}else {
8432         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8433        // }
8434
8435     },
8436
8437     // private
8438     afterAction : function(action, success){
8439         this.activeAction = null;
8440         var o = action.options;
8441
8442         if(this.loadMask){
8443             
8444             if(this.maskBody){
8445                 Roo.get(document.body).unmask();
8446             } else {
8447                 this.el.unmask();
8448             }
8449         }
8450         
8451         //if(this.waitMsgTarget === true){
8452 //            this.el.unmask();
8453         //}else if(this.waitMsgTarget){
8454         //    this.waitMsgTarget.unmask();
8455         //}else{
8456         //    Roo.MessageBox.updateProgress(1);
8457         //    Roo.MessageBox.hide();
8458        // }
8459         //
8460         if(success){
8461             if(o.reset){
8462                 this.reset();
8463             }
8464             Roo.callback(o.success, o.scope, [this, action]);
8465             this.fireEvent('actioncomplete', this, action);
8466
8467         }else{
8468
8469             // failure condition..
8470             // we have a scenario where updates need confirming.
8471             // eg. if a locking scenario exists..
8472             // we look for { errors : { needs_confirm : true }} in the response.
8473             if (
8474                 (typeof(action.result) != 'undefined')  &&
8475                 (typeof(action.result.errors) != 'undefined')  &&
8476                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8477            ){
8478                 var _t = this;
8479                 Roo.log("not supported yet");
8480                  /*
8481
8482                 Roo.MessageBox.confirm(
8483                     "Change requires confirmation",
8484                     action.result.errorMsg,
8485                     function(r) {
8486                         if (r != 'yes') {
8487                             return;
8488                         }
8489                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8490                     }
8491
8492                 );
8493                 */
8494
8495
8496                 return;
8497             }
8498
8499             Roo.callback(o.failure, o.scope, [this, action]);
8500             // show an error message if no failed handler is set..
8501             if (!this.hasListener('actionfailed')) {
8502                 Roo.log("need to add dialog support");
8503                 /*
8504                 Roo.MessageBox.alert("Error",
8505                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8506                         action.result.errorMsg :
8507                         "Saving Failed, please check your entries or try again"
8508                 );
8509                 */
8510             }
8511
8512             this.fireEvent('actionfailed', this, action);
8513         }
8514
8515     },
8516     /**
8517      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8518      * @param {String} id The value to search for
8519      * @return Field
8520      */
8521     findField : function(id){
8522         var items = this.getItems();
8523         var field = items.get(id);
8524         if(!field){
8525              items.each(function(f){
8526                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8527                     field = f;
8528                     return false;
8529                 }
8530                 return true;
8531             });
8532         }
8533         return field || null;
8534     },
8535      /**
8536      * Mark fields in this form invalid in bulk.
8537      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8538      * @return {BasicForm} this
8539      */
8540     markInvalid : function(errors){
8541         if(errors instanceof Array){
8542             for(var i = 0, len = errors.length; i < len; i++){
8543                 var fieldError = errors[i];
8544                 var f = this.findField(fieldError.id);
8545                 if(f){
8546                     f.markInvalid(fieldError.msg);
8547                 }
8548             }
8549         }else{
8550             var field, id;
8551             for(id in errors){
8552                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8553                     field.markInvalid(errors[id]);
8554                 }
8555             }
8556         }
8557         //Roo.each(this.childForms || [], function (f) {
8558         //    f.markInvalid(errors);
8559         //});
8560
8561         return this;
8562     },
8563
8564     /**
8565      * Set values for fields in this form in bulk.
8566      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8567      * @return {BasicForm} this
8568      */
8569     setValues : function(values){
8570         if(values instanceof Array){ // array of objects
8571             for(var i = 0, len = values.length; i < len; i++){
8572                 var v = values[i];
8573                 var f = this.findField(v.id);
8574                 if(f){
8575                     f.setValue(v.value);
8576                     if(this.trackResetOnLoad){
8577                         f.originalValue = f.getValue();
8578                     }
8579                 }
8580             }
8581         }else{ // object hash
8582             var field, id;
8583             for(id in values){
8584                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8585
8586                     if (field.setFromData &&
8587                         field.valueField &&
8588                         field.displayField &&
8589                         // combos' with local stores can
8590                         // be queried via setValue()
8591                         // to set their value..
8592                         (field.store && !field.store.isLocal)
8593                         ) {
8594                         // it's a combo
8595                         var sd = { };
8596                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8597                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8598                         field.setFromData(sd);
8599
8600                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8601                         
8602                         field.setFromData(values);
8603                         
8604                     } else {
8605                         field.setValue(values[id]);
8606                     }
8607
8608
8609                     if(this.trackResetOnLoad){
8610                         field.originalValue = field.getValue();
8611                     }
8612                 }
8613             }
8614         }
8615
8616         //Roo.each(this.childForms || [], function (f) {
8617         //    f.setValues(values);
8618         //});
8619
8620         return this;
8621     },
8622
8623     /**
8624      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8625      * they are returned as an array.
8626      * @param {Boolean} asString
8627      * @return {Object}
8628      */
8629     getValues : function(asString){
8630         //if (this.childForms) {
8631             // copy values from the child forms
8632         //    Roo.each(this.childForms, function (f) {
8633         //        this.setValues(f.getValues());
8634         //    }, this);
8635         //}
8636
8637
8638
8639         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8640         if(asString === true){
8641             return fs;
8642         }
8643         return Roo.urlDecode(fs);
8644     },
8645
8646     /**
8647      * Returns the fields in this form as an object with key/value pairs.
8648      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8649      * @return {Object}
8650      */
8651     getFieldValues : function(with_hidden)
8652     {
8653         var items = this.getItems();
8654         var ret = {};
8655         items.each(function(f){
8656             
8657             if (!f.getName()) {
8658                 return;
8659             }
8660             
8661             var v = f.getValue();
8662             
8663             if (f.inputType =='radio') {
8664                 if (typeof(ret[f.getName()]) == 'undefined') {
8665                     ret[f.getName()] = ''; // empty..
8666                 }
8667
8668                 if (!f.el.dom.checked) {
8669                     return;
8670
8671                 }
8672                 v = f.el.dom.value;
8673
8674             }
8675             
8676             if(f.xtype == 'MoneyField'){
8677                 ret[f.currencyName] = f.getCurrency();
8678             }
8679
8680             // not sure if this supported any more..
8681             if ((typeof(v) == 'object') && f.getRawValue) {
8682                 v = f.getRawValue() ; // dates..
8683             }
8684             // combo boxes where name != hiddenName...
8685             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8686                 ret[f.name] = f.getRawValue();
8687             }
8688             ret[f.getName()] = v;
8689         });
8690
8691         return ret;
8692     },
8693
8694     /**
8695      * Clears all invalid messages in this form.
8696      * @return {BasicForm} this
8697      */
8698     clearInvalid : function(){
8699         var items = this.getItems();
8700
8701         items.each(function(f){
8702            f.clearInvalid();
8703         });
8704
8705         return this;
8706     },
8707
8708     /**
8709      * Resets this form.
8710      * @return {BasicForm} this
8711      */
8712     reset : function(){
8713         var items = this.getItems();
8714         items.each(function(f){
8715             f.reset();
8716         });
8717
8718         Roo.each(this.childForms || [], function (f) {
8719             f.reset();
8720         });
8721
8722
8723         return this;
8724     },
8725     
8726     getItems : function()
8727     {
8728         var r=new Roo.util.MixedCollection(false, function(o){
8729             return o.id || (o.id = Roo.id());
8730         });
8731         var iter = function(el) {
8732             if (el.inputEl) {
8733                 r.add(el);
8734             }
8735             if (!el.items) {
8736                 return;
8737             }
8738             Roo.each(el.items,function(e) {
8739                 iter(e);
8740             });
8741         };
8742
8743         iter(this);
8744         return r;
8745     },
8746     
8747     hideFields : function(items)
8748     {
8749         Roo.each(items, function(i){
8750             
8751             var f = this.findField(i);
8752             
8753             if(!f){
8754                 return;
8755             }
8756             
8757             f.hide();
8758             
8759         }, this);
8760     },
8761     
8762     showFields : function(items)
8763     {
8764         Roo.each(items, function(i){
8765             
8766             var f = this.findField(i);
8767             
8768             if(!f){
8769                 return;
8770             }
8771             
8772             f.show();
8773             
8774         }, this);
8775     }
8776
8777 });
8778
8779 Roo.apply(Roo.bootstrap.Form, {
8780     
8781     popover : {
8782         
8783         padding : 5,
8784         
8785         isApplied : false,
8786         
8787         isMasked : false,
8788         
8789         form : false,
8790         
8791         target : false,
8792         
8793         toolTip : false,
8794         
8795         intervalID : false,
8796         
8797         maskEl : false,
8798         
8799         apply : function()
8800         {
8801             if(this.isApplied){
8802                 return;
8803             }
8804             
8805             this.maskEl = {
8806                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8807                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8808                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8809                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8810             };
8811             
8812             this.maskEl.top.enableDisplayMode("block");
8813             this.maskEl.left.enableDisplayMode("block");
8814             this.maskEl.bottom.enableDisplayMode("block");
8815             this.maskEl.right.enableDisplayMode("block");
8816             
8817             this.toolTip = new Roo.bootstrap.Tooltip({
8818                 cls : 'roo-form-error-popover',
8819                 alignment : {
8820                     'left' : ['r-l', [-2,0], 'right'],
8821                     'right' : ['l-r', [2,0], 'left'],
8822                     'bottom' : ['tl-bl', [0,2], 'top'],
8823                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8824                 }
8825             });
8826             
8827             this.toolTip.render(Roo.get(document.body));
8828
8829             this.toolTip.el.enableDisplayMode("block");
8830             
8831             Roo.get(document.body).on('click', function(){
8832                 this.unmask();
8833             }, this);
8834             
8835             Roo.get(document.body).on('touchstart', function(){
8836                 this.unmask();
8837             }, this);
8838             
8839             this.isApplied = true
8840         },
8841         
8842         mask : function(form, target)
8843         {
8844             this.form = form;
8845             
8846             this.target = target;
8847             
8848             if(!this.form.errorMask || !target.el){
8849                 return;
8850             }
8851             
8852             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8853             
8854             Roo.log(scrollable);
8855             
8856             var ot = this.target.el.calcOffsetsTo(scrollable);
8857             
8858             var scrollTo = ot[1] - this.form.maskOffset;
8859             
8860             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8861             
8862             scrollable.scrollTo('top', scrollTo);
8863             
8864             var box = this.target.el.getBox();
8865             Roo.log(box);
8866             var zIndex = Roo.bootstrap.Modal.zIndex++;
8867
8868             
8869             this.maskEl.top.setStyle('position', 'absolute');
8870             this.maskEl.top.setStyle('z-index', zIndex);
8871             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8872             this.maskEl.top.setLeft(0);
8873             this.maskEl.top.setTop(0);
8874             this.maskEl.top.show();
8875             
8876             this.maskEl.left.setStyle('position', 'absolute');
8877             this.maskEl.left.setStyle('z-index', zIndex);
8878             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8879             this.maskEl.left.setLeft(0);
8880             this.maskEl.left.setTop(box.y - this.padding);
8881             this.maskEl.left.show();
8882
8883             this.maskEl.bottom.setStyle('position', 'absolute');
8884             this.maskEl.bottom.setStyle('z-index', zIndex);
8885             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8886             this.maskEl.bottom.setLeft(0);
8887             this.maskEl.bottom.setTop(box.bottom + this.padding);
8888             this.maskEl.bottom.show();
8889
8890             this.maskEl.right.setStyle('position', 'absolute');
8891             this.maskEl.right.setStyle('z-index', zIndex);
8892             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8893             this.maskEl.right.setLeft(box.right + this.padding);
8894             this.maskEl.right.setTop(box.y - this.padding);
8895             this.maskEl.right.show();
8896
8897             this.toolTip.bindEl = this.target.el;
8898
8899             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8900
8901             var tip = this.target.blankText;
8902
8903             if(this.target.getValue() !== '' ) {
8904                 
8905                 if (this.target.invalidText.length) {
8906                     tip = this.target.invalidText;
8907                 } else if (this.target.regexText.length){
8908                     tip = this.target.regexText;
8909                 }
8910             }
8911
8912             this.toolTip.show(tip);
8913
8914             this.intervalID = window.setInterval(function() {
8915                 Roo.bootstrap.Form.popover.unmask();
8916             }, 10000);
8917
8918             window.onwheel = function(){ return false;};
8919             
8920             (function(){ this.isMasked = true; }).defer(500, this);
8921             
8922         },
8923         
8924         unmask : function()
8925         {
8926             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8927                 return;
8928             }
8929             
8930             this.maskEl.top.setStyle('position', 'absolute');
8931             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8932             this.maskEl.top.hide();
8933
8934             this.maskEl.left.setStyle('position', 'absolute');
8935             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8936             this.maskEl.left.hide();
8937
8938             this.maskEl.bottom.setStyle('position', 'absolute');
8939             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8940             this.maskEl.bottom.hide();
8941
8942             this.maskEl.right.setStyle('position', 'absolute');
8943             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8944             this.maskEl.right.hide();
8945             
8946             this.toolTip.hide();
8947             
8948             this.toolTip.el.hide();
8949             
8950             window.onwheel = function(){ return true;};
8951             
8952             if(this.intervalID){
8953                 window.clearInterval(this.intervalID);
8954                 this.intervalID = false;
8955             }
8956             
8957             this.isMasked = false;
8958             
8959         }
8960         
8961     }
8962     
8963 });
8964
8965 /*
8966  * Based on:
8967  * Ext JS Library 1.1.1
8968  * Copyright(c) 2006-2007, Ext JS, LLC.
8969  *
8970  * Originally Released Under LGPL - original licence link has changed is not relivant.
8971  *
8972  * Fork - LGPL
8973  * <script type="text/javascript">
8974  */
8975 /**
8976  * @class Roo.form.VTypes
8977  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8978  * @singleton
8979  */
8980 Roo.form.VTypes = function(){
8981     // closure these in so they are only created once.
8982     var alpha = /^[a-zA-Z_]+$/;
8983     var alphanum = /^[a-zA-Z0-9_]+$/;
8984     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8985     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8986
8987     // All these messages and functions are configurable
8988     return {
8989         /**
8990          * The function used to validate email addresses
8991          * @param {String} value The email address
8992          */
8993         'email' : function(v){
8994             return email.test(v);
8995         },
8996         /**
8997          * The error text to display when the email validation function returns false
8998          * @type String
8999          */
9000         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9001         /**
9002          * The keystroke filter mask to be applied on email input
9003          * @type RegExp
9004          */
9005         'emailMask' : /[a-z0-9_\.\-@]/i,
9006
9007         /**
9008          * The function used to validate URLs
9009          * @param {String} value The URL
9010          */
9011         'url' : function(v){
9012             return url.test(v);
9013         },
9014         /**
9015          * The error text to display when the url validation function returns false
9016          * @type String
9017          */
9018         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9019         
9020         /**
9021          * The function used to validate alpha values
9022          * @param {String} value The value
9023          */
9024         'alpha' : function(v){
9025             return alpha.test(v);
9026         },
9027         /**
9028          * The error text to display when the alpha validation function returns false
9029          * @type String
9030          */
9031         'alphaText' : 'This field should only contain letters and _',
9032         /**
9033          * The keystroke filter mask to be applied on alpha input
9034          * @type RegExp
9035          */
9036         'alphaMask' : /[a-z_]/i,
9037
9038         /**
9039          * The function used to validate alphanumeric values
9040          * @param {String} value The value
9041          */
9042         'alphanum' : function(v){
9043             return alphanum.test(v);
9044         },
9045         /**
9046          * The error text to display when the alphanumeric validation function returns false
9047          * @type String
9048          */
9049         'alphanumText' : 'This field should only contain letters, numbers and _',
9050         /**
9051          * The keystroke filter mask to be applied on alphanumeric input
9052          * @type RegExp
9053          */
9054         'alphanumMask' : /[a-z0-9_]/i
9055     };
9056 }();/*
9057  * - LGPL
9058  *
9059  * Input
9060  * 
9061  */
9062
9063 /**
9064  * @class Roo.bootstrap.Input
9065  * @extends Roo.bootstrap.Component
9066  * Bootstrap Input class
9067  * @cfg {Boolean} disabled is it disabled
9068  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9069  * @cfg {String} name name of the input
9070  * @cfg {string} fieldLabel - the label associated
9071  * @cfg {string} placeholder - placeholder to put in text.
9072  * @cfg {string}  before - input group add on before
9073  * @cfg {string} after - input group add on after
9074  * @cfg {string} size - (lg|sm) or leave empty..
9075  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9076  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9077  * @cfg {Number} md colspan out of 12 for computer-sized screens
9078  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9079  * @cfg {string} value default value of the input
9080  * @cfg {Number} labelWidth set the width of label 
9081  * @cfg {Number} labellg set the width of label (1-12)
9082  * @cfg {Number} labelmd set the width of label (1-12)
9083  * @cfg {Number} labelsm set the width of label (1-12)
9084  * @cfg {Number} labelxs set the width of label (1-12)
9085  * @cfg {String} labelAlign (top|left)
9086  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9087  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9088  * @cfg {String} indicatorpos (left|right) default left
9089  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9090  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9091
9092  * @cfg {String} align (left|center|right) Default left
9093  * @cfg {Boolean} forceFeedback (true|false) Default false
9094  * 
9095  * @constructor
9096  * Create a new Input
9097  * @param {Object} config The config object
9098  */
9099
9100 Roo.bootstrap.Input = function(config){
9101     
9102     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9103     
9104     this.addEvents({
9105         /**
9106          * @event focus
9107          * Fires when this field receives input focus.
9108          * @param {Roo.form.Field} this
9109          */
9110         focus : true,
9111         /**
9112          * @event blur
9113          * Fires when this field loses input focus.
9114          * @param {Roo.form.Field} this
9115          */
9116         blur : true,
9117         /**
9118          * @event specialkey
9119          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9120          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9121          * @param {Roo.form.Field} this
9122          * @param {Roo.EventObject} e The event object
9123          */
9124         specialkey : true,
9125         /**
9126          * @event change
9127          * Fires just before the field blurs if the field value has changed.
9128          * @param {Roo.form.Field} this
9129          * @param {Mixed} newValue The new value
9130          * @param {Mixed} oldValue The original value
9131          */
9132         change : true,
9133         /**
9134          * @event invalid
9135          * Fires after the field has been marked as invalid.
9136          * @param {Roo.form.Field} this
9137          * @param {String} msg The validation message
9138          */
9139         invalid : true,
9140         /**
9141          * @event valid
9142          * Fires after the field has been validated with no errors.
9143          * @param {Roo.form.Field} this
9144          */
9145         valid : true,
9146          /**
9147          * @event keyup
9148          * Fires after the key up
9149          * @param {Roo.form.Field} this
9150          * @param {Roo.EventObject}  e The event Object
9151          */
9152         keyup : true
9153     });
9154 };
9155
9156 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9157      /**
9158      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9159       automatic validation (defaults to "keyup").
9160      */
9161     validationEvent : "keyup",
9162      /**
9163      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9164      */
9165     validateOnBlur : true,
9166     /**
9167      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9168      */
9169     validationDelay : 250,
9170      /**
9171      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9172      */
9173     focusClass : "x-form-focus",  // not needed???
9174     
9175        
9176     /**
9177      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9178      */
9179     invalidClass : "has-warning",
9180     
9181     /**
9182      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9183      */
9184     validClass : "has-success",
9185     
9186     /**
9187      * @cfg {Boolean} hasFeedback (true|false) default true
9188      */
9189     hasFeedback : true,
9190     
9191     /**
9192      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9193      */
9194     invalidFeedbackClass : "glyphicon-warning-sign",
9195     
9196     /**
9197      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9198      */
9199     validFeedbackClass : "glyphicon-ok",
9200     
9201     /**
9202      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9203      */
9204     selectOnFocus : false,
9205     
9206      /**
9207      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9208      */
9209     maskRe : null,
9210        /**
9211      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9212      */
9213     vtype : null,
9214     
9215       /**
9216      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9217      */
9218     disableKeyFilter : false,
9219     
9220        /**
9221      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9222      */
9223     disabled : false,
9224      /**
9225      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9226      */
9227     allowBlank : true,
9228     /**
9229      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9230      */
9231     blankText : "Please complete this mandatory field",
9232     
9233      /**
9234      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9235      */
9236     minLength : 0,
9237     /**
9238      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9239      */
9240     maxLength : Number.MAX_VALUE,
9241     /**
9242      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9243      */
9244     minLengthText : "The minimum length for this field is {0}",
9245     /**
9246      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9247      */
9248     maxLengthText : "The maximum length for this field is {0}",
9249   
9250     
9251     /**
9252      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9253      * If available, this function will be called only after the basic validators all return true, and will be passed the
9254      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9255      */
9256     validator : null,
9257     /**
9258      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9259      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9260      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9261      */
9262     regex : null,
9263     /**
9264      * @cfg {String} regexText -- Depricated - use Invalid Text
9265      */
9266     regexText : "",
9267     
9268     /**
9269      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9270      */
9271     invalidText : "",
9272     
9273     
9274     
9275     autocomplete: false,
9276     
9277     
9278     fieldLabel : '',
9279     inputType : 'text',
9280     
9281     name : false,
9282     placeholder: false,
9283     before : false,
9284     after : false,
9285     size : false,
9286     hasFocus : false,
9287     preventMark: false,
9288     isFormField : true,
9289     value : '',
9290     labelWidth : 2,
9291     labelAlign : false,
9292     readOnly : false,
9293     align : false,
9294     formatedValue : false,
9295     forceFeedback : false,
9296     
9297     indicatorpos : 'left',
9298     
9299     labellg : 0,
9300     labelmd : 0,
9301     labelsm : 0,
9302     labelxs : 0,
9303     
9304     capture : '',
9305     accept : '',
9306     
9307     parentLabelAlign : function()
9308     {
9309         var parent = this;
9310         while (parent.parent()) {
9311             parent = parent.parent();
9312             if (typeof(parent.labelAlign) !='undefined') {
9313                 return parent.labelAlign;
9314             }
9315         }
9316         return 'left';
9317         
9318     },
9319     
9320     getAutoCreate : function()
9321     {
9322         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9323         
9324         var id = Roo.id();
9325         
9326         var cfg = {};
9327         
9328         if(this.inputType != 'hidden'){
9329             cfg.cls = 'form-group' //input-group
9330         }
9331         
9332         var input =  {
9333             tag: 'input',
9334             id : id,
9335             type : this.inputType,
9336             value : this.value,
9337             cls : 'form-control',
9338             placeholder : this.placeholder || '',
9339             autocomplete : this.autocomplete || 'new-password'
9340         };
9341         
9342         if(this.capture.length){
9343             input.capture = this.capture;
9344         }
9345         
9346         if(this.accept.length){
9347             input.accept = this.accept + "/*";
9348         }
9349         
9350         if(this.align){
9351             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9352         }
9353         
9354         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9355             input.maxLength = this.maxLength;
9356         }
9357         
9358         if (this.disabled) {
9359             input.disabled=true;
9360         }
9361         
9362         if (this.readOnly) {
9363             input.readonly=true;
9364         }
9365         
9366         if (this.name) {
9367             input.name = this.name;
9368         }
9369         
9370         if (this.size) {
9371             input.cls += ' input-' + this.size;
9372         }
9373         
9374         var settings=this;
9375         ['xs','sm','md','lg'].map(function(size){
9376             if (settings[size]) {
9377                 cfg.cls += ' col-' + size + '-' + settings[size];
9378             }
9379         });
9380         
9381         var inputblock = input;
9382         
9383         var feedback = {
9384             tag: 'span',
9385             cls: 'glyphicon form-control-feedback'
9386         };
9387             
9388         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9389             
9390             inputblock = {
9391                 cls : 'has-feedback',
9392                 cn :  [
9393                     input,
9394                     feedback
9395                 ] 
9396             };  
9397         }
9398         
9399         if (this.before || this.after) {
9400             
9401             inputblock = {
9402                 cls : 'input-group',
9403                 cn :  [] 
9404             };
9405             
9406             if (this.before && typeof(this.before) == 'string') {
9407                 
9408                 inputblock.cn.push({
9409                     tag :'span',
9410                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9411                     html : this.before
9412                 });
9413             }
9414             if (this.before && typeof(this.before) == 'object') {
9415                 this.before = Roo.factory(this.before);
9416                 
9417                 inputblock.cn.push({
9418                     tag :'span',
9419                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9420                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9421                 });
9422             }
9423             
9424             inputblock.cn.push(input);
9425             
9426             if (this.after && typeof(this.after) == 'string') {
9427                 inputblock.cn.push({
9428                     tag :'span',
9429                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9430                     html : this.after
9431                 });
9432             }
9433             if (this.after && typeof(this.after) == 'object') {
9434                 this.after = Roo.factory(this.after);
9435                 
9436                 inputblock.cn.push({
9437                     tag :'span',
9438                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9439                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9440                 });
9441             }
9442             
9443             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9444                 inputblock.cls += ' has-feedback';
9445                 inputblock.cn.push(feedback);
9446             }
9447         };
9448         var indicator = {
9449             tag : 'i',
9450             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9451             tooltip : 'This field is required'
9452         };
9453         if (Roo.bootstrap.version == 4) {
9454             indicator = {
9455                 tag : 'i',
9456                 style : 'display-none'
9457             };
9458         }
9459         if (align ==='left' && this.fieldLabel.length) {
9460             
9461             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9462             
9463             cfg.cn = [
9464                 indicator,
9465                 {
9466                     tag: 'label',
9467                     'for' :  id,
9468                     cls : 'control-label col-form-label',
9469                     html : this.fieldLabel
9470
9471                 },
9472                 {
9473                     cls : "", 
9474                     cn: [
9475                         inputblock
9476                     ]
9477                 }
9478             ];
9479             
9480             var labelCfg = cfg.cn[1];
9481             var contentCfg = cfg.cn[2];
9482             
9483             if(this.indicatorpos == 'right'){
9484                 cfg.cn = [
9485                     {
9486                         tag: 'label',
9487                         'for' :  id,
9488                         cls : 'control-label col-form-label',
9489                         cn : [
9490                             {
9491                                 tag : 'span',
9492                                 html : this.fieldLabel
9493                             },
9494                             indicator
9495                         ]
9496                     },
9497                     {
9498                         cls : "",
9499                         cn: [
9500                             inputblock
9501                         ]
9502                     }
9503
9504                 ];
9505                 
9506                 labelCfg = cfg.cn[0];
9507                 contentCfg = cfg.cn[1];
9508             
9509             }
9510             
9511             if(this.labelWidth > 12){
9512                 labelCfg.style = "width: " + this.labelWidth + 'px';
9513             }
9514             
9515             if(this.labelWidth < 13 && this.labelmd == 0){
9516                 this.labelmd = this.labelWidth;
9517             }
9518             
9519             if(this.labellg > 0){
9520                 labelCfg.cls += ' col-lg-' + this.labellg;
9521                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9522             }
9523             
9524             if(this.labelmd > 0){
9525                 labelCfg.cls += ' col-md-' + this.labelmd;
9526                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9527             }
9528             
9529             if(this.labelsm > 0){
9530                 labelCfg.cls += ' col-sm-' + this.labelsm;
9531                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9532             }
9533             
9534             if(this.labelxs > 0){
9535                 labelCfg.cls += ' col-xs-' + this.labelxs;
9536                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9537             }
9538             
9539             
9540         } else if ( this.fieldLabel.length) {
9541                 
9542             cfg.cn = [
9543                 {
9544                     tag : 'i',
9545                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9546                     tooltip : 'This field is required'
9547                 },
9548                 {
9549                     tag: 'label',
9550                    //cls : 'input-group-addon',
9551                     html : this.fieldLabel
9552
9553                 },
9554
9555                inputblock
9556
9557            ];
9558            
9559            if(this.indicatorpos == 'right'){
9560                 
9561                 cfg.cn = [
9562                     {
9563                         tag: 'label',
9564                        //cls : 'input-group-addon',
9565                         html : this.fieldLabel
9566
9567                     },
9568                     {
9569                         tag : 'i',
9570                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9571                         tooltip : 'This field is required'
9572                     },
9573
9574                    inputblock
9575
9576                ];
9577
9578             }
9579
9580         } else {
9581             
9582             cfg.cn = [
9583
9584                     inputblock
9585
9586             ];
9587                 
9588                 
9589         };
9590         
9591         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9592            cfg.cls += ' navbar-form';
9593         }
9594         
9595         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9596             // on BS4 we do this only if not form 
9597             cfg.cls += ' navbar-form';
9598             cfg.tag = 'li';
9599         }
9600         
9601         return cfg;
9602         
9603     },
9604     /**
9605      * return the real input element.
9606      */
9607     inputEl: function ()
9608     {
9609         return this.el.select('input.form-control',true).first();
9610     },
9611     
9612     tooltipEl : function()
9613     {
9614         return this.inputEl();
9615     },
9616     
9617     indicatorEl : function()
9618     {
9619         if (Roo.bootstrap.version == 4) {
9620             return false; // not enabled in v4 yet.
9621         }
9622         
9623         var indicator = this.el.select('i.roo-required-indicator',true).first();
9624         
9625         if(!indicator){
9626             return false;
9627         }
9628         
9629         return indicator;
9630         
9631     },
9632     
9633     setDisabled : function(v)
9634     {
9635         var i  = this.inputEl().dom;
9636         if (!v) {
9637             i.removeAttribute('disabled');
9638             return;
9639             
9640         }
9641         i.setAttribute('disabled','true');
9642     },
9643     initEvents : function()
9644     {
9645           
9646         this.inputEl().on("keydown" , this.fireKey,  this);
9647         this.inputEl().on("focus", this.onFocus,  this);
9648         this.inputEl().on("blur", this.onBlur,  this);
9649         
9650         this.inputEl().relayEvent('keyup', this);
9651         
9652         this.indicator = this.indicatorEl();
9653         
9654         if(this.indicator){
9655             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9656         }
9657  
9658         // reference to original value for reset
9659         this.originalValue = this.getValue();
9660         //Roo.form.TextField.superclass.initEvents.call(this);
9661         if(this.validationEvent == 'keyup'){
9662             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9663             this.inputEl().on('keyup', this.filterValidation, this);
9664         }
9665         else if(this.validationEvent !== false){
9666             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9667         }
9668         
9669         if(this.selectOnFocus){
9670             this.on("focus", this.preFocus, this);
9671             
9672         }
9673         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9674             this.inputEl().on("keypress", this.filterKeys, this);
9675         } else {
9676             this.inputEl().relayEvent('keypress', this);
9677         }
9678        /* if(this.grow){
9679             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9680             this.el.on("click", this.autoSize,  this);
9681         }
9682         */
9683         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9684             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9685         }
9686         
9687         if (typeof(this.before) == 'object') {
9688             this.before.render(this.el.select('.roo-input-before',true).first());
9689         }
9690         if (typeof(this.after) == 'object') {
9691             this.after.render(this.el.select('.roo-input-after',true).first());
9692         }
9693         
9694         this.inputEl().on('change', this.onChange, this);
9695         
9696     },
9697     filterValidation : function(e){
9698         if(!e.isNavKeyPress()){
9699             this.validationTask.delay(this.validationDelay);
9700         }
9701     },
9702      /**
9703      * Validates the field value
9704      * @return {Boolean} True if the value is valid, else false
9705      */
9706     validate : function(){
9707         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9708         if(this.disabled || this.validateValue(this.getRawValue())){
9709             this.markValid();
9710             return true;
9711         }
9712         
9713         this.markInvalid();
9714         return false;
9715     },
9716     
9717     
9718     /**
9719      * Validates a value according to the field's validation rules and marks the field as invalid
9720      * if the validation fails
9721      * @param {Mixed} value The value to validate
9722      * @return {Boolean} True if the value is valid, else false
9723      */
9724     validateValue : function(value)
9725     {
9726         if(this.getVisibilityEl().hasClass('hidden')){
9727             return true;
9728         }
9729         
9730         if(value.length < 1)  { // if it's blank
9731             if(this.allowBlank){
9732                 return true;
9733             }
9734             return false;
9735         }
9736         
9737         if(value.length < this.minLength){
9738             return false;
9739         }
9740         if(value.length > this.maxLength){
9741             return false;
9742         }
9743         if(this.vtype){
9744             var vt = Roo.form.VTypes;
9745             if(!vt[this.vtype](value, this)){
9746                 return false;
9747             }
9748         }
9749         if(typeof this.validator == "function"){
9750             var msg = this.validator(value);
9751             if(msg !== true){
9752                 return false;
9753             }
9754             if (typeof(msg) == 'string') {
9755                 this.invalidText = msg;
9756             }
9757         }
9758         
9759         if(this.regex && !this.regex.test(value)){
9760             return false;
9761         }
9762         
9763         return true;
9764     },
9765     
9766      // private
9767     fireKey : function(e){
9768         //Roo.log('field ' + e.getKey());
9769         if(e.isNavKeyPress()){
9770             this.fireEvent("specialkey", this, e);
9771         }
9772     },
9773     focus : function (selectText){
9774         if(this.rendered){
9775             this.inputEl().focus();
9776             if(selectText === true){
9777                 this.inputEl().dom.select();
9778             }
9779         }
9780         return this;
9781     } ,
9782     
9783     onFocus : function(){
9784         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9785            // this.el.addClass(this.focusClass);
9786         }
9787         if(!this.hasFocus){
9788             this.hasFocus = true;
9789             this.startValue = this.getValue();
9790             this.fireEvent("focus", this);
9791         }
9792     },
9793     
9794     beforeBlur : Roo.emptyFn,
9795
9796     
9797     // private
9798     onBlur : function(){
9799         this.beforeBlur();
9800         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9801             //this.el.removeClass(this.focusClass);
9802         }
9803         this.hasFocus = false;
9804         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9805             this.validate();
9806         }
9807         var v = this.getValue();
9808         if(String(v) !== String(this.startValue)){
9809             this.fireEvent('change', this, v, this.startValue);
9810         }
9811         this.fireEvent("blur", this);
9812     },
9813     
9814     onChange : function(e)
9815     {
9816         var v = this.getValue();
9817         if(String(v) !== String(this.startValue)){
9818             this.fireEvent('change', this, v, this.startValue);
9819         }
9820         
9821     },
9822     
9823     /**
9824      * Resets the current field value to the originally loaded value and clears any validation messages
9825      */
9826     reset : function(){
9827         this.setValue(this.originalValue);
9828         this.validate();
9829     },
9830      /**
9831      * Returns the name of the field
9832      * @return {Mixed} name The name field
9833      */
9834     getName: function(){
9835         return this.name;
9836     },
9837      /**
9838      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9839      * @return {Mixed} value The field value
9840      */
9841     getValue : function(){
9842         
9843         var v = this.inputEl().getValue();
9844         
9845         return v;
9846     },
9847     /**
9848      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9849      * @return {Mixed} value The field value
9850      */
9851     getRawValue : function(){
9852         var v = this.inputEl().getValue();
9853         
9854         return v;
9855     },
9856     
9857     /**
9858      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9859      * @param {Mixed} value The value to set
9860      */
9861     setRawValue : function(v){
9862         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9863     },
9864     
9865     selectText : function(start, end){
9866         var v = this.getRawValue();
9867         if(v.length > 0){
9868             start = start === undefined ? 0 : start;
9869             end = end === undefined ? v.length : end;
9870             var d = this.inputEl().dom;
9871             if(d.setSelectionRange){
9872                 d.setSelectionRange(start, end);
9873             }else if(d.createTextRange){
9874                 var range = d.createTextRange();
9875                 range.moveStart("character", start);
9876                 range.moveEnd("character", v.length-end);
9877                 range.select();
9878             }
9879         }
9880     },
9881     
9882     /**
9883      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9884      * @param {Mixed} value The value to set
9885      */
9886     setValue : function(v){
9887         this.value = v;
9888         if(this.rendered){
9889             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9890             this.validate();
9891         }
9892     },
9893     
9894     /*
9895     processValue : function(value){
9896         if(this.stripCharsRe){
9897             var newValue = value.replace(this.stripCharsRe, '');
9898             if(newValue !== value){
9899                 this.setRawValue(newValue);
9900                 return newValue;
9901             }
9902         }
9903         return value;
9904     },
9905   */
9906     preFocus : function(){
9907         
9908         if(this.selectOnFocus){
9909             this.inputEl().dom.select();
9910         }
9911     },
9912     filterKeys : function(e){
9913         var k = e.getKey();
9914         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9915             return;
9916         }
9917         var c = e.getCharCode(), cc = String.fromCharCode(c);
9918         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9919             return;
9920         }
9921         if(!this.maskRe.test(cc)){
9922             e.stopEvent();
9923         }
9924     },
9925      /**
9926      * Clear any invalid styles/messages for this field
9927      */
9928     clearInvalid : function(){
9929         
9930         if(!this.el || this.preventMark){ // not rendered
9931             return;
9932         }
9933         
9934         
9935         this.el.removeClass([this.invalidClass, 'is-invalid']);
9936         
9937         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9938             
9939             var feedback = this.el.select('.form-control-feedback', true).first();
9940             
9941             if(feedback){
9942                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9943             }
9944             
9945         }
9946         
9947         if(this.indicator){
9948             this.indicator.removeClass('visible');
9949             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9950         }
9951         
9952         this.fireEvent('valid', this);
9953     },
9954     
9955      /**
9956      * Mark this field as valid
9957      */
9958     markValid : function()
9959     {
9960         if(!this.el  || this.preventMark){ // not rendered...
9961             return;
9962         }
9963         
9964         this.el.removeClass([this.invalidClass, this.validClass]);
9965         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9966
9967         var feedback = this.el.select('.form-control-feedback', true).first();
9968             
9969         if(feedback){
9970             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9971         }
9972         
9973         if(this.indicator){
9974             this.indicator.removeClass('visible');
9975             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9976         }
9977         
9978         if(this.disabled){
9979             return;
9980         }
9981         
9982         if(this.allowBlank && !this.getRawValue().length){
9983             return;
9984         }
9985         if (Roo.bootstrap.version == 3) {
9986             this.el.addClass(this.validClass);
9987         } else {
9988             this.inputEl().addClass('is-valid');
9989         }
9990
9991         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9992             
9993             var feedback = this.el.select('.form-control-feedback', true).first();
9994             
9995             if(feedback){
9996                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9997                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9998             }
9999             
10000         }
10001         
10002         this.fireEvent('valid', this);
10003     },
10004     
10005      /**
10006      * Mark this field as invalid
10007      * @param {String} msg The validation message
10008      */
10009     markInvalid : function(msg)
10010     {
10011         if(!this.el  || this.preventMark){ // not rendered
10012             return;
10013         }
10014         
10015         this.el.removeClass([this.invalidClass, this.validClass]);
10016         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10017         
10018         var feedback = this.el.select('.form-control-feedback', true).first();
10019             
10020         if(feedback){
10021             this.el.select('.form-control-feedback', true).first().removeClass(
10022                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10023         }
10024
10025         if(this.disabled){
10026             return;
10027         }
10028         
10029         if(this.allowBlank && !this.getRawValue().length){
10030             return;
10031         }
10032         
10033         if(this.indicator){
10034             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10035             this.indicator.addClass('visible');
10036         }
10037         if (Roo.bootstrap.version == 3) {
10038             this.el.addClass(this.invalidClass);
10039         } else {
10040             this.inputEl().addClass('is-invalid');
10041         }
10042         
10043         
10044         
10045         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10046             
10047             var feedback = this.el.select('.form-control-feedback', true).first();
10048             
10049             if(feedback){
10050                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10051                 
10052                 if(this.getValue().length || this.forceFeedback){
10053                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10054                 }
10055                 
10056             }
10057             
10058         }
10059         
10060         this.fireEvent('invalid', this, msg);
10061     },
10062     // private
10063     SafariOnKeyDown : function(event)
10064     {
10065         // this is a workaround for a password hang bug on chrome/ webkit.
10066         if (this.inputEl().dom.type != 'password') {
10067             return;
10068         }
10069         
10070         var isSelectAll = false;
10071         
10072         if(this.inputEl().dom.selectionEnd > 0){
10073             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10074         }
10075         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10076             event.preventDefault();
10077             this.setValue('');
10078             return;
10079         }
10080         
10081         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10082             
10083             event.preventDefault();
10084             // this is very hacky as keydown always get's upper case.
10085             //
10086             var cc = String.fromCharCode(event.getCharCode());
10087             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10088             
10089         }
10090     },
10091     adjustWidth : function(tag, w){
10092         tag = tag.toLowerCase();
10093         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10094             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10095                 if(tag == 'input'){
10096                     return w + 2;
10097                 }
10098                 if(tag == 'textarea'){
10099                     return w-2;
10100                 }
10101             }else if(Roo.isOpera){
10102                 if(tag == 'input'){
10103                     return w + 2;
10104                 }
10105                 if(tag == 'textarea'){
10106                     return w-2;
10107                 }
10108             }
10109         }
10110         return w;
10111     },
10112     
10113     setFieldLabel : function(v)
10114     {
10115         if(!this.rendered){
10116             return;
10117         }
10118         
10119         if(this.indicatorEl()){
10120             var ar = this.el.select('label > span',true);
10121             
10122             if (ar.elements.length) {
10123                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10124                 this.fieldLabel = v;
10125                 return;
10126             }
10127             
10128             var br = this.el.select('label',true);
10129             
10130             if(br.elements.length) {
10131                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10132                 this.fieldLabel = v;
10133                 return;
10134             }
10135             
10136             Roo.log('Cannot Found any of label > span || label in input');
10137             return;
10138         }
10139         
10140         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10141         this.fieldLabel = v;
10142         
10143         
10144     }
10145 });
10146
10147  
10148 /*
10149  * - LGPL
10150  *
10151  * Input
10152  * 
10153  */
10154
10155 /**
10156  * @class Roo.bootstrap.TextArea
10157  * @extends Roo.bootstrap.Input
10158  * Bootstrap TextArea class
10159  * @cfg {Number} cols Specifies the visible width of a text area
10160  * @cfg {Number} rows Specifies the visible number of lines in a text area
10161  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10162  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10163  * @cfg {string} html text
10164  * 
10165  * @constructor
10166  * Create a new TextArea
10167  * @param {Object} config The config object
10168  */
10169
10170 Roo.bootstrap.TextArea = function(config){
10171     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10172    
10173 };
10174
10175 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10176      
10177     cols : false,
10178     rows : 5,
10179     readOnly : false,
10180     warp : 'soft',
10181     resize : false,
10182     value: false,
10183     html: false,
10184     
10185     getAutoCreate : function(){
10186         
10187         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10188         
10189         var id = Roo.id();
10190         
10191         var cfg = {};
10192         
10193         if(this.inputType != 'hidden'){
10194             cfg.cls = 'form-group' //input-group
10195         }
10196         
10197         var input =  {
10198             tag: 'textarea',
10199             id : id,
10200             warp : this.warp,
10201             rows : this.rows,
10202             value : this.value || '',
10203             html: this.html || '',
10204             cls : 'form-control',
10205             placeholder : this.placeholder || '' 
10206             
10207         };
10208         
10209         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10210             input.maxLength = this.maxLength;
10211         }
10212         
10213         if(this.resize){
10214             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10215         }
10216         
10217         if(this.cols){
10218             input.cols = this.cols;
10219         }
10220         
10221         if (this.readOnly) {
10222             input.readonly = true;
10223         }
10224         
10225         if (this.name) {
10226             input.name = this.name;
10227         }
10228         
10229         if (this.size) {
10230             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10231         }
10232         
10233         var settings=this;
10234         ['xs','sm','md','lg'].map(function(size){
10235             if (settings[size]) {
10236                 cfg.cls += ' col-' + size + '-' + settings[size];
10237             }
10238         });
10239         
10240         var inputblock = input;
10241         
10242         if(this.hasFeedback && !this.allowBlank){
10243             
10244             var feedback = {
10245                 tag: 'span',
10246                 cls: 'glyphicon form-control-feedback'
10247             };
10248
10249             inputblock = {
10250                 cls : 'has-feedback',
10251                 cn :  [
10252                     input,
10253                     feedback
10254                 ] 
10255             };  
10256         }
10257         
10258         
10259         if (this.before || this.after) {
10260             
10261             inputblock = {
10262                 cls : 'input-group',
10263                 cn :  [] 
10264             };
10265             if (this.before) {
10266                 inputblock.cn.push({
10267                     tag :'span',
10268                     cls : 'input-group-addon',
10269                     html : this.before
10270                 });
10271             }
10272             
10273             inputblock.cn.push(input);
10274             
10275             if(this.hasFeedback && !this.allowBlank){
10276                 inputblock.cls += ' has-feedback';
10277                 inputblock.cn.push(feedback);
10278             }
10279             
10280             if (this.after) {
10281                 inputblock.cn.push({
10282                     tag :'span',
10283                     cls : 'input-group-addon',
10284                     html : this.after
10285                 });
10286             }
10287             
10288         }
10289         
10290         if (align ==='left' && this.fieldLabel.length) {
10291             cfg.cn = [
10292                 {
10293                     tag: 'label',
10294                     'for' :  id,
10295                     cls : 'control-label',
10296                     html : this.fieldLabel
10297                 },
10298                 {
10299                     cls : "",
10300                     cn: [
10301                         inputblock
10302                     ]
10303                 }
10304
10305             ];
10306             
10307             if(this.labelWidth > 12){
10308                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10309             }
10310
10311             if(this.labelWidth < 13 && this.labelmd == 0){
10312                 this.labelmd = this.labelWidth;
10313             }
10314
10315             if(this.labellg > 0){
10316                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10317                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10318             }
10319
10320             if(this.labelmd > 0){
10321                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10322                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10323             }
10324
10325             if(this.labelsm > 0){
10326                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10327                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10328             }
10329
10330             if(this.labelxs > 0){
10331                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10332                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10333             }
10334             
10335         } else if ( this.fieldLabel.length) {
10336             cfg.cn = [
10337
10338                {
10339                    tag: 'label',
10340                    //cls : 'input-group-addon',
10341                    html : this.fieldLabel
10342
10343                },
10344
10345                inputblock
10346
10347            ];
10348
10349         } else {
10350
10351             cfg.cn = [
10352
10353                 inputblock
10354
10355             ];
10356                 
10357         }
10358         
10359         if (this.disabled) {
10360             input.disabled=true;
10361         }
10362         
10363         return cfg;
10364         
10365     },
10366     /**
10367      * return the real textarea element.
10368      */
10369     inputEl: function ()
10370     {
10371         return this.el.select('textarea.form-control',true).first();
10372     },
10373     
10374     /**
10375      * Clear any invalid styles/messages for this field
10376      */
10377     clearInvalid : function()
10378     {
10379         
10380         if(!this.el || this.preventMark){ // not rendered
10381             return;
10382         }
10383         
10384         var label = this.el.select('label', true).first();
10385         var icon = this.el.select('i.fa-star', true).first();
10386         
10387         if(label && icon){
10388             icon.remove();
10389         }
10390         this.el.removeClass( this.validClass);
10391         this.inputEl().removeClass('is-invalid');
10392          
10393         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10394             
10395             var feedback = this.el.select('.form-control-feedback', true).first();
10396             
10397             if(feedback){
10398                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10399             }
10400             
10401         }
10402         
10403         this.fireEvent('valid', this);
10404     },
10405     
10406      /**
10407      * Mark this field as valid
10408      */
10409     markValid : function()
10410     {
10411         if(!this.el  || this.preventMark){ // not rendered
10412             return;
10413         }
10414         
10415         this.el.removeClass([this.invalidClass, this.validClass]);
10416         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10417         
10418         var feedback = this.el.select('.form-control-feedback', true).first();
10419             
10420         if(feedback){
10421             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10422         }
10423
10424         if(this.disabled || this.allowBlank){
10425             return;
10426         }
10427         
10428         var label = this.el.select('label', true).first();
10429         var icon = this.el.select('i.fa-star', true).first();
10430         
10431         if(label && icon){
10432             icon.remove();
10433         }
10434         if (Roo.bootstrap.version == 3) {
10435             this.el.addClass(this.validClass);
10436         } else {
10437             this.inputEl().addClass('is-valid');
10438         }
10439         
10440         
10441         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10442             
10443             var feedback = this.el.select('.form-control-feedback', true).first();
10444             
10445             if(feedback){
10446                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10447                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10448             }
10449             
10450         }
10451         
10452         this.fireEvent('valid', this);
10453     },
10454     
10455      /**
10456      * Mark this field as invalid
10457      * @param {String} msg The validation message
10458      */
10459     markInvalid : function(msg)
10460     {
10461         if(!this.el  || this.preventMark){ // not rendered
10462             return;
10463         }
10464         
10465         this.el.removeClass([this.invalidClass, this.validClass]);
10466         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10467         
10468         var feedback = this.el.select('.form-control-feedback', true).first();
10469             
10470         if(feedback){
10471             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10472         }
10473
10474         if(this.disabled || this.allowBlank){
10475             return;
10476         }
10477         
10478         var label = this.el.select('label', true).first();
10479         var icon = this.el.select('i.fa-star', true).first();
10480         
10481         if(!this.getValue().length && label && !icon){
10482             this.el.createChild({
10483                 tag : 'i',
10484                 cls : 'text-danger fa fa-lg fa-star',
10485                 tooltip : 'This field is required',
10486                 style : 'margin-right:5px;'
10487             }, label, true);
10488         }
10489         
10490         if (Roo.bootstrap.version == 3) {
10491             this.el.addClass(this.invalidClass);
10492         } else {
10493             this.inputEl().addClass('is-invalid');
10494         }
10495         
10496         // fixme ... this may be depricated need to test..
10497         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10498             
10499             var feedback = this.el.select('.form-control-feedback', true).first();
10500             
10501             if(feedback){
10502                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10503                 
10504                 if(this.getValue().length || this.forceFeedback){
10505                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10506                 }
10507                 
10508             }
10509             
10510         }
10511         
10512         this.fireEvent('invalid', this, msg);
10513     }
10514 });
10515
10516  
10517 /*
10518  * - LGPL
10519  *
10520  * trigger field - base class for combo..
10521  * 
10522  */
10523  
10524 /**
10525  * @class Roo.bootstrap.TriggerField
10526  * @extends Roo.bootstrap.Input
10527  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10528  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10529  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10530  * for which you can provide a custom implementation.  For example:
10531  * <pre><code>
10532 var trigger = new Roo.bootstrap.TriggerField();
10533 trigger.onTriggerClick = myTriggerFn;
10534 trigger.applyTo('my-field');
10535 </code></pre>
10536  *
10537  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10538  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10539  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10540  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10541  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10542
10543  * @constructor
10544  * Create a new TriggerField.
10545  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10546  * to the base TextField)
10547  */
10548 Roo.bootstrap.TriggerField = function(config){
10549     this.mimicing = false;
10550     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10551 };
10552
10553 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10554     /**
10555      * @cfg {String} triggerClass A CSS class to apply to the trigger
10556      */
10557      /**
10558      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10559      */
10560     hideTrigger:false,
10561
10562     /**
10563      * @cfg {Boolean} removable (true|false) special filter default false
10564      */
10565     removable : false,
10566     
10567     /** @cfg {Boolean} grow @hide */
10568     /** @cfg {Number} growMin @hide */
10569     /** @cfg {Number} growMax @hide */
10570
10571     /**
10572      * @hide 
10573      * @method
10574      */
10575     autoSize: Roo.emptyFn,
10576     // private
10577     monitorTab : true,
10578     // private
10579     deferHeight : true,
10580
10581     
10582     actionMode : 'wrap',
10583     
10584     caret : false,
10585     
10586     
10587     getAutoCreate : function(){
10588        
10589         var align = this.labelAlign || this.parentLabelAlign();
10590         
10591         var id = Roo.id();
10592         
10593         var cfg = {
10594             cls: 'form-group' //input-group
10595         };
10596         
10597         
10598         var input =  {
10599             tag: 'input',
10600             id : id,
10601             type : this.inputType,
10602             cls : 'form-control',
10603             autocomplete: 'new-password',
10604             placeholder : this.placeholder || '' 
10605             
10606         };
10607         if (this.name) {
10608             input.name = this.name;
10609         }
10610         if (this.size) {
10611             input.cls += ' input-' + this.size;
10612         }
10613         
10614         if (this.disabled) {
10615             input.disabled=true;
10616         }
10617         
10618         var inputblock = input;
10619         
10620         if(this.hasFeedback && !this.allowBlank){
10621             
10622             var feedback = {
10623                 tag: 'span',
10624                 cls: 'glyphicon form-control-feedback'
10625             };
10626             
10627             if(this.removable && !this.editable && !this.tickable){
10628                 inputblock = {
10629                     cls : 'has-feedback',
10630                     cn :  [
10631                         inputblock,
10632                         {
10633                             tag: 'button',
10634                             html : 'x',
10635                             cls : 'roo-combo-removable-btn close'
10636                         },
10637                         feedback
10638                     ] 
10639                 };
10640             } else {
10641                 inputblock = {
10642                     cls : 'has-feedback',
10643                     cn :  [
10644                         inputblock,
10645                         feedback
10646                     ] 
10647                 };
10648             }
10649
10650         } else {
10651             if(this.removable && !this.editable && !this.tickable){
10652                 inputblock = {
10653                     cls : 'roo-removable',
10654                     cn :  [
10655                         inputblock,
10656                         {
10657                             tag: 'button',
10658                             html : 'x',
10659                             cls : 'roo-combo-removable-btn close'
10660                         }
10661                     ] 
10662                 };
10663             }
10664         }
10665         
10666         if (this.before || this.after) {
10667             
10668             inputblock = {
10669                 cls : 'input-group',
10670                 cn :  [] 
10671             };
10672             if (this.before) {
10673                 inputblock.cn.push({
10674                     tag :'span',
10675                     cls : 'input-group-addon input-group-prepend input-group-text',
10676                     html : this.before
10677                 });
10678             }
10679             
10680             inputblock.cn.push(input);
10681             
10682             if(this.hasFeedback && !this.allowBlank){
10683                 inputblock.cls += ' has-feedback';
10684                 inputblock.cn.push(feedback);
10685             }
10686             
10687             if (this.after) {
10688                 inputblock.cn.push({
10689                     tag :'span',
10690                     cls : 'input-group-addon input-group-append input-group-text',
10691                     html : this.after
10692                 });
10693             }
10694             
10695         };
10696         
10697       
10698         
10699         var ibwrap = inputblock;
10700         
10701         if(this.multiple){
10702             ibwrap = {
10703                 tag: 'ul',
10704                 cls: 'roo-select2-choices',
10705                 cn:[
10706                     {
10707                         tag: 'li',
10708                         cls: 'roo-select2-search-field',
10709                         cn: [
10710
10711                             inputblock
10712                         ]
10713                     }
10714                 ]
10715             };
10716                 
10717         }
10718         
10719         var combobox = {
10720             cls: 'roo-select2-container input-group',
10721             cn: [
10722                  {
10723                     tag: 'input',
10724                     type : 'hidden',
10725                     cls: 'form-hidden-field'
10726                 },
10727                 ibwrap
10728             ]
10729         };
10730         
10731         if(!this.multiple && this.showToggleBtn){
10732             
10733             var caret = {
10734                         tag: 'span',
10735                         cls: 'caret'
10736              };
10737             if (this.caret != false) {
10738                 caret = {
10739                      tag: 'i',
10740                      cls: 'fa fa-' + this.caret
10741                 };
10742                 
10743             }
10744             
10745             combobox.cn.push({
10746                 tag :'span',
10747                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10748                 cn : [
10749                     Roo.bootstrap.version == 3 ? caret : '',
10750                     {
10751                         tag: 'span',
10752                         cls: 'combobox-clear',
10753                         cn  : [
10754                             {
10755                                 tag : 'i',
10756                                 cls: 'icon-remove'
10757                             }
10758                         ]
10759                     }
10760                 ]
10761
10762             })
10763         }
10764         
10765         if(this.multiple){
10766             combobox.cls += ' roo-select2-container-multi';
10767         }
10768          var indicator = {
10769             tag : 'i',
10770             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10771             tooltip : 'This field is required'
10772         };
10773         if (Roo.bootstrap.version == 4) {
10774             indicator = {
10775                 tag : 'i',
10776                 style : 'display:none'
10777             };
10778         }
10779         
10780         
10781         if (align ==='left' && this.fieldLabel.length) {
10782             
10783             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10784
10785             cfg.cn = [
10786                 indicator,
10787                 {
10788                     tag: 'label',
10789                     'for' :  id,
10790                     cls : 'control-label',
10791                     html : this.fieldLabel
10792
10793                 },
10794                 {
10795                     cls : "", 
10796                     cn: [
10797                         combobox
10798                     ]
10799                 }
10800
10801             ];
10802             
10803             var labelCfg = cfg.cn[1];
10804             var contentCfg = cfg.cn[2];
10805             
10806             if(this.indicatorpos == 'right'){
10807                 cfg.cn = [
10808                     {
10809                         tag: 'label',
10810                         'for' :  id,
10811                         cls : 'control-label',
10812                         cn : [
10813                             {
10814                                 tag : 'span',
10815                                 html : this.fieldLabel
10816                             },
10817                             indicator
10818                         ]
10819                     },
10820                     {
10821                         cls : "", 
10822                         cn: [
10823                             combobox
10824                         ]
10825                     }
10826
10827                 ];
10828                 
10829                 labelCfg = cfg.cn[0];
10830                 contentCfg = cfg.cn[1];
10831             }
10832             
10833             if(this.labelWidth > 12){
10834                 labelCfg.style = "width: " + this.labelWidth + 'px';
10835             }
10836             
10837             if(this.labelWidth < 13 && this.labelmd == 0){
10838                 this.labelmd = this.labelWidth;
10839             }
10840             
10841             if(this.labellg > 0){
10842                 labelCfg.cls += ' col-lg-' + this.labellg;
10843                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10844             }
10845             
10846             if(this.labelmd > 0){
10847                 labelCfg.cls += ' col-md-' + this.labelmd;
10848                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10849             }
10850             
10851             if(this.labelsm > 0){
10852                 labelCfg.cls += ' col-sm-' + this.labelsm;
10853                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10854             }
10855             
10856             if(this.labelxs > 0){
10857                 labelCfg.cls += ' col-xs-' + this.labelxs;
10858                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10859             }
10860             
10861         } else if ( this.fieldLabel.length) {
10862 //                Roo.log(" label");
10863             cfg.cn = [
10864                 indicator,
10865                {
10866                    tag: 'label',
10867                    //cls : 'input-group-addon',
10868                    html : this.fieldLabel
10869
10870                },
10871
10872                combobox
10873
10874             ];
10875             
10876             if(this.indicatorpos == 'right'){
10877                 
10878                 cfg.cn = [
10879                     {
10880                        tag: 'label',
10881                        cn : [
10882                            {
10883                                tag : 'span',
10884                                html : this.fieldLabel
10885                            },
10886                            indicator
10887                        ]
10888
10889                     },
10890                     combobox
10891
10892                 ];
10893
10894             }
10895
10896         } else {
10897             
10898 //                Roo.log(" no label && no align");
10899                 cfg = combobox
10900                      
10901                 
10902         }
10903         
10904         var settings=this;
10905         ['xs','sm','md','lg'].map(function(size){
10906             if (settings[size]) {
10907                 cfg.cls += ' col-' + size + '-' + settings[size];
10908             }
10909         });
10910         
10911         return cfg;
10912         
10913     },
10914     
10915     
10916     
10917     // private
10918     onResize : function(w, h){
10919 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10920 //        if(typeof w == 'number'){
10921 //            var x = w - this.trigger.getWidth();
10922 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10923 //            this.trigger.setStyle('left', x+'px');
10924 //        }
10925     },
10926
10927     // private
10928     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10929
10930     // private
10931     getResizeEl : function(){
10932         return this.inputEl();
10933     },
10934
10935     // private
10936     getPositionEl : function(){
10937         return this.inputEl();
10938     },
10939
10940     // private
10941     alignErrorIcon : function(){
10942         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10943     },
10944
10945     // private
10946     initEvents : function(){
10947         
10948         this.createList();
10949         
10950         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10951         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10952         if(!this.multiple && this.showToggleBtn){
10953             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10954             if(this.hideTrigger){
10955                 this.trigger.setDisplayed(false);
10956             }
10957             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10958         }
10959         
10960         if(this.multiple){
10961             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10962         }
10963         
10964         if(this.removable && !this.editable && !this.tickable){
10965             var close = this.closeTriggerEl();
10966             
10967             if(close){
10968                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10969                 close.on('click', this.removeBtnClick, this, close);
10970             }
10971         }
10972         
10973         //this.trigger.addClassOnOver('x-form-trigger-over');
10974         //this.trigger.addClassOnClick('x-form-trigger-click');
10975         
10976         //if(!this.width){
10977         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10978         //}
10979     },
10980     
10981     closeTriggerEl : function()
10982     {
10983         var close = this.el.select('.roo-combo-removable-btn', true).first();
10984         return close ? close : false;
10985     },
10986     
10987     removeBtnClick : function(e, h, el)
10988     {
10989         e.preventDefault();
10990         
10991         if(this.fireEvent("remove", this) !== false){
10992             this.reset();
10993             this.fireEvent("afterremove", this)
10994         }
10995     },
10996     
10997     createList : function()
10998     {
10999         this.list = Roo.get(document.body).createChild({
11000             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11001             cls: 'typeahead typeahead-long dropdown-menu',
11002             style: 'display:none'
11003         });
11004         
11005         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11006         
11007     },
11008
11009     // private
11010     initTrigger : function(){
11011        
11012     },
11013
11014     // private
11015     onDestroy : function(){
11016         if(this.trigger){
11017             this.trigger.removeAllListeners();
11018           //  this.trigger.remove();
11019         }
11020         //if(this.wrap){
11021         //    this.wrap.remove();
11022         //}
11023         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11024     },
11025
11026     // private
11027     onFocus : function(){
11028         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11029         /*
11030         if(!this.mimicing){
11031             this.wrap.addClass('x-trigger-wrap-focus');
11032             this.mimicing = true;
11033             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11034             if(this.monitorTab){
11035                 this.el.on("keydown", this.checkTab, this);
11036             }
11037         }
11038         */
11039     },
11040
11041     // private
11042     checkTab : function(e){
11043         if(e.getKey() == e.TAB){
11044             this.triggerBlur();
11045         }
11046     },
11047
11048     // private
11049     onBlur : function(){
11050         // do nothing
11051     },
11052
11053     // private
11054     mimicBlur : function(e, t){
11055         /*
11056         if(!this.wrap.contains(t) && this.validateBlur()){
11057             this.triggerBlur();
11058         }
11059         */
11060     },
11061
11062     // private
11063     triggerBlur : function(){
11064         this.mimicing = false;
11065         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11066         if(this.monitorTab){
11067             this.el.un("keydown", this.checkTab, this);
11068         }
11069         //this.wrap.removeClass('x-trigger-wrap-focus');
11070         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11071     },
11072
11073     // private
11074     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11075     validateBlur : function(e, t){
11076         return true;
11077     },
11078
11079     // private
11080     onDisable : function(){
11081         this.inputEl().dom.disabled = true;
11082         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11083         //if(this.wrap){
11084         //    this.wrap.addClass('x-item-disabled');
11085         //}
11086     },
11087
11088     // private
11089     onEnable : function(){
11090         this.inputEl().dom.disabled = false;
11091         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11092         //if(this.wrap){
11093         //    this.el.removeClass('x-item-disabled');
11094         //}
11095     },
11096
11097     // private
11098     onShow : function(){
11099         var ae = this.getActionEl();
11100         
11101         if(ae){
11102             ae.dom.style.display = '';
11103             ae.dom.style.visibility = 'visible';
11104         }
11105     },
11106
11107     // private
11108     
11109     onHide : function(){
11110         var ae = this.getActionEl();
11111         ae.dom.style.display = 'none';
11112     },
11113
11114     /**
11115      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11116      * by an implementing function.
11117      * @method
11118      * @param {EventObject} e
11119      */
11120     onTriggerClick : Roo.emptyFn
11121 });
11122  /*
11123  * Based on:
11124  * Ext JS Library 1.1.1
11125  * Copyright(c) 2006-2007, Ext JS, LLC.
11126  *
11127  * Originally Released Under LGPL - original licence link has changed is not relivant.
11128  *
11129  * Fork - LGPL
11130  * <script type="text/javascript">
11131  */
11132
11133
11134 /**
11135  * @class Roo.data.SortTypes
11136  * @singleton
11137  * Defines the default sorting (casting?) comparison functions used when sorting data.
11138  */
11139 Roo.data.SortTypes = {
11140     /**
11141      * Default sort that does nothing
11142      * @param {Mixed} s The value being converted
11143      * @return {Mixed} The comparison value
11144      */
11145     none : function(s){
11146         return s;
11147     },
11148     
11149     /**
11150      * The regular expression used to strip tags
11151      * @type {RegExp}
11152      * @property
11153      */
11154     stripTagsRE : /<\/?[^>]+>/gi,
11155     
11156     /**
11157      * Strips all HTML tags to sort on text only
11158      * @param {Mixed} s The value being converted
11159      * @return {String} The comparison value
11160      */
11161     asText : function(s){
11162         return String(s).replace(this.stripTagsRE, "");
11163     },
11164     
11165     /**
11166      * Strips all HTML tags to sort on text only - Case insensitive
11167      * @param {Mixed} s The value being converted
11168      * @return {String} The comparison value
11169      */
11170     asUCText : function(s){
11171         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11172     },
11173     
11174     /**
11175      * Case insensitive string
11176      * @param {Mixed} s The value being converted
11177      * @return {String} The comparison value
11178      */
11179     asUCString : function(s) {
11180         return String(s).toUpperCase();
11181     },
11182     
11183     /**
11184      * Date sorting
11185      * @param {Mixed} s The value being converted
11186      * @return {Number} The comparison value
11187      */
11188     asDate : function(s) {
11189         if(!s){
11190             return 0;
11191         }
11192         if(s instanceof Date){
11193             return s.getTime();
11194         }
11195         return Date.parse(String(s));
11196     },
11197     
11198     /**
11199      * Float sorting
11200      * @param {Mixed} s The value being converted
11201      * @return {Float} The comparison value
11202      */
11203     asFloat : function(s) {
11204         var val = parseFloat(String(s).replace(/,/g, ""));
11205         if(isNaN(val)) {
11206             val = 0;
11207         }
11208         return val;
11209     },
11210     
11211     /**
11212      * Integer sorting
11213      * @param {Mixed} s The value being converted
11214      * @return {Number} The comparison value
11215      */
11216     asInt : function(s) {
11217         var val = parseInt(String(s).replace(/,/g, ""));
11218         if(isNaN(val)) {
11219             val = 0;
11220         }
11221         return val;
11222     }
11223 };/*
11224  * Based on:
11225  * Ext JS Library 1.1.1
11226  * Copyright(c) 2006-2007, Ext JS, LLC.
11227  *
11228  * Originally Released Under LGPL - original licence link has changed is not relivant.
11229  *
11230  * Fork - LGPL
11231  * <script type="text/javascript">
11232  */
11233
11234 /**
11235 * @class Roo.data.Record
11236  * Instances of this class encapsulate both record <em>definition</em> information, and record
11237  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11238  * to access Records cached in an {@link Roo.data.Store} object.<br>
11239  * <p>
11240  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11241  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11242  * objects.<br>
11243  * <p>
11244  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11245  * @constructor
11246  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11247  * {@link #create}. The parameters are the same.
11248  * @param {Array} data An associative Array of data values keyed by the field name.
11249  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11250  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11251  * not specified an integer id is generated.
11252  */
11253 Roo.data.Record = function(data, id){
11254     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11255     this.data = data;
11256 };
11257
11258 /**
11259  * Generate a constructor for a specific record layout.
11260  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11261  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11262  * Each field definition object may contain the following properties: <ul>
11263  * <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,
11264  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11265  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11266  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11267  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11268  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11269  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11270  * this may be omitted.</p></li>
11271  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11272  * <ul><li>auto (Default, implies no conversion)</li>
11273  * <li>string</li>
11274  * <li>int</li>
11275  * <li>float</li>
11276  * <li>boolean</li>
11277  * <li>date</li></ul></p></li>
11278  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11279  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11280  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11281  * by the Reader into an object that will be stored in the Record. It is passed the
11282  * following parameters:<ul>
11283  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11284  * </ul></p></li>
11285  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11286  * </ul>
11287  * <br>usage:<br><pre><code>
11288 var TopicRecord = Roo.data.Record.create(
11289     {name: 'title', mapping: 'topic_title'},
11290     {name: 'author', mapping: 'username'},
11291     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11292     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11293     {name: 'lastPoster', mapping: 'user2'},
11294     {name: 'excerpt', mapping: 'post_text'}
11295 );
11296
11297 var myNewRecord = new TopicRecord({
11298     title: 'Do my job please',
11299     author: 'noobie',
11300     totalPosts: 1,
11301     lastPost: new Date(),
11302     lastPoster: 'Animal',
11303     excerpt: 'No way dude!'
11304 });
11305 myStore.add(myNewRecord);
11306 </code></pre>
11307  * @method create
11308  * @static
11309  */
11310 Roo.data.Record.create = function(o){
11311     var f = function(){
11312         f.superclass.constructor.apply(this, arguments);
11313     };
11314     Roo.extend(f, Roo.data.Record);
11315     var p = f.prototype;
11316     p.fields = new Roo.util.MixedCollection(false, function(field){
11317         return field.name;
11318     });
11319     for(var i = 0, len = o.length; i < len; i++){
11320         p.fields.add(new Roo.data.Field(o[i]));
11321     }
11322     f.getField = function(name){
11323         return p.fields.get(name);  
11324     };
11325     return f;
11326 };
11327
11328 Roo.data.Record.AUTO_ID = 1000;
11329 Roo.data.Record.EDIT = 'edit';
11330 Roo.data.Record.REJECT = 'reject';
11331 Roo.data.Record.COMMIT = 'commit';
11332
11333 Roo.data.Record.prototype = {
11334     /**
11335      * Readonly flag - true if this record has been modified.
11336      * @type Boolean
11337      */
11338     dirty : false,
11339     editing : false,
11340     error: null,
11341     modified: null,
11342
11343     // private
11344     join : function(store){
11345         this.store = store;
11346     },
11347
11348     /**
11349      * Set the named field to the specified value.
11350      * @param {String} name The name of the field to set.
11351      * @param {Object} value The value to set the field to.
11352      */
11353     set : function(name, value){
11354         if(this.data[name] == value){
11355             return;
11356         }
11357         this.dirty = true;
11358         if(!this.modified){
11359             this.modified = {};
11360         }
11361         if(typeof this.modified[name] == 'undefined'){
11362             this.modified[name] = this.data[name];
11363         }
11364         this.data[name] = value;
11365         if(!this.editing && this.store){
11366             this.store.afterEdit(this);
11367         }       
11368     },
11369
11370     /**
11371      * Get the value of the named field.
11372      * @param {String} name The name of the field to get the value of.
11373      * @return {Object} The value of the field.
11374      */
11375     get : function(name){
11376         return this.data[name]; 
11377     },
11378
11379     // private
11380     beginEdit : function(){
11381         this.editing = true;
11382         this.modified = {}; 
11383     },
11384
11385     // private
11386     cancelEdit : function(){
11387         this.editing = false;
11388         delete this.modified;
11389     },
11390
11391     // private
11392     endEdit : function(){
11393         this.editing = false;
11394         if(this.dirty && this.store){
11395             this.store.afterEdit(this);
11396         }
11397     },
11398
11399     /**
11400      * Usually called by the {@link Roo.data.Store} which owns the Record.
11401      * Rejects all changes made to the Record since either creation, or the last commit operation.
11402      * Modified fields are reverted to their original values.
11403      * <p>
11404      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11405      * of reject operations.
11406      */
11407     reject : function(){
11408         var m = this.modified;
11409         for(var n in m){
11410             if(typeof m[n] != "function"){
11411                 this.data[n] = m[n];
11412             }
11413         }
11414         this.dirty = false;
11415         delete this.modified;
11416         this.editing = false;
11417         if(this.store){
11418             this.store.afterReject(this);
11419         }
11420     },
11421
11422     /**
11423      * Usually called by the {@link Roo.data.Store} which owns the Record.
11424      * Commits all changes made to the Record since either creation, or the last commit operation.
11425      * <p>
11426      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11427      * of commit operations.
11428      */
11429     commit : function(){
11430         this.dirty = false;
11431         delete this.modified;
11432         this.editing = false;
11433         if(this.store){
11434             this.store.afterCommit(this);
11435         }
11436     },
11437
11438     // private
11439     hasError : function(){
11440         return this.error != null;
11441     },
11442
11443     // private
11444     clearError : function(){
11445         this.error = null;
11446     },
11447
11448     /**
11449      * Creates a copy of this record.
11450      * @param {String} id (optional) A new record id if you don't want to use this record's id
11451      * @return {Record}
11452      */
11453     copy : function(newId) {
11454         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11455     }
11456 };/*
11457  * Based on:
11458  * Ext JS Library 1.1.1
11459  * Copyright(c) 2006-2007, Ext JS, LLC.
11460  *
11461  * Originally Released Under LGPL - original licence link has changed is not relivant.
11462  *
11463  * Fork - LGPL
11464  * <script type="text/javascript">
11465  */
11466
11467
11468
11469 /**
11470  * @class Roo.data.Store
11471  * @extends Roo.util.Observable
11472  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11473  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11474  * <p>
11475  * 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
11476  * has no knowledge of the format of the data returned by the Proxy.<br>
11477  * <p>
11478  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11479  * instances from the data object. These records are cached and made available through accessor functions.
11480  * @constructor
11481  * Creates a new Store.
11482  * @param {Object} config A config object containing the objects needed for the Store to access data,
11483  * and read the data into Records.
11484  */
11485 Roo.data.Store = function(config){
11486     this.data = new Roo.util.MixedCollection(false);
11487     this.data.getKey = function(o){
11488         return o.id;
11489     };
11490     this.baseParams = {};
11491     // private
11492     this.paramNames = {
11493         "start" : "start",
11494         "limit" : "limit",
11495         "sort" : "sort",
11496         "dir" : "dir",
11497         "multisort" : "_multisort"
11498     };
11499
11500     if(config && config.data){
11501         this.inlineData = config.data;
11502         delete config.data;
11503     }
11504
11505     Roo.apply(this, config);
11506     
11507     if(this.reader){ // reader passed
11508         this.reader = Roo.factory(this.reader, Roo.data);
11509         this.reader.xmodule = this.xmodule || false;
11510         if(!this.recordType){
11511             this.recordType = this.reader.recordType;
11512         }
11513         if(this.reader.onMetaChange){
11514             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11515         }
11516     }
11517
11518     if(this.recordType){
11519         this.fields = this.recordType.prototype.fields;
11520     }
11521     this.modified = [];
11522
11523     this.addEvents({
11524         /**
11525          * @event datachanged
11526          * Fires when the data cache has changed, and a widget which is using this Store
11527          * as a Record cache should refresh its view.
11528          * @param {Store} this
11529          */
11530         datachanged : true,
11531         /**
11532          * @event metachange
11533          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11534          * @param {Store} this
11535          * @param {Object} meta The JSON metadata
11536          */
11537         metachange : true,
11538         /**
11539          * @event add
11540          * Fires when Records have been added to the Store
11541          * @param {Store} this
11542          * @param {Roo.data.Record[]} records The array of Records added
11543          * @param {Number} index The index at which the record(s) were added
11544          */
11545         add : true,
11546         /**
11547          * @event remove
11548          * Fires when a Record has been removed from the Store
11549          * @param {Store} this
11550          * @param {Roo.data.Record} record The Record that was removed
11551          * @param {Number} index The index at which the record was removed
11552          */
11553         remove : true,
11554         /**
11555          * @event update
11556          * Fires when a Record has been updated
11557          * @param {Store} this
11558          * @param {Roo.data.Record} record The Record that was updated
11559          * @param {String} operation The update operation being performed.  Value may be one of:
11560          * <pre><code>
11561  Roo.data.Record.EDIT
11562  Roo.data.Record.REJECT
11563  Roo.data.Record.COMMIT
11564          * </code></pre>
11565          */
11566         update : true,
11567         /**
11568          * @event clear
11569          * Fires when the data cache has been cleared.
11570          * @param {Store} this
11571          */
11572         clear : true,
11573         /**
11574          * @event beforeload
11575          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11576          * the load action will be canceled.
11577          * @param {Store} this
11578          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11579          */
11580         beforeload : true,
11581         /**
11582          * @event beforeloadadd
11583          * Fires after a new set of Records has been loaded.
11584          * @param {Store} this
11585          * @param {Roo.data.Record[]} records The Records that were loaded
11586          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11587          */
11588         beforeloadadd : true,
11589         /**
11590          * @event load
11591          * Fires after a new set of Records has been loaded, before they are added to the store.
11592          * @param {Store} this
11593          * @param {Roo.data.Record[]} records The Records that were loaded
11594          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11595          * @params {Object} return from reader
11596          */
11597         load : true,
11598         /**
11599          * @event loadexception
11600          * Fires if an exception occurs in the Proxy during loading.
11601          * Called with the signature of the Proxy's "loadexception" event.
11602          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11603          * 
11604          * @param {Proxy} 
11605          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11606          * @param {Object} load options 
11607          * @param {Object} jsonData from your request (normally this contains the Exception)
11608          */
11609         loadexception : true
11610     });
11611     
11612     if(this.proxy){
11613         this.proxy = Roo.factory(this.proxy, Roo.data);
11614         this.proxy.xmodule = this.xmodule || false;
11615         this.relayEvents(this.proxy,  ["loadexception"]);
11616     }
11617     this.sortToggle = {};
11618     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11619
11620     Roo.data.Store.superclass.constructor.call(this);
11621
11622     if(this.inlineData){
11623         this.loadData(this.inlineData);
11624         delete this.inlineData;
11625     }
11626 };
11627
11628 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11629      /**
11630     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11631     * without a remote query - used by combo/forms at present.
11632     */
11633     
11634     /**
11635     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11636     */
11637     /**
11638     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11639     */
11640     /**
11641     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11642     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11643     */
11644     /**
11645     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11646     * on any HTTP request
11647     */
11648     /**
11649     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11650     */
11651     /**
11652     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11653     */
11654     multiSort: false,
11655     /**
11656     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11657     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11658     */
11659     remoteSort : false,
11660
11661     /**
11662     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11663      * loaded or when a record is removed. (defaults to false).
11664     */
11665     pruneModifiedRecords : false,
11666
11667     // private
11668     lastOptions : null,
11669
11670     /**
11671      * Add Records to the Store and fires the add event.
11672      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11673      */
11674     add : function(records){
11675         records = [].concat(records);
11676         for(var i = 0, len = records.length; i < len; i++){
11677             records[i].join(this);
11678         }
11679         var index = this.data.length;
11680         this.data.addAll(records);
11681         this.fireEvent("add", this, records, index);
11682     },
11683
11684     /**
11685      * Remove a Record from the Store and fires the remove event.
11686      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11687      */
11688     remove : function(record){
11689         var index = this.data.indexOf(record);
11690         this.data.removeAt(index);
11691  
11692         if(this.pruneModifiedRecords){
11693             this.modified.remove(record);
11694         }
11695         this.fireEvent("remove", this, record, index);
11696     },
11697
11698     /**
11699      * Remove all Records from the Store and fires the clear event.
11700      */
11701     removeAll : function(){
11702         this.data.clear();
11703         if(this.pruneModifiedRecords){
11704             this.modified = [];
11705         }
11706         this.fireEvent("clear", this);
11707     },
11708
11709     /**
11710      * Inserts Records to the Store at the given index and fires the add event.
11711      * @param {Number} index The start index at which to insert the passed Records.
11712      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11713      */
11714     insert : function(index, records){
11715         records = [].concat(records);
11716         for(var i = 0, len = records.length; i < len; i++){
11717             this.data.insert(index, records[i]);
11718             records[i].join(this);
11719         }
11720         this.fireEvent("add", this, records, index);
11721     },
11722
11723     /**
11724      * Get the index within the cache of the passed Record.
11725      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11726      * @return {Number} The index of the passed Record. Returns -1 if not found.
11727      */
11728     indexOf : function(record){
11729         return this.data.indexOf(record);
11730     },
11731
11732     /**
11733      * Get the index within the cache of the Record with the passed id.
11734      * @param {String} id The id of the Record to find.
11735      * @return {Number} The index of the Record. Returns -1 if not found.
11736      */
11737     indexOfId : function(id){
11738         return this.data.indexOfKey(id);
11739     },
11740
11741     /**
11742      * Get the Record with the specified id.
11743      * @param {String} id The id of the Record to find.
11744      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11745      */
11746     getById : function(id){
11747         return this.data.key(id);
11748     },
11749
11750     /**
11751      * Get the Record at the specified index.
11752      * @param {Number} index The index of the Record to find.
11753      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11754      */
11755     getAt : function(index){
11756         return this.data.itemAt(index);
11757     },
11758
11759     /**
11760      * Returns a range of Records between specified indices.
11761      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11762      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11763      * @return {Roo.data.Record[]} An array of Records
11764      */
11765     getRange : function(start, end){
11766         return this.data.getRange(start, end);
11767     },
11768
11769     // private
11770     storeOptions : function(o){
11771         o = Roo.apply({}, o);
11772         delete o.callback;
11773         delete o.scope;
11774         this.lastOptions = o;
11775     },
11776
11777     /**
11778      * Loads the Record cache from the configured Proxy using the configured Reader.
11779      * <p>
11780      * If using remote paging, then the first load call must specify the <em>start</em>
11781      * and <em>limit</em> properties in the options.params property to establish the initial
11782      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11783      * <p>
11784      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11785      * and this call will return before the new data has been loaded. Perform any post-processing
11786      * in a callback function, or in a "load" event handler.</strong>
11787      * <p>
11788      * @param {Object} options An object containing properties which control loading options:<ul>
11789      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11790      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11791      * passed the following arguments:<ul>
11792      * <li>r : Roo.data.Record[]</li>
11793      * <li>options: Options object from the load call</li>
11794      * <li>success: Boolean success indicator</li></ul></li>
11795      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11796      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11797      * </ul>
11798      */
11799     load : function(options){
11800         options = options || {};
11801         if(this.fireEvent("beforeload", this, options) !== false){
11802             this.storeOptions(options);
11803             var p = Roo.apply(options.params || {}, this.baseParams);
11804             // if meta was not loaded from remote source.. try requesting it.
11805             if (!this.reader.metaFromRemote) {
11806                 p._requestMeta = 1;
11807             }
11808             if(this.sortInfo && this.remoteSort){
11809                 var pn = this.paramNames;
11810                 p[pn["sort"]] = this.sortInfo.field;
11811                 p[pn["dir"]] = this.sortInfo.direction;
11812             }
11813             if (this.multiSort) {
11814                 var pn = this.paramNames;
11815                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11816             }
11817             
11818             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11819         }
11820     },
11821
11822     /**
11823      * Reloads the Record cache from the configured Proxy using the configured Reader and
11824      * the options from the last load operation performed.
11825      * @param {Object} options (optional) An object containing properties which may override the options
11826      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11827      * the most recently used options are reused).
11828      */
11829     reload : function(options){
11830         this.load(Roo.applyIf(options||{}, this.lastOptions));
11831     },
11832
11833     // private
11834     // Called as a callback by the Reader during a load operation.
11835     loadRecords : function(o, options, success){
11836         if(!o || success === false){
11837             if(success !== false){
11838                 this.fireEvent("load", this, [], options, o);
11839             }
11840             if(options.callback){
11841                 options.callback.call(options.scope || this, [], options, false);
11842             }
11843             return;
11844         }
11845         // if data returned failure - throw an exception.
11846         if (o.success === false) {
11847             // show a message if no listener is registered.
11848             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11849                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11850             }
11851             // loadmask wil be hooked into this..
11852             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11853             return;
11854         }
11855         var r = o.records, t = o.totalRecords || r.length;
11856         
11857         this.fireEvent("beforeloadadd", this, r, options, o);
11858         
11859         if(!options || options.add !== true){
11860             if(this.pruneModifiedRecords){
11861                 this.modified = [];
11862             }
11863             for(var i = 0, len = r.length; i < len; i++){
11864                 r[i].join(this);
11865             }
11866             if(this.snapshot){
11867                 this.data = this.snapshot;
11868                 delete this.snapshot;
11869             }
11870             this.data.clear();
11871             this.data.addAll(r);
11872             this.totalLength = t;
11873             this.applySort();
11874             this.fireEvent("datachanged", this);
11875         }else{
11876             this.totalLength = Math.max(t, this.data.length+r.length);
11877             this.add(r);
11878         }
11879         
11880         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11881                 
11882             var e = new Roo.data.Record({});
11883
11884             e.set(this.parent.displayField, this.parent.emptyTitle);
11885             e.set(this.parent.valueField, '');
11886
11887             this.insert(0, e);
11888         }
11889             
11890         this.fireEvent("load", this, r, options, o);
11891         if(options.callback){
11892             options.callback.call(options.scope || this, r, options, true);
11893         }
11894     },
11895
11896
11897     /**
11898      * Loads data from a passed data block. A Reader which understands the format of the data
11899      * must have been configured in the constructor.
11900      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11901      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11902      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11903      */
11904     loadData : function(o, append){
11905         var r = this.reader.readRecords(o);
11906         this.loadRecords(r, {add: append}, true);
11907     },
11908
11909     /**
11910      * Gets the number of cached records.
11911      * <p>
11912      * <em>If using paging, this may not be the total size of the dataset. If the data object
11913      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11914      * the data set size</em>
11915      */
11916     getCount : function(){
11917         return this.data.length || 0;
11918     },
11919
11920     /**
11921      * Gets the total number of records in the dataset as returned by the server.
11922      * <p>
11923      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11924      * the dataset size</em>
11925      */
11926     getTotalCount : function(){
11927         return this.totalLength || 0;
11928     },
11929
11930     /**
11931      * Returns the sort state of the Store as an object with two properties:
11932      * <pre><code>
11933  field {String} The name of the field by which the Records are sorted
11934  direction {String} The sort order, "ASC" or "DESC"
11935      * </code></pre>
11936      */
11937     getSortState : function(){
11938         return this.sortInfo;
11939     },
11940
11941     // private
11942     applySort : function(){
11943         if(this.sortInfo && !this.remoteSort){
11944             var s = this.sortInfo, f = s.field;
11945             var st = this.fields.get(f).sortType;
11946             var fn = function(r1, r2){
11947                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11948                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11949             };
11950             this.data.sort(s.direction, fn);
11951             if(this.snapshot && this.snapshot != this.data){
11952                 this.snapshot.sort(s.direction, fn);
11953             }
11954         }
11955     },
11956
11957     /**
11958      * Sets the default sort column and order to be used by the next load operation.
11959      * @param {String} fieldName The name of the field to sort by.
11960      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11961      */
11962     setDefaultSort : function(field, dir){
11963         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11964     },
11965
11966     /**
11967      * Sort the Records.
11968      * If remote sorting is used, the sort is performed on the server, and the cache is
11969      * reloaded. If local sorting is used, the cache is sorted internally.
11970      * @param {String} fieldName The name of the field to sort by.
11971      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11972      */
11973     sort : function(fieldName, dir){
11974         var f = this.fields.get(fieldName);
11975         if(!dir){
11976             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11977             
11978             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11979                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11980             }else{
11981                 dir = f.sortDir;
11982             }
11983         }
11984         this.sortToggle[f.name] = dir;
11985         this.sortInfo = {field: f.name, direction: dir};
11986         if(!this.remoteSort){
11987             this.applySort();
11988             this.fireEvent("datachanged", this);
11989         }else{
11990             this.load(this.lastOptions);
11991         }
11992     },
11993
11994     /**
11995      * Calls the specified function for each of the Records in the cache.
11996      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11997      * Returning <em>false</em> aborts and exits the iteration.
11998      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11999      */
12000     each : function(fn, scope){
12001         this.data.each(fn, scope);
12002     },
12003
12004     /**
12005      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12006      * (e.g., during paging).
12007      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12008      */
12009     getModifiedRecords : function(){
12010         return this.modified;
12011     },
12012
12013     // private
12014     createFilterFn : function(property, value, anyMatch){
12015         if(!value.exec){ // not a regex
12016             value = String(value);
12017             if(value.length == 0){
12018                 return false;
12019             }
12020             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12021         }
12022         return function(r){
12023             return value.test(r.data[property]);
12024         };
12025     },
12026
12027     /**
12028      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12029      * @param {String} property A field on your records
12030      * @param {Number} start The record index to start at (defaults to 0)
12031      * @param {Number} end The last record index to include (defaults to length - 1)
12032      * @return {Number} The sum
12033      */
12034     sum : function(property, start, end){
12035         var rs = this.data.items, v = 0;
12036         start = start || 0;
12037         end = (end || end === 0) ? end : rs.length-1;
12038
12039         for(var i = start; i <= end; i++){
12040             v += (rs[i].data[property] || 0);
12041         }
12042         return v;
12043     },
12044
12045     /**
12046      * Filter the records by a specified property.
12047      * @param {String} field A field on your records
12048      * @param {String/RegExp} value Either a string that the field
12049      * should start with or a RegExp to test against the field
12050      * @param {Boolean} anyMatch True to match any part not just the beginning
12051      */
12052     filter : function(property, value, anyMatch){
12053         var fn = this.createFilterFn(property, value, anyMatch);
12054         return fn ? this.filterBy(fn) : this.clearFilter();
12055     },
12056
12057     /**
12058      * Filter by a function. The specified function will be called with each
12059      * record in this data source. If the function returns true the record is included,
12060      * otherwise it is filtered.
12061      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12062      * @param {Object} scope (optional) The scope of the function (defaults to this)
12063      */
12064     filterBy : function(fn, scope){
12065         this.snapshot = this.snapshot || this.data;
12066         this.data = this.queryBy(fn, scope||this);
12067         this.fireEvent("datachanged", this);
12068     },
12069
12070     /**
12071      * Query the records by a specified property.
12072      * @param {String} field A field on your records
12073      * @param {String/RegExp} value Either a string that the field
12074      * should start with or a RegExp to test against the field
12075      * @param {Boolean} anyMatch True to match any part not just the beginning
12076      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12077      */
12078     query : function(property, value, anyMatch){
12079         var fn = this.createFilterFn(property, value, anyMatch);
12080         return fn ? this.queryBy(fn) : this.data.clone();
12081     },
12082
12083     /**
12084      * Query by a function. The specified function will be called with each
12085      * record in this data source. If the function returns true the record is included
12086      * in the results.
12087      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12088      * @param {Object} scope (optional) The scope of the function (defaults to this)
12089       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12090      **/
12091     queryBy : function(fn, scope){
12092         var data = this.snapshot || this.data;
12093         return data.filterBy(fn, scope||this);
12094     },
12095
12096     /**
12097      * Collects unique values for a particular dataIndex from this store.
12098      * @param {String} dataIndex The property to collect
12099      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12100      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12101      * @return {Array} An array of the unique values
12102      **/
12103     collect : function(dataIndex, allowNull, bypassFilter){
12104         var d = (bypassFilter === true && this.snapshot) ?
12105                 this.snapshot.items : this.data.items;
12106         var v, sv, r = [], l = {};
12107         for(var i = 0, len = d.length; i < len; i++){
12108             v = d[i].data[dataIndex];
12109             sv = String(v);
12110             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12111                 l[sv] = true;
12112                 r[r.length] = v;
12113             }
12114         }
12115         return r;
12116     },
12117
12118     /**
12119      * Revert to a view of the Record cache with no filtering applied.
12120      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12121      */
12122     clearFilter : function(suppressEvent){
12123         if(this.snapshot && this.snapshot != this.data){
12124             this.data = this.snapshot;
12125             delete this.snapshot;
12126             if(suppressEvent !== true){
12127                 this.fireEvent("datachanged", this);
12128             }
12129         }
12130     },
12131
12132     // private
12133     afterEdit : function(record){
12134         if(this.modified.indexOf(record) == -1){
12135             this.modified.push(record);
12136         }
12137         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12138     },
12139     
12140     // private
12141     afterReject : function(record){
12142         this.modified.remove(record);
12143         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12144     },
12145
12146     // private
12147     afterCommit : function(record){
12148         this.modified.remove(record);
12149         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12150     },
12151
12152     /**
12153      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12154      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12155      */
12156     commitChanges : function(){
12157         var m = this.modified.slice(0);
12158         this.modified = [];
12159         for(var i = 0, len = m.length; i < len; i++){
12160             m[i].commit();
12161         }
12162     },
12163
12164     /**
12165      * Cancel outstanding changes on all changed records.
12166      */
12167     rejectChanges : function(){
12168         var m = this.modified.slice(0);
12169         this.modified = [];
12170         for(var i = 0, len = m.length; i < len; i++){
12171             m[i].reject();
12172         }
12173     },
12174
12175     onMetaChange : function(meta, rtype, o){
12176         this.recordType = rtype;
12177         this.fields = rtype.prototype.fields;
12178         delete this.snapshot;
12179         this.sortInfo = meta.sortInfo || this.sortInfo;
12180         this.modified = [];
12181         this.fireEvent('metachange', this, this.reader.meta);
12182     },
12183     
12184     moveIndex : function(data, type)
12185     {
12186         var index = this.indexOf(data);
12187         
12188         var newIndex = index + type;
12189         
12190         this.remove(data);
12191         
12192         this.insert(newIndex, data);
12193         
12194     }
12195 });/*
12196  * Based on:
12197  * Ext JS Library 1.1.1
12198  * Copyright(c) 2006-2007, Ext JS, LLC.
12199  *
12200  * Originally Released Under LGPL - original licence link has changed is not relivant.
12201  *
12202  * Fork - LGPL
12203  * <script type="text/javascript">
12204  */
12205
12206 /**
12207  * @class Roo.data.SimpleStore
12208  * @extends Roo.data.Store
12209  * Small helper class to make creating Stores from Array data easier.
12210  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12211  * @cfg {Array} fields An array of field definition objects, or field name strings.
12212  * @cfg {Array} data The multi-dimensional array of data
12213  * @constructor
12214  * @param {Object} config
12215  */
12216 Roo.data.SimpleStore = function(config){
12217     Roo.data.SimpleStore.superclass.constructor.call(this, {
12218         isLocal : true,
12219         reader: new Roo.data.ArrayReader({
12220                 id: config.id
12221             },
12222             Roo.data.Record.create(config.fields)
12223         ),
12224         proxy : new Roo.data.MemoryProxy(config.data)
12225     });
12226     this.load();
12227 };
12228 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12229  * Based on:
12230  * Ext JS Library 1.1.1
12231  * Copyright(c) 2006-2007, Ext JS, LLC.
12232  *
12233  * Originally Released Under LGPL - original licence link has changed is not relivant.
12234  *
12235  * Fork - LGPL
12236  * <script type="text/javascript">
12237  */
12238
12239 /**
12240 /**
12241  * @extends Roo.data.Store
12242  * @class Roo.data.JsonStore
12243  * Small helper class to make creating Stores for JSON data easier. <br/>
12244 <pre><code>
12245 var store = new Roo.data.JsonStore({
12246     url: 'get-images.php',
12247     root: 'images',
12248     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12249 });
12250 </code></pre>
12251  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12252  * JsonReader and HttpProxy (unless inline data is provided).</b>
12253  * @cfg {Array} fields An array of field definition objects, or field name strings.
12254  * @constructor
12255  * @param {Object} config
12256  */
12257 Roo.data.JsonStore = function(c){
12258     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12259         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12260         reader: new Roo.data.JsonReader(c, c.fields)
12261     }));
12262 };
12263 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12264  * Based on:
12265  * Ext JS Library 1.1.1
12266  * Copyright(c) 2006-2007, Ext JS, LLC.
12267  *
12268  * Originally Released Under LGPL - original licence link has changed is not relivant.
12269  *
12270  * Fork - LGPL
12271  * <script type="text/javascript">
12272  */
12273
12274  
12275 Roo.data.Field = function(config){
12276     if(typeof config == "string"){
12277         config = {name: config};
12278     }
12279     Roo.apply(this, config);
12280     
12281     if(!this.type){
12282         this.type = "auto";
12283     }
12284     
12285     var st = Roo.data.SortTypes;
12286     // named sortTypes are supported, here we look them up
12287     if(typeof this.sortType == "string"){
12288         this.sortType = st[this.sortType];
12289     }
12290     
12291     // set default sortType for strings and dates
12292     if(!this.sortType){
12293         switch(this.type){
12294             case "string":
12295                 this.sortType = st.asUCString;
12296                 break;
12297             case "date":
12298                 this.sortType = st.asDate;
12299                 break;
12300             default:
12301                 this.sortType = st.none;
12302         }
12303     }
12304
12305     // define once
12306     var stripRe = /[\$,%]/g;
12307
12308     // prebuilt conversion function for this field, instead of
12309     // switching every time we're reading a value
12310     if(!this.convert){
12311         var cv, dateFormat = this.dateFormat;
12312         switch(this.type){
12313             case "":
12314             case "auto":
12315             case undefined:
12316                 cv = function(v){ return v; };
12317                 break;
12318             case "string":
12319                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12320                 break;
12321             case "int":
12322                 cv = function(v){
12323                     return v !== undefined && v !== null && v !== '' ?
12324                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12325                     };
12326                 break;
12327             case "float":
12328                 cv = function(v){
12329                     return v !== undefined && v !== null && v !== '' ?
12330                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12331                     };
12332                 break;
12333             case "bool":
12334             case "boolean":
12335                 cv = function(v){ return v === true || v === "true" || v == 1; };
12336                 break;
12337             case "date":
12338                 cv = function(v){
12339                     if(!v){
12340                         return '';
12341                     }
12342                     if(v instanceof Date){
12343                         return v;
12344                     }
12345                     if(dateFormat){
12346                         if(dateFormat == "timestamp"){
12347                             return new Date(v*1000);
12348                         }
12349                         return Date.parseDate(v, dateFormat);
12350                     }
12351                     var parsed = Date.parse(v);
12352                     return parsed ? new Date(parsed) : null;
12353                 };
12354              break;
12355             
12356         }
12357         this.convert = cv;
12358     }
12359 };
12360
12361 Roo.data.Field.prototype = {
12362     dateFormat: null,
12363     defaultValue: "",
12364     mapping: null,
12365     sortType : null,
12366     sortDir : "ASC"
12367 };/*
12368  * Based on:
12369  * Ext JS Library 1.1.1
12370  * Copyright(c) 2006-2007, Ext JS, LLC.
12371  *
12372  * Originally Released Under LGPL - original licence link has changed is not relivant.
12373  *
12374  * Fork - LGPL
12375  * <script type="text/javascript">
12376  */
12377  
12378 // Base class for reading structured data from a data source.  This class is intended to be
12379 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12380
12381 /**
12382  * @class Roo.data.DataReader
12383  * Base class for reading structured data from a data source.  This class is intended to be
12384  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12385  */
12386
12387 Roo.data.DataReader = function(meta, recordType){
12388     
12389     this.meta = meta;
12390     
12391     this.recordType = recordType instanceof Array ? 
12392         Roo.data.Record.create(recordType) : recordType;
12393 };
12394
12395 Roo.data.DataReader.prototype = {
12396      /**
12397      * Create an empty record
12398      * @param {Object} data (optional) - overlay some values
12399      * @return {Roo.data.Record} record created.
12400      */
12401     newRow :  function(d) {
12402         var da =  {};
12403         this.recordType.prototype.fields.each(function(c) {
12404             switch( c.type) {
12405                 case 'int' : da[c.name] = 0; break;
12406                 case 'date' : da[c.name] = new Date(); break;
12407                 case 'float' : da[c.name] = 0.0; break;
12408                 case 'boolean' : da[c.name] = false; break;
12409                 default : da[c.name] = ""; break;
12410             }
12411             
12412         });
12413         return new this.recordType(Roo.apply(da, d));
12414     }
12415     
12416 };/*
12417  * Based on:
12418  * Ext JS Library 1.1.1
12419  * Copyright(c) 2006-2007, Ext JS, LLC.
12420  *
12421  * Originally Released Under LGPL - original licence link has changed is not relivant.
12422  *
12423  * Fork - LGPL
12424  * <script type="text/javascript">
12425  */
12426
12427 /**
12428  * @class Roo.data.DataProxy
12429  * @extends Roo.data.Observable
12430  * This class is an abstract base class for implementations which provide retrieval of
12431  * unformatted data objects.<br>
12432  * <p>
12433  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12434  * (of the appropriate type which knows how to parse the data object) to provide a block of
12435  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12436  * <p>
12437  * Custom implementations must implement the load method as described in
12438  * {@link Roo.data.HttpProxy#load}.
12439  */
12440 Roo.data.DataProxy = function(){
12441     this.addEvents({
12442         /**
12443          * @event beforeload
12444          * Fires before a network request is made to retrieve a data object.
12445          * @param {Object} This DataProxy object.
12446          * @param {Object} params The params parameter to the load function.
12447          */
12448         beforeload : true,
12449         /**
12450          * @event load
12451          * Fires before the load method's callback is called.
12452          * @param {Object} This DataProxy object.
12453          * @param {Object} o The data object.
12454          * @param {Object} arg The callback argument object passed to the load function.
12455          */
12456         load : true,
12457         /**
12458          * @event loadexception
12459          * Fires if an Exception occurs during data retrieval.
12460          * @param {Object} This DataProxy object.
12461          * @param {Object} o The data object.
12462          * @param {Object} arg The callback argument object passed to the load function.
12463          * @param {Object} e The Exception.
12464          */
12465         loadexception : true
12466     });
12467     Roo.data.DataProxy.superclass.constructor.call(this);
12468 };
12469
12470 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12471
12472     /**
12473      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12474      */
12475 /*
12476  * Based on:
12477  * Ext JS Library 1.1.1
12478  * Copyright(c) 2006-2007, Ext JS, LLC.
12479  *
12480  * Originally Released Under LGPL - original licence link has changed is not relivant.
12481  *
12482  * Fork - LGPL
12483  * <script type="text/javascript">
12484  */
12485 /**
12486  * @class Roo.data.MemoryProxy
12487  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12488  * to the Reader when its load method is called.
12489  * @constructor
12490  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12491  */
12492 Roo.data.MemoryProxy = function(data){
12493     if (data.data) {
12494         data = data.data;
12495     }
12496     Roo.data.MemoryProxy.superclass.constructor.call(this);
12497     this.data = data;
12498 };
12499
12500 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12501     
12502     /**
12503      * Load data from the requested source (in this case an in-memory
12504      * data object passed to the constructor), read the data object into
12505      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12506      * process that block using the passed callback.
12507      * @param {Object} params This parameter is not used by the MemoryProxy class.
12508      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12509      * object into a block of Roo.data.Records.
12510      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12511      * The function must be passed <ul>
12512      * <li>The Record block object</li>
12513      * <li>The "arg" argument from the load function</li>
12514      * <li>A boolean success indicator</li>
12515      * </ul>
12516      * @param {Object} scope The scope in which to call the callback
12517      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12518      */
12519     load : function(params, reader, callback, scope, arg){
12520         params = params || {};
12521         var result;
12522         try {
12523             result = reader.readRecords(params.data ? params.data :this.data);
12524         }catch(e){
12525             this.fireEvent("loadexception", this, arg, null, e);
12526             callback.call(scope, null, arg, false);
12527             return;
12528         }
12529         callback.call(scope, result, arg, true);
12530     },
12531     
12532     // private
12533     update : function(params, records){
12534         
12535     }
12536 });/*
12537  * Based on:
12538  * Ext JS Library 1.1.1
12539  * Copyright(c) 2006-2007, Ext JS, LLC.
12540  *
12541  * Originally Released Under LGPL - original licence link has changed is not relivant.
12542  *
12543  * Fork - LGPL
12544  * <script type="text/javascript">
12545  */
12546 /**
12547  * @class Roo.data.HttpProxy
12548  * @extends Roo.data.DataProxy
12549  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12550  * configured to reference a certain URL.<br><br>
12551  * <p>
12552  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12553  * from which the running page was served.<br><br>
12554  * <p>
12555  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12556  * <p>
12557  * Be aware that to enable the browser to parse an XML document, the server must set
12558  * the Content-Type header in the HTTP response to "text/xml".
12559  * @constructor
12560  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12561  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12562  * will be used to make the request.
12563  */
12564 Roo.data.HttpProxy = function(conn){
12565     Roo.data.HttpProxy.superclass.constructor.call(this);
12566     // is conn a conn config or a real conn?
12567     this.conn = conn;
12568     this.useAjax = !conn || !conn.events;
12569   
12570 };
12571
12572 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12573     // thse are take from connection...
12574     
12575     /**
12576      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12577      */
12578     /**
12579      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12580      * extra parameters to each request made by this object. (defaults to undefined)
12581      */
12582     /**
12583      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12584      *  to each request made by this object. (defaults to undefined)
12585      */
12586     /**
12587      * @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)
12588      */
12589     /**
12590      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12591      */
12592      /**
12593      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12594      * @type Boolean
12595      */
12596   
12597
12598     /**
12599      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12600      * @type Boolean
12601      */
12602     /**
12603      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12604      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12605      * a finer-grained basis than the DataProxy events.
12606      */
12607     getConnection : function(){
12608         return this.useAjax ? Roo.Ajax : this.conn;
12609     },
12610
12611     /**
12612      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12613      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12614      * process that block using the passed callback.
12615      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12616      * for the request to the remote server.
12617      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12618      * object into a block of Roo.data.Records.
12619      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12620      * The function must be passed <ul>
12621      * <li>The Record block object</li>
12622      * <li>The "arg" argument from the load function</li>
12623      * <li>A boolean success indicator</li>
12624      * </ul>
12625      * @param {Object} scope The scope in which to call the callback
12626      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12627      */
12628     load : function(params, reader, callback, scope, arg){
12629         if(this.fireEvent("beforeload", this, params) !== false){
12630             var  o = {
12631                 params : params || {},
12632                 request: {
12633                     callback : callback,
12634                     scope : scope,
12635                     arg : arg
12636                 },
12637                 reader: reader,
12638                 callback : this.loadResponse,
12639                 scope: this
12640             };
12641             if(this.useAjax){
12642                 Roo.applyIf(o, this.conn);
12643                 if(this.activeRequest){
12644                     Roo.Ajax.abort(this.activeRequest);
12645                 }
12646                 this.activeRequest = Roo.Ajax.request(o);
12647             }else{
12648                 this.conn.request(o);
12649             }
12650         }else{
12651             callback.call(scope||this, null, arg, false);
12652         }
12653     },
12654
12655     // private
12656     loadResponse : function(o, success, response){
12657         delete this.activeRequest;
12658         if(!success){
12659             this.fireEvent("loadexception", this, o, response);
12660             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12661             return;
12662         }
12663         var result;
12664         try {
12665             result = o.reader.read(response);
12666         }catch(e){
12667             this.fireEvent("loadexception", this, o, response, e);
12668             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12669             return;
12670         }
12671         
12672         this.fireEvent("load", this, o, o.request.arg);
12673         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12674     },
12675
12676     // private
12677     update : function(dataSet){
12678
12679     },
12680
12681     // private
12682     updateResponse : function(dataSet){
12683
12684     }
12685 });/*
12686  * Based on:
12687  * Ext JS Library 1.1.1
12688  * Copyright(c) 2006-2007, Ext JS, LLC.
12689  *
12690  * Originally Released Under LGPL - original licence link has changed is not relivant.
12691  *
12692  * Fork - LGPL
12693  * <script type="text/javascript">
12694  */
12695
12696 /**
12697  * @class Roo.data.ScriptTagProxy
12698  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12699  * other than the originating domain of the running page.<br><br>
12700  * <p>
12701  * <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
12702  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12703  * <p>
12704  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12705  * source code that is used as the source inside a &lt;script> tag.<br><br>
12706  * <p>
12707  * In order for the browser to process the returned data, the server must wrap the data object
12708  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12709  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12710  * depending on whether the callback name was passed:
12711  * <p>
12712  * <pre><code>
12713 boolean scriptTag = false;
12714 String cb = request.getParameter("callback");
12715 if (cb != null) {
12716     scriptTag = true;
12717     response.setContentType("text/javascript");
12718 } else {
12719     response.setContentType("application/x-json");
12720 }
12721 Writer out = response.getWriter();
12722 if (scriptTag) {
12723     out.write(cb + "(");
12724 }
12725 out.print(dataBlock.toJsonString());
12726 if (scriptTag) {
12727     out.write(");");
12728 }
12729 </pre></code>
12730  *
12731  * @constructor
12732  * @param {Object} config A configuration object.
12733  */
12734 Roo.data.ScriptTagProxy = function(config){
12735     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12736     Roo.apply(this, config);
12737     this.head = document.getElementsByTagName("head")[0];
12738 };
12739
12740 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12741
12742 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12743     /**
12744      * @cfg {String} url The URL from which to request the data object.
12745      */
12746     /**
12747      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12748      */
12749     timeout : 30000,
12750     /**
12751      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12752      * the server the name of the callback function set up by the load call to process the returned data object.
12753      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12754      * javascript output which calls this named function passing the data object as its only parameter.
12755      */
12756     callbackParam : "callback",
12757     /**
12758      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12759      * name to the request.
12760      */
12761     nocache : true,
12762
12763     /**
12764      * Load data from the configured URL, read the data object into
12765      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12766      * process that block using the passed callback.
12767      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12768      * for the request to the remote server.
12769      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12770      * object into a block of Roo.data.Records.
12771      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12772      * The function must be passed <ul>
12773      * <li>The Record block object</li>
12774      * <li>The "arg" argument from the load function</li>
12775      * <li>A boolean success indicator</li>
12776      * </ul>
12777      * @param {Object} scope The scope in which to call the callback
12778      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12779      */
12780     load : function(params, reader, callback, scope, arg){
12781         if(this.fireEvent("beforeload", this, params) !== false){
12782
12783             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12784
12785             var url = this.url;
12786             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12787             if(this.nocache){
12788                 url += "&_dc=" + (new Date().getTime());
12789             }
12790             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12791             var trans = {
12792                 id : transId,
12793                 cb : "stcCallback"+transId,
12794                 scriptId : "stcScript"+transId,
12795                 params : params,
12796                 arg : arg,
12797                 url : url,
12798                 callback : callback,
12799                 scope : scope,
12800                 reader : reader
12801             };
12802             var conn = this;
12803
12804             window[trans.cb] = function(o){
12805                 conn.handleResponse(o, trans);
12806             };
12807
12808             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12809
12810             if(this.autoAbort !== false){
12811                 this.abort();
12812             }
12813
12814             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12815
12816             var script = document.createElement("script");
12817             script.setAttribute("src", url);
12818             script.setAttribute("type", "text/javascript");
12819             script.setAttribute("id", trans.scriptId);
12820             this.head.appendChild(script);
12821
12822             this.trans = trans;
12823         }else{
12824             callback.call(scope||this, null, arg, false);
12825         }
12826     },
12827
12828     // private
12829     isLoading : function(){
12830         return this.trans ? true : false;
12831     },
12832
12833     /**
12834      * Abort the current server request.
12835      */
12836     abort : function(){
12837         if(this.isLoading()){
12838             this.destroyTrans(this.trans);
12839         }
12840     },
12841
12842     // private
12843     destroyTrans : function(trans, isLoaded){
12844         this.head.removeChild(document.getElementById(trans.scriptId));
12845         clearTimeout(trans.timeoutId);
12846         if(isLoaded){
12847             window[trans.cb] = undefined;
12848             try{
12849                 delete window[trans.cb];
12850             }catch(e){}
12851         }else{
12852             // if hasn't been loaded, wait for load to remove it to prevent script error
12853             window[trans.cb] = function(){
12854                 window[trans.cb] = undefined;
12855                 try{
12856                     delete window[trans.cb];
12857                 }catch(e){}
12858             };
12859         }
12860     },
12861
12862     // private
12863     handleResponse : function(o, trans){
12864         this.trans = false;
12865         this.destroyTrans(trans, true);
12866         var result;
12867         try {
12868             result = trans.reader.readRecords(o);
12869         }catch(e){
12870             this.fireEvent("loadexception", this, o, trans.arg, e);
12871             trans.callback.call(trans.scope||window, null, trans.arg, false);
12872             return;
12873         }
12874         this.fireEvent("load", this, o, trans.arg);
12875         trans.callback.call(trans.scope||window, result, trans.arg, true);
12876     },
12877
12878     // private
12879     handleFailure : function(trans){
12880         this.trans = false;
12881         this.destroyTrans(trans, false);
12882         this.fireEvent("loadexception", this, null, trans.arg);
12883         trans.callback.call(trans.scope||window, null, trans.arg, false);
12884     }
12885 });/*
12886  * Based on:
12887  * Ext JS Library 1.1.1
12888  * Copyright(c) 2006-2007, Ext JS, LLC.
12889  *
12890  * Originally Released Under LGPL - original licence link has changed is not relivant.
12891  *
12892  * Fork - LGPL
12893  * <script type="text/javascript">
12894  */
12895
12896 /**
12897  * @class Roo.data.JsonReader
12898  * @extends Roo.data.DataReader
12899  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12900  * based on mappings in a provided Roo.data.Record constructor.
12901  * 
12902  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12903  * in the reply previously. 
12904  * 
12905  * <p>
12906  * Example code:
12907  * <pre><code>
12908 var RecordDef = Roo.data.Record.create([
12909     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12910     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12911 ]);
12912 var myReader = new Roo.data.JsonReader({
12913     totalProperty: "results",    // The property which contains the total dataset size (optional)
12914     root: "rows",                // The property which contains an Array of row objects
12915     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12916 }, RecordDef);
12917 </code></pre>
12918  * <p>
12919  * This would consume a JSON file like this:
12920  * <pre><code>
12921 { 'results': 2, 'rows': [
12922     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12923     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12924 }
12925 </code></pre>
12926  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12927  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12928  * paged from the remote server.
12929  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12930  * @cfg {String} root name of the property which contains the Array of row objects.
12931  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12932  * @cfg {Array} fields Array of field definition objects
12933  * @constructor
12934  * Create a new JsonReader
12935  * @param {Object} meta Metadata configuration options
12936  * @param {Object} recordType Either an Array of field definition objects,
12937  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12938  */
12939 Roo.data.JsonReader = function(meta, recordType){
12940     
12941     meta = meta || {};
12942     // set some defaults:
12943     Roo.applyIf(meta, {
12944         totalProperty: 'total',
12945         successProperty : 'success',
12946         root : 'data',
12947         id : 'id'
12948     });
12949     
12950     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12951 };
12952 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12953     
12954     /**
12955      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12956      * Used by Store query builder to append _requestMeta to params.
12957      * 
12958      */
12959     metaFromRemote : false,
12960     /**
12961      * This method is only used by a DataProxy which has retrieved data from a remote server.
12962      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12963      * @return {Object} data A data block which is used by an Roo.data.Store object as
12964      * a cache of Roo.data.Records.
12965      */
12966     read : function(response){
12967         var json = response.responseText;
12968        
12969         var o = /* eval:var:o */ eval("("+json+")");
12970         if(!o) {
12971             throw {message: "JsonReader.read: Json object not found"};
12972         }
12973         
12974         if(o.metaData){
12975             
12976             delete this.ef;
12977             this.metaFromRemote = true;
12978             this.meta = o.metaData;
12979             this.recordType = Roo.data.Record.create(o.metaData.fields);
12980             this.onMetaChange(this.meta, this.recordType, o);
12981         }
12982         return this.readRecords(o);
12983     },
12984
12985     // private function a store will implement
12986     onMetaChange : function(meta, recordType, o){
12987
12988     },
12989
12990     /**
12991          * @ignore
12992          */
12993     simpleAccess: function(obj, subsc) {
12994         return obj[subsc];
12995     },
12996
12997         /**
12998          * @ignore
12999          */
13000     getJsonAccessor: function(){
13001         var re = /[\[\.]/;
13002         return function(expr) {
13003             try {
13004                 return(re.test(expr))
13005                     ? new Function("obj", "return obj." + expr)
13006                     : function(obj){
13007                         return obj[expr];
13008                     };
13009             } catch(e){}
13010             return Roo.emptyFn;
13011         };
13012     }(),
13013
13014     /**
13015      * Create a data block containing Roo.data.Records from an XML document.
13016      * @param {Object} o An object which contains an Array of row objects in the property specified
13017      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13018      * which contains the total size of the dataset.
13019      * @return {Object} data A data block which is used by an Roo.data.Store object as
13020      * a cache of Roo.data.Records.
13021      */
13022     readRecords : function(o){
13023         /**
13024          * After any data loads, the raw JSON data is available for further custom processing.
13025          * @type Object
13026          */
13027         this.o = o;
13028         var s = this.meta, Record = this.recordType,
13029             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13030
13031 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13032         if (!this.ef) {
13033             if(s.totalProperty) {
13034                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13035                 }
13036                 if(s.successProperty) {
13037                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13038                 }
13039                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13040                 if (s.id) {
13041                         var g = this.getJsonAccessor(s.id);
13042                         this.getId = function(rec) {
13043                                 var r = g(rec);  
13044                                 return (r === undefined || r === "") ? null : r;
13045                         };
13046                 } else {
13047                         this.getId = function(){return null;};
13048                 }
13049             this.ef = [];
13050             for(var jj = 0; jj < fl; jj++){
13051                 f = fi[jj];
13052                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13053                 this.ef[jj] = this.getJsonAccessor(map);
13054             }
13055         }
13056
13057         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13058         if(s.totalProperty){
13059             var vt = parseInt(this.getTotal(o), 10);
13060             if(!isNaN(vt)){
13061                 totalRecords = vt;
13062             }
13063         }
13064         if(s.successProperty){
13065             var vs = this.getSuccess(o);
13066             if(vs === false || vs === 'false'){
13067                 success = false;
13068             }
13069         }
13070         var records = [];
13071         for(var i = 0; i < c; i++){
13072                 var n = root[i];
13073             var values = {};
13074             var id = this.getId(n);
13075             for(var j = 0; j < fl; j++){
13076                 f = fi[j];
13077             var v = this.ef[j](n);
13078             if (!f.convert) {
13079                 Roo.log('missing convert for ' + f.name);
13080                 Roo.log(f);
13081                 continue;
13082             }
13083             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13084             }
13085             var record = new Record(values, id);
13086             record.json = n;
13087             records[i] = record;
13088         }
13089         return {
13090             raw : o,
13091             success : success,
13092             records : records,
13093             totalRecords : totalRecords
13094         };
13095     }
13096 });/*
13097  * Based on:
13098  * Ext JS Library 1.1.1
13099  * Copyright(c) 2006-2007, Ext JS, LLC.
13100  *
13101  * Originally Released Under LGPL - original licence link has changed is not relivant.
13102  *
13103  * Fork - LGPL
13104  * <script type="text/javascript">
13105  */
13106
13107 /**
13108  * @class Roo.data.ArrayReader
13109  * @extends Roo.data.DataReader
13110  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13111  * Each element of that Array represents a row of data fields. The
13112  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13113  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13114  * <p>
13115  * Example code:.
13116  * <pre><code>
13117 var RecordDef = Roo.data.Record.create([
13118     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13119     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13120 ]);
13121 var myReader = new Roo.data.ArrayReader({
13122     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13123 }, RecordDef);
13124 </code></pre>
13125  * <p>
13126  * This would consume an Array like this:
13127  * <pre><code>
13128 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13129   </code></pre>
13130  
13131  * @constructor
13132  * Create a new JsonReader
13133  * @param {Object} meta Metadata configuration options.
13134  * @param {Object|Array} recordType Either an Array of field definition objects
13135  * 
13136  * @cfg {Array} fields Array of field definition objects
13137  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13138  * as specified to {@link Roo.data.Record#create},
13139  * or an {@link Roo.data.Record} object
13140  *
13141  * 
13142  * created using {@link Roo.data.Record#create}.
13143  */
13144 Roo.data.ArrayReader = function(meta, recordType){
13145     
13146      
13147     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13148 };
13149
13150 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13151     /**
13152      * Create a data block containing Roo.data.Records from an XML document.
13153      * @param {Object} o An Array of row objects which represents the dataset.
13154      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13155      * a cache of Roo.data.Records.
13156      */
13157     readRecords : function(o){
13158         var sid = this.meta ? this.meta.id : null;
13159         var recordType = this.recordType, fields = recordType.prototype.fields;
13160         var records = [];
13161         var root = o;
13162             for(var i = 0; i < root.length; i++){
13163                     var n = root[i];
13164                 var values = {};
13165                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13166                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13167                 var f = fields.items[j];
13168                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13169                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13170                 v = f.convert(v);
13171                 values[f.name] = v;
13172             }
13173                 var record = new recordType(values, id);
13174                 record.json = n;
13175                 records[records.length] = record;
13176             }
13177             return {
13178                 records : records,
13179                 totalRecords : records.length
13180             };
13181     }
13182 });/*
13183  * - LGPL
13184  * * 
13185  */
13186
13187 /**
13188  * @class Roo.bootstrap.ComboBox
13189  * @extends Roo.bootstrap.TriggerField
13190  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13191  * @cfg {Boolean} append (true|false) default false
13192  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13193  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13194  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13195  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13196  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13197  * @cfg {Boolean} animate default true
13198  * @cfg {Boolean} emptyResultText only for touch device
13199  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13200  * @cfg {String} emptyTitle default ''
13201  * @constructor
13202  * Create a new ComboBox.
13203  * @param {Object} config Configuration options
13204  */
13205 Roo.bootstrap.ComboBox = function(config){
13206     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13207     this.addEvents({
13208         /**
13209          * @event expand
13210          * Fires when the dropdown list is expanded
13211         * @param {Roo.bootstrap.ComboBox} combo This combo box
13212         */
13213         'expand' : true,
13214         /**
13215          * @event collapse
13216          * Fires when the dropdown list is collapsed
13217         * @param {Roo.bootstrap.ComboBox} combo This combo box
13218         */
13219         'collapse' : true,
13220         /**
13221          * @event beforeselect
13222          * Fires before a list item is selected. Return false to cancel the selection.
13223         * @param {Roo.bootstrap.ComboBox} combo This combo box
13224         * @param {Roo.data.Record} record The data record returned from the underlying store
13225         * @param {Number} index The index of the selected item in the dropdown list
13226         */
13227         'beforeselect' : true,
13228         /**
13229          * @event select
13230          * Fires when a list item is selected
13231         * @param {Roo.bootstrap.ComboBox} combo This combo box
13232         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13233         * @param {Number} index The index of the selected item in the dropdown list
13234         */
13235         'select' : true,
13236         /**
13237          * @event beforequery
13238          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13239          * The event object passed has these properties:
13240         * @param {Roo.bootstrap.ComboBox} combo This combo box
13241         * @param {String} query The query
13242         * @param {Boolean} forceAll true to force "all" query
13243         * @param {Boolean} cancel true to cancel the query
13244         * @param {Object} e The query event object
13245         */
13246         'beforequery': true,
13247          /**
13248          * @event add
13249          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13250         * @param {Roo.bootstrap.ComboBox} combo This combo box
13251         */
13252         'add' : true,
13253         /**
13254          * @event edit
13255          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13256         * @param {Roo.bootstrap.ComboBox} combo This combo box
13257         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13258         */
13259         'edit' : true,
13260         /**
13261          * @event remove
13262          * Fires when the remove value from the combobox array
13263         * @param {Roo.bootstrap.ComboBox} combo This combo box
13264         */
13265         'remove' : true,
13266         /**
13267          * @event afterremove
13268          * Fires when the remove value from the combobox array
13269         * @param {Roo.bootstrap.ComboBox} combo This combo box
13270         */
13271         'afterremove' : true,
13272         /**
13273          * @event specialfilter
13274          * Fires when specialfilter
13275             * @param {Roo.bootstrap.ComboBox} combo This combo box
13276             */
13277         'specialfilter' : true,
13278         /**
13279          * @event tick
13280          * Fires when tick the element
13281             * @param {Roo.bootstrap.ComboBox} combo This combo box
13282             */
13283         'tick' : true,
13284         /**
13285          * @event touchviewdisplay
13286          * Fires when touch view require special display (default is using displayField)
13287             * @param {Roo.bootstrap.ComboBox} combo This combo box
13288             * @param {Object} cfg set html .
13289             */
13290         'touchviewdisplay' : true
13291         
13292     });
13293     
13294     this.item = [];
13295     this.tickItems = [];
13296     
13297     this.selectedIndex = -1;
13298     if(this.mode == 'local'){
13299         if(config.queryDelay === undefined){
13300             this.queryDelay = 10;
13301         }
13302         if(config.minChars === undefined){
13303             this.minChars = 0;
13304         }
13305     }
13306 };
13307
13308 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13309      
13310     /**
13311      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13312      * rendering into an Roo.Editor, defaults to false)
13313      */
13314     /**
13315      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13316      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13317      */
13318     /**
13319      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13320      */
13321     /**
13322      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13323      * the dropdown list (defaults to undefined, with no header element)
13324      */
13325
13326      /**
13327      * @cfg {String/Roo.Template} tpl The template to use to render the output
13328      */
13329      
13330      /**
13331      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13332      */
13333     listWidth: undefined,
13334     /**
13335      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13336      * mode = 'remote' or 'text' if mode = 'local')
13337      */
13338     displayField: undefined,
13339     
13340     /**
13341      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13342      * mode = 'remote' or 'value' if mode = 'local'). 
13343      * Note: use of a valueField requires the user make a selection
13344      * in order for a value to be mapped.
13345      */
13346     valueField: undefined,
13347     /**
13348      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13349      */
13350     modalTitle : '',
13351     
13352     /**
13353      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13354      * field's data value (defaults to the underlying DOM element's name)
13355      */
13356     hiddenName: undefined,
13357     /**
13358      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13359      */
13360     listClass: '',
13361     /**
13362      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13363      */
13364     selectedClass: 'active',
13365     
13366     /**
13367      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13368      */
13369     shadow:'sides',
13370     /**
13371      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13372      * anchor positions (defaults to 'tl-bl')
13373      */
13374     listAlign: 'tl-bl?',
13375     /**
13376      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13377      */
13378     maxHeight: 300,
13379     /**
13380      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13381      * query specified by the allQuery config option (defaults to 'query')
13382      */
13383     triggerAction: 'query',
13384     /**
13385      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13386      * (defaults to 4, does not apply if editable = false)
13387      */
13388     minChars : 4,
13389     /**
13390      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13391      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13392      */
13393     typeAhead: false,
13394     /**
13395      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13396      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13397      */
13398     queryDelay: 500,
13399     /**
13400      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13401      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13402      */
13403     pageSize: 0,
13404     /**
13405      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13406      * when editable = true (defaults to false)
13407      */
13408     selectOnFocus:false,
13409     /**
13410      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13411      */
13412     queryParam: 'query',
13413     /**
13414      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13415      * when mode = 'remote' (defaults to 'Loading...')
13416      */
13417     loadingText: 'Loading...',
13418     /**
13419      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13420      */
13421     resizable: false,
13422     /**
13423      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13424      */
13425     handleHeight : 8,
13426     /**
13427      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13428      * traditional select (defaults to true)
13429      */
13430     editable: true,
13431     /**
13432      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13433      */
13434     allQuery: '',
13435     /**
13436      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13437      */
13438     mode: 'remote',
13439     /**
13440      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13441      * listWidth has a higher value)
13442      */
13443     minListWidth : 70,
13444     /**
13445      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13446      * allow the user to set arbitrary text into the field (defaults to false)
13447      */
13448     forceSelection:false,
13449     /**
13450      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13451      * if typeAhead = true (defaults to 250)
13452      */
13453     typeAheadDelay : 250,
13454     /**
13455      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13456      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13457      */
13458     valueNotFoundText : undefined,
13459     /**
13460      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13461      */
13462     blockFocus : false,
13463     
13464     /**
13465      * @cfg {Boolean} disableClear Disable showing of clear button.
13466      */
13467     disableClear : false,
13468     /**
13469      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13470      */
13471     alwaysQuery : false,
13472     
13473     /**
13474      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13475      */
13476     multiple : false,
13477     
13478     /**
13479      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13480      */
13481     invalidClass : "has-warning",
13482     
13483     /**
13484      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13485      */
13486     validClass : "has-success",
13487     
13488     /**
13489      * @cfg {Boolean} specialFilter (true|false) special filter default false
13490      */
13491     specialFilter : false,
13492     
13493     /**
13494      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13495      */
13496     mobileTouchView : true,
13497     
13498     /**
13499      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13500      */
13501     useNativeIOS : false,
13502     
13503     /**
13504      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13505      */
13506     mobile_restrict_height : false,
13507     
13508     ios_options : false,
13509     
13510     //private
13511     addicon : false,
13512     editicon: false,
13513     
13514     page: 0,
13515     hasQuery: false,
13516     append: false,
13517     loadNext: false,
13518     autoFocus : true,
13519     tickable : false,
13520     btnPosition : 'right',
13521     triggerList : true,
13522     showToggleBtn : true,
13523     animate : true,
13524     emptyResultText: 'Empty',
13525     triggerText : 'Select',
13526     emptyTitle : '',
13527     
13528     // element that contains real text value.. (when hidden is used..)
13529     
13530     getAutoCreate : function()
13531     {   
13532         var cfg = false;
13533         //render
13534         /*
13535          * Render classic select for iso
13536          */
13537         
13538         if(Roo.isIOS && this.useNativeIOS){
13539             cfg = this.getAutoCreateNativeIOS();
13540             return cfg;
13541         }
13542         
13543         /*
13544          * Touch Devices
13545          */
13546         
13547         if(Roo.isTouch && this.mobileTouchView){
13548             cfg = this.getAutoCreateTouchView();
13549             return cfg;;
13550         }
13551         
13552         /*
13553          *  Normal ComboBox
13554          */
13555         if(!this.tickable){
13556             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13557             return cfg;
13558         }
13559         
13560         /*
13561          *  ComboBox with tickable selections
13562          */
13563              
13564         var align = this.labelAlign || this.parentLabelAlign();
13565         
13566         cfg = {
13567             cls : 'form-group roo-combobox-tickable' //input-group
13568         };
13569         
13570         var btn_text_select = '';
13571         var btn_text_done = '';
13572         var btn_text_cancel = '';
13573         
13574         if (this.btn_text_show) {
13575             btn_text_select = 'Select';
13576             btn_text_done = 'Done';
13577             btn_text_cancel = 'Cancel'; 
13578         }
13579         
13580         var buttons = {
13581             tag : 'div',
13582             cls : 'tickable-buttons',
13583             cn : [
13584                 {
13585                     tag : 'button',
13586                     type : 'button',
13587                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13588                     //html : this.triggerText
13589                     html: btn_text_select
13590                 },
13591                 {
13592                     tag : 'button',
13593                     type : 'button',
13594                     name : 'ok',
13595                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13596                     //html : 'Done'
13597                     html: btn_text_done
13598                 },
13599                 {
13600                     tag : 'button',
13601                     type : 'button',
13602                     name : 'cancel',
13603                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13604                     //html : 'Cancel'
13605                     html: btn_text_cancel
13606                 }
13607             ]
13608         };
13609         
13610         if(this.editable){
13611             buttons.cn.unshift({
13612                 tag: 'input',
13613                 cls: 'roo-select2-search-field-input'
13614             });
13615         }
13616         
13617         var _this = this;
13618         
13619         Roo.each(buttons.cn, function(c){
13620             if (_this.size) {
13621                 c.cls += ' btn-' + _this.size;
13622             }
13623
13624             if (_this.disabled) {
13625                 c.disabled = true;
13626             }
13627         });
13628         
13629         var box = {
13630             tag: 'div',
13631             style : 'display: contents',
13632             cn: [
13633                 {
13634                     tag: 'input',
13635                     type : 'hidden',
13636                     cls: 'form-hidden-field'
13637                 },
13638                 {
13639                     tag: 'ul',
13640                     cls: 'roo-select2-choices',
13641                     cn:[
13642                         {
13643                             tag: 'li',
13644                             cls: 'roo-select2-search-field',
13645                             cn: [
13646                                 buttons
13647                             ]
13648                         }
13649                     ]
13650                 }
13651             ]
13652         };
13653         
13654         var combobox = {
13655             cls: 'roo-select2-container input-group roo-select2-container-multi',
13656             cn: [
13657                 
13658                 box
13659 //                {
13660 //                    tag: 'ul',
13661 //                    cls: 'typeahead typeahead-long dropdown-menu',
13662 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13663 //                }
13664             ]
13665         };
13666         
13667         if(this.hasFeedback && !this.allowBlank){
13668             
13669             var feedback = {
13670                 tag: 'span',
13671                 cls: 'glyphicon form-control-feedback'
13672             };
13673
13674             combobox.cn.push(feedback);
13675         }
13676         
13677         var indicator = {
13678             tag : 'i',
13679             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13680             tooltip : 'This field is required'
13681         };
13682         if (Roo.bootstrap.version == 4) {
13683             indicator = {
13684                 tag : 'i',
13685                 style : 'display:none'
13686             };
13687         }
13688         if (align ==='left' && this.fieldLabel.length) {
13689             
13690             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13691             
13692             cfg.cn = [
13693                 indicator,
13694                 {
13695                     tag: 'label',
13696                     'for' :  id,
13697                     cls : 'control-label col-form-label',
13698                     html : this.fieldLabel
13699
13700                 },
13701                 {
13702                     cls : "", 
13703                     cn: [
13704                         combobox
13705                     ]
13706                 }
13707
13708             ];
13709             
13710             var labelCfg = cfg.cn[1];
13711             var contentCfg = cfg.cn[2];
13712             
13713
13714             if(this.indicatorpos == 'right'){
13715                 
13716                 cfg.cn = [
13717                     {
13718                         tag: 'label',
13719                         'for' :  id,
13720                         cls : 'control-label col-form-label',
13721                         cn : [
13722                             {
13723                                 tag : 'span',
13724                                 html : this.fieldLabel
13725                             },
13726                             indicator
13727                         ]
13728                     },
13729                     {
13730                         cls : "",
13731                         cn: [
13732                             combobox
13733                         ]
13734                     }
13735
13736                 ];
13737                 
13738                 
13739                 
13740                 labelCfg = cfg.cn[0];
13741                 contentCfg = cfg.cn[1];
13742             
13743             }
13744             
13745             if(this.labelWidth > 12){
13746                 labelCfg.style = "width: " + this.labelWidth + 'px';
13747             }
13748             
13749             if(this.labelWidth < 13 && this.labelmd == 0){
13750                 this.labelmd = this.labelWidth;
13751             }
13752             
13753             if(this.labellg > 0){
13754                 labelCfg.cls += ' col-lg-' + this.labellg;
13755                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13756             }
13757             
13758             if(this.labelmd > 0){
13759                 labelCfg.cls += ' col-md-' + this.labelmd;
13760                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13761             }
13762             
13763             if(this.labelsm > 0){
13764                 labelCfg.cls += ' col-sm-' + this.labelsm;
13765                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13766             }
13767             
13768             if(this.labelxs > 0){
13769                 labelCfg.cls += ' col-xs-' + this.labelxs;
13770                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13771             }
13772                 
13773                 
13774         } else if ( this.fieldLabel.length) {
13775 //                Roo.log(" label");
13776                  cfg.cn = [
13777                    indicator,
13778                     {
13779                         tag: 'label',
13780                         //cls : 'input-group-addon',
13781                         html : this.fieldLabel
13782                     },
13783                     combobox
13784                 ];
13785                 
13786                 if(this.indicatorpos == 'right'){
13787                     cfg.cn = [
13788                         {
13789                             tag: 'label',
13790                             //cls : 'input-group-addon',
13791                             html : this.fieldLabel
13792                         },
13793                         indicator,
13794                         combobox
13795                     ];
13796                     
13797                 }
13798
13799         } else {
13800             
13801 //                Roo.log(" no label && no align");
13802                 cfg = combobox
13803                      
13804                 
13805         }
13806          
13807         var settings=this;
13808         ['xs','sm','md','lg'].map(function(size){
13809             if (settings[size]) {
13810                 cfg.cls += ' col-' + size + '-' + settings[size];
13811             }
13812         });
13813         
13814         return cfg;
13815         
13816     },
13817     
13818     _initEventsCalled : false,
13819     
13820     // private
13821     initEvents: function()
13822     {   
13823         if (this._initEventsCalled) { // as we call render... prevent looping...
13824             return;
13825         }
13826         this._initEventsCalled = true;
13827         
13828         if (!this.store) {
13829             throw "can not find store for combo";
13830         }
13831         
13832         this.indicator = this.indicatorEl();
13833         
13834         this.store = Roo.factory(this.store, Roo.data);
13835         this.store.parent = this;
13836         
13837         // if we are building from html. then this element is so complex, that we can not really
13838         // use the rendered HTML.
13839         // so we have to trash and replace the previous code.
13840         if (Roo.XComponent.build_from_html) {
13841             // remove this element....
13842             var e = this.el.dom, k=0;
13843             while (e ) { e = e.previousSibling;  ++k;}
13844
13845             this.el.remove();
13846             
13847             this.el=false;
13848             this.rendered = false;
13849             
13850             this.render(this.parent().getChildContainer(true), k);
13851         }
13852         
13853         if(Roo.isIOS && this.useNativeIOS){
13854             this.initIOSView();
13855             return;
13856         }
13857         
13858         /*
13859          * Touch Devices
13860          */
13861         
13862         if(Roo.isTouch && this.mobileTouchView){
13863             this.initTouchView();
13864             return;
13865         }
13866         
13867         if(this.tickable){
13868             this.initTickableEvents();
13869             return;
13870         }
13871         
13872         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13873         
13874         if(this.hiddenName){
13875             
13876             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13877             
13878             this.hiddenField.dom.value =
13879                 this.hiddenValue !== undefined ? this.hiddenValue :
13880                 this.value !== undefined ? this.value : '';
13881
13882             // prevent input submission
13883             this.el.dom.removeAttribute('name');
13884             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13885              
13886              
13887         }
13888         //if(Roo.isGecko){
13889         //    this.el.dom.setAttribute('autocomplete', 'off');
13890         //}
13891         
13892         var cls = 'x-combo-list';
13893         
13894         //this.list = new Roo.Layer({
13895         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13896         //});
13897         
13898         var _this = this;
13899         
13900         (function(){
13901             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13902             _this.list.setWidth(lw);
13903         }).defer(100);
13904         
13905         this.list.on('mouseover', this.onViewOver, this);
13906         this.list.on('mousemove', this.onViewMove, this);
13907         this.list.on('scroll', this.onViewScroll, this);
13908         
13909         /*
13910         this.list.swallowEvent('mousewheel');
13911         this.assetHeight = 0;
13912
13913         if(this.title){
13914             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13915             this.assetHeight += this.header.getHeight();
13916         }
13917
13918         this.innerList = this.list.createChild({cls:cls+'-inner'});
13919         this.innerList.on('mouseover', this.onViewOver, this);
13920         this.innerList.on('mousemove', this.onViewMove, this);
13921         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13922         
13923         if(this.allowBlank && !this.pageSize && !this.disableClear){
13924             this.footer = this.list.createChild({cls:cls+'-ft'});
13925             this.pageTb = new Roo.Toolbar(this.footer);
13926            
13927         }
13928         if(this.pageSize){
13929             this.footer = this.list.createChild({cls:cls+'-ft'});
13930             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13931                     {pageSize: this.pageSize});
13932             
13933         }
13934         
13935         if (this.pageTb && this.allowBlank && !this.disableClear) {
13936             var _this = this;
13937             this.pageTb.add(new Roo.Toolbar.Fill(), {
13938                 cls: 'x-btn-icon x-btn-clear',
13939                 text: '&#160;',
13940                 handler: function()
13941                 {
13942                     _this.collapse();
13943                     _this.clearValue();
13944                     _this.onSelect(false, -1);
13945                 }
13946             });
13947         }
13948         if (this.footer) {
13949             this.assetHeight += this.footer.getHeight();
13950         }
13951         */
13952             
13953         if(!this.tpl){
13954             this.tpl = Roo.bootstrap.version == 4 ?
13955                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13956                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13957         }
13958
13959         this.view = new Roo.View(this.list, this.tpl, {
13960             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13961         });
13962         //this.view.wrapEl.setDisplayed(false);
13963         this.view.on('click', this.onViewClick, this);
13964         
13965         
13966         this.store.on('beforeload', this.onBeforeLoad, this);
13967         this.store.on('load', this.onLoad, this);
13968         this.store.on('loadexception', this.onLoadException, this);
13969         /*
13970         if(this.resizable){
13971             this.resizer = new Roo.Resizable(this.list,  {
13972                pinned:true, handles:'se'
13973             });
13974             this.resizer.on('resize', function(r, w, h){
13975                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13976                 this.listWidth = w;
13977                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13978                 this.restrictHeight();
13979             }, this);
13980             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13981         }
13982         */
13983         if(!this.editable){
13984             this.editable = true;
13985             this.setEditable(false);
13986         }
13987         
13988         /*
13989         
13990         if (typeof(this.events.add.listeners) != 'undefined') {
13991             
13992             this.addicon = this.wrap.createChild(
13993                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13994        
13995             this.addicon.on('click', function(e) {
13996                 this.fireEvent('add', this);
13997             }, this);
13998         }
13999         if (typeof(this.events.edit.listeners) != 'undefined') {
14000             
14001             this.editicon = this.wrap.createChild(
14002                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14003             if (this.addicon) {
14004                 this.editicon.setStyle('margin-left', '40px');
14005             }
14006             this.editicon.on('click', function(e) {
14007                 
14008                 // we fire even  if inothing is selected..
14009                 this.fireEvent('edit', this, this.lastData );
14010                 
14011             }, this);
14012         }
14013         */
14014         
14015         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14016             "up" : function(e){
14017                 this.inKeyMode = true;
14018                 this.selectPrev();
14019             },
14020
14021             "down" : function(e){
14022                 if(!this.isExpanded()){
14023                     this.onTriggerClick();
14024                 }else{
14025                     this.inKeyMode = true;
14026                     this.selectNext();
14027                 }
14028             },
14029
14030             "enter" : function(e){
14031 //                this.onViewClick();
14032                 //return true;
14033                 this.collapse();
14034                 
14035                 if(this.fireEvent("specialkey", this, e)){
14036                     this.onViewClick(false);
14037                 }
14038                 
14039                 return true;
14040             },
14041
14042             "esc" : function(e){
14043                 this.collapse();
14044             },
14045
14046             "tab" : function(e){
14047                 this.collapse();
14048                 
14049                 if(this.fireEvent("specialkey", this, e)){
14050                     this.onViewClick(false);
14051                 }
14052                 
14053                 return true;
14054             },
14055
14056             scope : this,
14057
14058             doRelay : function(foo, bar, hname){
14059                 if(hname == 'down' || this.scope.isExpanded()){
14060                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14061                 }
14062                 return true;
14063             },
14064
14065             forceKeyDown: true
14066         });
14067         
14068         
14069         this.queryDelay = Math.max(this.queryDelay || 10,
14070                 this.mode == 'local' ? 10 : 250);
14071         
14072         
14073         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14074         
14075         if(this.typeAhead){
14076             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14077         }
14078         if(this.editable !== false){
14079             this.inputEl().on("keyup", this.onKeyUp, this);
14080         }
14081         if(this.forceSelection){
14082             this.inputEl().on('blur', this.doForce, this);
14083         }
14084         
14085         if(this.multiple){
14086             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14087             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14088         }
14089     },
14090     
14091     initTickableEvents: function()
14092     {   
14093         this.createList();
14094         
14095         if(this.hiddenName){
14096             
14097             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14098             
14099             this.hiddenField.dom.value =
14100                 this.hiddenValue !== undefined ? this.hiddenValue :
14101                 this.value !== undefined ? this.value : '';
14102
14103             // prevent input submission
14104             this.el.dom.removeAttribute('name');
14105             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14106              
14107              
14108         }
14109         
14110 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14111         
14112         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14113         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14114         if(this.triggerList){
14115             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14116         }
14117          
14118         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14119         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14120         
14121         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14122         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14123         
14124         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14125         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14126         
14127         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14128         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14129         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14130         
14131         this.okBtn.hide();
14132         this.cancelBtn.hide();
14133         
14134         var _this = this;
14135         
14136         (function(){
14137             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14138             _this.list.setWidth(lw);
14139         }).defer(100);
14140         
14141         this.list.on('mouseover', this.onViewOver, this);
14142         this.list.on('mousemove', this.onViewMove, this);
14143         
14144         this.list.on('scroll', this.onViewScroll, this);
14145         
14146         if(!this.tpl){
14147             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14148                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14149         }
14150
14151         this.view = new Roo.View(this.list, this.tpl, {
14152             singleSelect:true,
14153             tickable:true,
14154             parent:this,
14155             store: this.store,
14156             selectedClass: this.selectedClass
14157         });
14158         
14159         //this.view.wrapEl.setDisplayed(false);
14160         this.view.on('click', this.onViewClick, this);
14161         
14162         
14163         
14164         this.store.on('beforeload', this.onBeforeLoad, this);
14165         this.store.on('load', this.onLoad, this);
14166         this.store.on('loadexception', this.onLoadException, this);
14167         
14168         if(this.editable){
14169             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14170                 "up" : function(e){
14171                     this.inKeyMode = true;
14172                     this.selectPrev();
14173                 },
14174
14175                 "down" : function(e){
14176                     this.inKeyMode = true;
14177                     this.selectNext();
14178                 },
14179
14180                 "enter" : function(e){
14181                     if(this.fireEvent("specialkey", this, e)){
14182                         this.onViewClick(false);
14183                     }
14184                     
14185                     return true;
14186                 },
14187
14188                 "esc" : function(e){
14189                     this.onTickableFooterButtonClick(e, false, false);
14190                 },
14191
14192                 "tab" : function(e){
14193                     this.fireEvent("specialkey", this, e);
14194                     
14195                     this.onTickableFooterButtonClick(e, false, false);
14196                     
14197                     return true;
14198                 },
14199
14200                 scope : this,
14201
14202                 doRelay : function(e, fn, key){
14203                     if(this.scope.isExpanded()){
14204                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14205                     }
14206                     return true;
14207                 },
14208
14209                 forceKeyDown: true
14210             });
14211         }
14212         
14213         this.queryDelay = Math.max(this.queryDelay || 10,
14214                 this.mode == 'local' ? 10 : 250);
14215         
14216         
14217         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14218         
14219         if(this.typeAhead){
14220             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14221         }
14222         
14223         if(this.editable !== false){
14224             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14225         }
14226         
14227         this.indicator = this.indicatorEl();
14228         
14229         if(this.indicator){
14230             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14231             this.indicator.hide();
14232         }
14233         
14234     },
14235
14236     onDestroy : function(){
14237         if(this.view){
14238             this.view.setStore(null);
14239             this.view.el.removeAllListeners();
14240             this.view.el.remove();
14241             this.view.purgeListeners();
14242         }
14243         if(this.list){
14244             this.list.dom.innerHTML  = '';
14245         }
14246         
14247         if(this.store){
14248             this.store.un('beforeload', this.onBeforeLoad, this);
14249             this.store.un('load', this.onLoad, this);
14250             this.store.un('loadexception', this.onLoadException, this);
14251         }
14252         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14253     },
14254
14255     // private
14256     fireKey : function(e){
14257         if(e.isNavKeyPress() && !this.list.isVisible()){
14258             this.fireEvent("specialkey", this, e);
14259         }
14260     },
14261
14262     // private
14263     onResize: function(w, h){
14264 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14265 //        
14266 //        if(typeof w != 'number'){
14267 //            // we do not handle it!?!?
14268 //            return;
14269 //        }
14270 //        var tw = this.trigger.getWidth();
14271 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14272 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14273 //        var x = w - tw;
14274 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14275 //            
14276 //        //this.trigger.setStyle('left', x+'px');
14277 //        
14278 //        if(this.list && this.listWidth === undefined){
14279 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14280 //            this.list.setWidth(lw);
14281 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14282 //        }
14283         
14284     
14285         
14286     },
14287
14288     /**
14289      * Allow or prevent the user from directly editing the field text.  If false is passed,
14290      * the user will only be able to select from the items defined in the dropdown list.  This method
14291      * is the runtime equivalent of setting the 'editable' config option at config time.
14292      * @param {Boolean} value True to allow the user to directly edit the field text
14293      */
14294     setEditable : function(value){
14295         if(value == this.editable){
14296             return;
14297         }
14298         this.editable = value;
14299         if(!value){
14300             this.inputEl().dom.setAttribute('readOnly', true);
14301             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14302             this.inputEl().addClass('x-combo-noedit');
14303         }else{
14304             this.inputEl().dom.setAttribute('readOnly', false);
14305             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14306             this.inputEl().removeClass('x-combo-noedit');
14307         }
14308     },
14309
14310     // private
14311     
14312     onBeforeLoad : function(combo,opts){
14313         if(!this.hasFocus){
14314             return;
14315         }
14316          if (!opts.add) {
14317             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14318          }
14319         this.restrictHeight();
14320         this.selectedIndex = -1;
14321     },
14322
14323     // private
14324     onLoad : function(){
14325         
14326         this.hasQuery = false;
14327         
14328         if(!this.hasFocus){
14329             return;
14330         }
14331         
14332         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14333             this.loading.hide();
14334         }
14335         
14336         if(this.store.getCount() > 0){
14337             
14338             this.expand();
14339             this.restrictHeight();
14340             if(this.lastQuery == this.allQuery){
14341                 if(this.editable && !this.tickable){
14342                     this.inputEl().dom.select();
14343                 }
14344                 
14345                 if(
14346                     !this.selectByValue(this.value, true) &&
14347                     this.autoFocus && 
14348                     (
14349                         !this.store.lastOptions ||
14350                         typeof(this.store.lastOptions.add) == 'undefined' || 
14351                         this.store.lastOptions.add != true
14352                     )
14353                 ){
14354                     this.select(0, true);
14355                 }
14356             }else{
14357                 if(this.autoFocus){
14358                     this.selectNext();
14359                 }
14360                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14361                     this.taTask.delay(this.typeAheadDelay);
14362                 }
14363             }
14364         }else{
14365             this.onEmptyResults();
14366         }
14367         
14368         //this.el.focus();
14369     },
14370     // private
14371     onLoadException : function()
14372     {
14373         this.hasQuery = false;
14374         
14375         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14376             this.loading.hide();
14377         }
14378         
14379         if(this.tickable && this.editable){
14380             return;
14381         }
14382         
14383         this.collapse();
14384         // only causes errors at present
14385         //Roo.log(this.store.reader.jsonData);
14386         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14387             // fixme
14388             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14389         //}
14390         
14391         
14392     },
14393     // private
14394     onTypeAhead : function(){
14395         if(this.store.getCount() > 0){
14396             var r = this.store.getAt(0);
14397             var newValue = r.data[this.displayField];
14398             var len = newValue.length;
14399             var selStart = this.getRawValue().length;
14400             
14401             if(selStart != len){
14402                 this.setRawValue(newValue);
14403                 this.selectText(selStart, newValue.length);
14404             }
14405         }
14406     },
14407
14408     // private
14409     onSelect : function(record, index){
14410         
14411         if(this.fireEvent('beforeselect', this, record, index) !== false){
14412         
14413             this.setFromData(index > -1 ? record.data : false);
14414             
14415             this.collapse();
14416             this.fireEvent('select', this, record, index);
14417         }
14418     },
14419
14420     /**
14421      * Returns the currently selected field value or empty string if no value is set.
14422      * @return {String} value The selected value
14423      */
14424     getValue : function()
14425     {
14426         if(Roo.isIOS && this.useNativeIOS){
14427             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14428         }
14429         
14430         if(this.multiple){
14431             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14432         }
14433         
14434         if(this.valueField){
14435             return typeof this.value != 'undefined' ? this.value : '';
14436         }else{
14437             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14438         }
14439     },
14440     
14441     getRawValue : function()
14442     {
14443         if(Roo.isIOS && this.useNativeIOS){
14444             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14445         }
14446         
14447         var v = this.inputEl().getValue();
14448         
14449         return v;
14450     },
14451
14452     /**
14453      * Clears any text/value currently set in the field
14454      */
14455     clearValue : function(){
14456         
14457         if(this.hiddenField){
14458             this.hiddenField.dom.value = '';
14459         }
14460         this.value = '';
14461         this.setRawValue('');
14462         this.lastSelectionText = '';
14463         this.lastData = false;
14464         
14465         var close = this.closeTriggerEl();
14466         
14467         if(close){
14468             close.hide();
14469         }
14470         
14471         this.validate();
14472         
14473     },
14474
14475     /**
14476      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14477      * will be displayed in the field.  If the value does not match the data value of an existing item,
14478      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14479      * Otherwise the field will be blank (although the value will still be set).
14480      * @param {String} value The value to match
14481      */
14482     setValue : function(v)
14483     {
14484         if(Roo.isIOS && this.useNativeIOS){
14485             this.setIOSValue(v);
14486             return;
14487         }
14488         
14489         if(this.multiple){
14490             this.syncValue();
14491             return;
14492         }
14493         
14494         var text = v;
14495         if(this.valueField){
14496             var r = this.findRecord(this.valueField, v);
14497             if(r){
14498                 text = r.data[this.displayField];
14499             }else if(this.valueNotFoundText !== undefined){
14500                 text = this.valueNotFoundText;
14501             }
14502         }
14503         this.lastSelectionText = text;
14504         if(this.hiddenField){
14505             this.hiddenField.dom.value = v;
14506         }
14507         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14508         this.value = v;
14509         
14510         var close = this.closeTriggerEl();
14511         
14512         if(close){
14513             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14514         }
14515         
14516         this.validate();
14517     },
14518     /**
14519      * @property {Object} the last set data for the element
14520      */
14521     
14522     lastData : false,
14523     /**
14524      * Sets the value of the field based on a object which is related to the record format for the store.
14525      * @param {Object} value the value to set as. or false on reset?
14526      */
14527     setFromData : function(o){
14528         
14529         if(this.multiple){
14530             this.addItem(o);
14531             return;
14532         }
14533             
14534         var dv = ''; // display value
14535         var vv = ''; // value value..
14536         this.lastData = o;
14537         if (this.displayField) {
14538             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14539         } else {
14540             // this is an error condition!!!
14541             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14542         }
14543         
14544         if(this.valueField){
14545             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14546         }
14547         
14548         var close = this.closeTriggerEl();
14549         
14550         if(close){
14551             if(dv.length || vv * 1 > 0){
14552                 close.show() ;
14553                 this.blockFocus=true;
14554             } else {
14555                 close.hide();
14556             }             
14557         }
14558         
14559         if(this.hiddenField){
14560             this.hiddenField.dom.value = vv;
14561             
14562             this.lastSelectionText = dv;
14563             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14564             this.value = vv;
14565             return;
14566         }
14567         // no hidden field.. - we store the value in 'value', but still display
14568         // display field!!!!
14569         this.lastSelectionText = dv;
14570         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14571         this.value = vv;
14572         
14573         
14574         
14575     },
14576     // private
14577     reset : function(){
14578         // overridden so that last data is reset..
14579         
14580         if(this.multiple){
14581             this.clearItem();
14582             return;
14583         }
14584         
14585         this.setValue(this.originalValue);
14586         //this.clearInvalid();
14587         this.lastData = false;
14588         if (this.view) {
14589             this.view.clearSelections();
14590         }
14591         
14592         this.validate();
14593     },
14594     // private
14595     findRecord : function(prop, value){
14596         var record;
14597         if(this.store.getCount() > 0){
14598             this.store.each(function(r){
14599                 if(r.data[prop] == value){
14600                     record = r;
14601                     return false;
14602                 }
14603                 return true;
14604             });
14605         }
14606         return record;
14607     },
14608     
14609     getName: function()
14610     {
14611         // returns hidden if it's set..
14612         if (!this.rendered) {return ''};
14613         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14614         
14615     },
14616     // private
14617     onViewMove : function(e, t){
14618         this.inKeyMode = false;
14619     },
14620
14621     // private
14622     onViewOver : function(e, t){
14623         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14624             return;
14625         }
14626         var item = this.view.findItemFromChild(t);
14627         
14628         if(item){
14629             var index = this.view.indexOf(item);
14630             this.select(index, false);
14631         }
14632     },
14633
14634     // private
14635     onViewClick : function(view, doFocus, el, e)
14636     {
14637         var index = this.view.getSelectedIndexes()[0];
14638         
14639         var r = this.store.getAt(index);
14640         
14641         if(this.tickable){
14642             
14643             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14644                 return;
14645             }
14646             
14647             var rm = false;
14648             var _this = this;
14649             
14650             Roo.each(this.tickItems, function(v,k){
14651                 
14652                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14653                     Roo.log(v);
14654                     _this.tickItems.splice(k, 1);
14655                     
14656                     if(typeof(e) == 'undefined' && view == false){
14657                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14658                     }
14659                     
14660                     rm = true;
14661                     return;
14662                 }
14663             });
14664             
14665             if(rm){
14666                 return;
14667             }
14668             
14669             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14670                 this.tickItems.push(r.data);
14671             }
14672             
14673             if(typeof(e) == 'undefined' && view == false){
14674                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14675             }
14676                     
14677             return;
14678         }
14679         
14680         if(r){
14681             this.onSelect(r, index);
14682         }
14683         if(doFocus !== false && !this.blockFocus){
14684             this.inputEl().focus();
14685         }
14686     },
14687
14688     // private
14689     restrictHeight : function(){
14690         //this.innerList.dom.style.height = '';
14691         //var inner = this.innerList.dom;
14692         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14693         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14694         //this.list.beginUpdate();
14695         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14696         this.list.alignTo(this.inputEl(), this.listAlign);
14697         this.list.alignTo(this.inputEl(), this.listAlign);
14698         //this.list.endUpdate();
14699     },
14700
14701     // private
14702     onEmptyResults : function(){
14703         
14704         if(this.tickable && this.editable){
14705             this.hasFocus = false;
14706             this.restrictHeight();
14707             return;
14708         }
14709         
14710         this.collapse();
14711     },
14712
14713     /**
14714      * Returns true if the dropdown list is expanded, else false.
14715      */
14716     isExpanded : function(){
14717         return this.list.isVisible();
14718     },
14719
14720     /**
14721      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14722      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14723      * @param {String} value The data value of the item to select
14724      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14725      * selected item if it is not currently in view (defaults to true)
14726      * @return {Boolean} True if the value matched an item in the list, else false
14727      */
14728     selectByValue : function(v, scrollIntoView){
14729         if(v !== undefined && v !== null){
14730             var r = this.findRecord(this.valueField || this.displayField, v);
14731             if(r){
14732                 this.select(this.store.indexOf(r), scrollIntoView);
14733                 return true;
14734             }
14735         }
14736         return false;
14737     },
14738
14739     /**
14740      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14741      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14742      * @param {Number} index The zero-based index of the list item to select
14743      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14744      * selected item if it is not currently in view (defaults to true)
14745      */
14746     select : function(index, scrollIntoView){
14747         this.selectedIndex = index;
14748         this.view.select(index);
14749         if(scrollIntoView !== false){
14750             var el = this.view.getNode(index);
14751             /*
14752              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14753              */
14754             if(el){
14755                 this.list.scrollChildIntoView(el, false);
14756             }
14757         }
14758     },
14759
14760     // private
14761     selectNext : function(){
14762         var ct = this.store.getCount();
14763         if(ct > 0){
14764             if(this.selectedIndex == -1){
14765                 this.select(0);
14766             }else if(this.selectedIndex < ct-1){
14767                 this.select(this.selectedIndex+1);
14768             }
14769         }
14770     },
14771
14772     // private
14773     selectPrev : function(){
14774         var ct = this.store.getCount();
14775         if(ct > 0){
14776             if(this.selectedIndex == -1){
14777                 this.select(0);
14778             }else if(this.selectedIndex != 0){
14779                 this.select(this.selectedIndex-1);
14780             }
14781         }
14782     },
14783
14784     // private
14785     onKeyUp : function(e){
14786         if(this.editable !== false && !e.isSpecialKey()){
14787             this.lastKey = e.getKey();
14788             this.dqTask.delay(this.queryDelay);
14789         }
14790     },
14791
14792     // private
14793     validateBlur : function(){
14794         return !this.list || !this.list.isVisible();   
14795     },
14796
14797     // private
14798     initQuery : function(){
14799         
14800         var v = this.getRawValue();
14801         
14802         if(this.tickable && this.editable){
14803             v = this.tickableInputEl().getValue();
14804         }
14805         
14806         this.doQuery(v);
14807     },
14808
14809     // private
14810     doForce : function(){
14811         if(this.inputEl().dom.value.length > 0){
14812             this.inputEl().dom.value =
14813                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14814              
14815         }
14816     },
14817
14818     /**
14819      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14820      * query allowing the query action to be canceled if needed.
14821      * @param {String} query The SQL query to execute
14822      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14823      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14824      * saved in the current store (defaults to false)
14825      */
14826     doQuery : function(q, forceAll){
14827         
14828         if(q === undefined || q === null){
14829             q = '';
14830         }
14831         var qe = {
14832             query: q,
14833             forceAll: forceAll,
14834             combo: this,
14835             cancel:false
14836         };
14837         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14838             return false;
14839         }
14840         q = qe.query;
14841         
14842         forceAll = qe.forceAll;
14843         if(forceAll === true || (q.length >= this.minChars)){
14844             
14845             this.hasQuery = true;
14846             
14847             if(this.lastQuery != q || this.alwaysQuery){
14848                 this.lastQuery = q;
14849                 if(this.mode == 'local'){
14850                     this.selectedIndex = -1;
14851                     if(forceAll){
14852                         this.store.clearFilter();
14853                     }else{
14854                         
14855                         if(this.specialFilter){
14856                             this.fireEvent('specialfilter', this);
14857                             this.onLoad();
14858                             return;
14859                         }
14860                         
14861                         this.store.filter(this.displayField, q);
14862                     }
14863                     
14864                     this.store.fireEvent("datachanged", this.store);
14865                     
14866                     this.onLoad();
14867                     
14868                     
14869                 }else{
14870                     
14871                     this.store.baseParams[this.queryParam] = q;
14872                     
14873                     var options = {params : this.getParams(q)};
14874                     
14875                     if(this.loadNext){
14876                         options.add = true;
14877                         options.params.start = this.page * this.pageSize;
14878                     }
14879                     
14880                     this.store.load(options);
14881                     
14882                     /*
14883                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14884                      *  we should expand the list on onLoad
14885                      *  so command out it
14886                      */
14887 //                    this.expand();
14888                 }
14889             }else{
14890                 this.selectedIndex = -1;
14891                 this.onLoad();   
14892             }
14893         }
14894         
14895         this.loadNext = false;
14896     },
14897     
14898     // private
14899     getParams : function(q){
14900         var p = {};
14901         //p[this.queryParam] = q;
14902         
14903         if(this.pageSize){
14904             p.start = 0;
14905             p.limit = this.pageSize;
14906         }
14907         return p;
14908     },
14909
14910     /**
14911      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14912      */
14913     collapse : function(){
14914         if(!this.isExpanded()){
14915             return;
14916         }
14917         
14918         this.list.hide();
14919         
14920         this.hasFocus = false;
14921         
14922         if(this.tickable){
14923             this.okBtn.hide();
14924             this.cancelBtn.hide();
14925             this.trigger.show();
14926             
14927             if(this.editable){
14928                 this.tickableInputEl().dom.value = '';
14929                 this.tickableInputEl().blur();
14930             }
14931             
14932         }
14933         
14934         Roo.get(document).un('mousedown', this.collapseIf, this);
14935         Roo.get(document).un('mousewheel', this.collapseIf, this);
14936         if (!this.editable) {
14937             Roo.get(document).un('keydown', this.listKeyPress, this);
14938         }
14939         this.fireEvent('collapse', this);
14940         
14941         this.validate();
14942     },
14943
14944     // private
14945     collapseIf : function(e){
14946         var in_combo  = e.within(this.el);
14947         var in_list =  e.within(this.list);
14948         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14949         
14950         if (in_combo || in_list || is_list) {
14951             //e.stopPropagation();
14952             return;
14953         }
14954         
14955         if(this.tickable){
14956             this.onTickableFooterButtonClick(e, false, false);
14957         }
14958
14959         this.collapse();
14960         
14961     },
14962
14963     /**
14964      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14965      */
14966     expand : function(){
14967        
14968         if(this.isExpanded() || !this.hasFocus){
14969             return;
14970         }
14971         
14972         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14973         this.list.setWidth(lw);
14974         
14975         Roo.log('expand');
14976         
14977         this.list.show();
14978         
14979         this.restrictHeight();
14980         
14981         if(this.tickable){
14982             
14983             this.tickItems = Roo.apply([], this.item);
14984             
14985             this.okBtn.show();
14986             this.cancelBtn.show();
14987             this.trigger.hide();
14988             
14989             if(this.editable){
14990                 this.tickableInputEl().focus();
14991             }
14992             
14993         }
14994         
14995         Roo.get(document).on('mousedown', this.collapseIf, this);
14996         Roo.get(document).on('mousewheel', this.collapseIf, this);
14997         if (!this.editable) {
14998             Roo.get(document).on('keydown', this.listKeyPress, this);
14999         }
15000         
15001         this.fireEvent('expand', this);
15002     },
15003
15004     // private
15005     // Implements the default empty TriggerField.onTriggerClick function
15006     onTriggerClick : function(e)
15007     {
15008         Roo.log('trigger click');
15009         
15010         if(this.disabled || !this.triggerList){
15011             return;
15012         }
15013         
15014         this.page = 0;
15015         this.loadNext = false;
15016         
15017         if(this.isExpanded()){
15018             this.collapse();
15019             if (!this.blockFocus) {
15020                 this.inputEl().focus();
15021             }
15022             
15023         }else {
15024             this.hasFocus = true;
15025             if(this.triggerAction == 'all') {
15026                 this.doQuery(this.allQuery, true);
15027             } else {
15028                 this.doQuery(this.getRawValue());
15029             }
15030             if (!this.blockFocus) {
15031                 this.inputEl().focus();
15032             }
15033         }
15034     },
15035     
15036     onTickableTriggerClick : function(e)
15037     {
15038         if(this.disabled){
15039             return;
15040         }
15041         
15042         this.page = 0;
15043         this.loadNext = false;
15044         this.hasFocus = true;
15045         
15046         if(this.triggerAction == 'all') {
15047             this.doQuery(this.allQuery, true);
15048         } else {
15049             this.doQuery(this.getRawValue());
15050         }
15051     },
15052     
15053     onSearchFieldClick : function(e)
15054     {
15055         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15056             this.onTickableFooterButtonClick(e, false, false);
15057             return;
15058         }
15059         
15060         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15061             return;
15062         }
15063         
15064         this.page = 0;
15065         this.loadNext = false;
15066         this.hasFocus = true;
15067         
15068         if(this.triggerAction == 'all') {
15069             this.doQuery(this.allQuery, true);
15070         } else {
15071             this.doQuery(this.getRawValue());
15072         }
15073     },
15074     
15075     listKeyPress : function(e)
15076     {
15077         //Roo.log('listkeypress');
15078         // scroll to first matching element based on key pres..
15079         if (e.isSpecialKey()) {
15080             return false;
15081         }
15082         var k = String.fromCharCode(e.getKey()).toUpperCase();
15083         //Roo.log(k);
15084         var match  = false;
15085         var csel = this.view.getSelectedNodes();
15086         var cselitem = false;
15087         if (csel.length) {
15088             var ix = this.view.indexOf(csel[0]);
15089             cselitem  = this.store.getAt(ix);
15090             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15091                 cselitem = false;
15092             }
15093             
15094         }
15095         
15096         this.store.each(function(v) { 
15097             if (cselitem) {
15098                 // start at existing selection.
15099                 if (cselitem.id == v.id) {
15100                     cselitem = false;
15101                 }
15102                 return true;
15103             }
15104                 
15105             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15106                 match = this.store.indexOf(v);
15107                 return false;
15108             }
15109             return true;
15110         }, this);
15111         
15112         if (match === false) {
15113             return true; // no more action?
15114         }
15115         // scroll to?
15116         this.view.select(match);
15117         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15118         sn.scrollIntoView(sn.dom.parentNode, false);
15119     },
15120     
15121     onViewScroll : function(e, t){
15122         
15123         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){
15124             return;
15125         }
15126         
15127         this.hasQuery = true;
15128         
15129         this.loading = this.list.select('.loading', true).first();
15130         
15131         if(this.loading === null){
15132             this.list.createChild({
15133                 tag: 'div',
15134                 cls: 'loading roo-select2-more-results roo-select2-active',
15135                 html: 'Loading more results...'
15136             });
15137             
15138             this.loading = this.list.select('.loading', true).first();
15139             
15140             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15141             
15142             this.loading.hide();
15143         }
15144         
15145         this.loading.show();
15146         
15147         var _combo = this;
15148         
15149         this.page++;
15150         this.loadNext = true;
15151         
15152         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15153         
15154         return;
15155     },
15156     
15157     addItem : function(o)
15158     {   
15159         var dv = ''; // display value
15160         
15161         if (this.displayField) {
15162             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15163         } else {
15164             // this is an error condition!!!
15165             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15166         }
15167         
15168         if(!dv.length){
15169             return;
15170         }
15171         
15172         var choice = this.choices.createChild({
15173             tag: 'li',
15174             cls: 'roo-select2-search-choice',
15175             cn: [
15176                 {
15177                     tag: 'div',
15178                     html: dv
15179                 },
15180                 {
15181                     tag: 'a',
15182                     href: '#',
15183                     cls: 'roo-select2-search-choice-close fa fa-times',
15184                     tabindex: '-1'
15185                 }
15186             ]
15187             
15188         }, this.searchField);
15189         
15190         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15191         
15192         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15193         
15194         this.item.push(o);
15195         
15196         this.lastData = o;
15197         
15198         this.syncValue();
15199         
15200         this.inputEl().dom.value = '';
15201         
15202         this.validate();
15203     },
15204     
15205     onRemoveItem : function(e, _self, o)
15206     {
15207         e.preventDefault();
15208         
15209         this.lastItem = Roo.apply([], this.item);
15210         
15211         var index = this.item.indexOf(o.data) * 1;
15212         
15213         if( index < 0){
15214             Roo.log('not this item?!');
15215             return;
15216         }
15217         
15218         this.item.splice(index, 1);
15219         o.item.remove();
15220         
15221         this.syncValue();
15222         
15223         this.fireEvent('remove', this, e);
15224         
15225         this.validate();
15226         
15227     },
15228     
15229     syncValue : function()
15230     {
15231         if(!this.item.length){
15232             this.clearValue();
15233             return;
15234         }
15235             
15236         var value = [];
15237         var _this = this;
15238         Roo.each(this.item, function(i){
15239             if(_this.valueField){
15240                 value.push(i[_this.valueField]);
15241                 return;
15242             }
15243
15244             value.push(i);
15245         });
15246
15247         this.value = value.join(',');
15248
15249         if(this.hiddenField){
15250             this.hiddenField.dom.value = this.value;
15251         }
15252         
15253         this.store.fireEvent("datachanged", this.store);
15254         
15255         this.validate();
15256     },
15257     
15258     clearItem : function()
15259     {
15260         if(!this.multiple){
15261             return;
15262         }
15263         
15264         this.item = [];
15265         
15266         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15267            c.remove();
15268         });
15269         
15270         this.syncValue();
15271         
15272         this.validate();
15273         
15274         if(this.tickable && !Roo.isTouch){
15275             this.view.refresh();
15276         }
15277     },
15278     
15279     inputEl: function ()
15280     {
15281         if(Roo.isIOS && this.useNativeIOS){
15282             return this.el.select('select.roo-ios-select', true).first();
15283         }
15284         
15285         if(Roo.isTouch && this.mobileTouchView){
15286             return this.el.select('input.form-control',true).first();
15287         }
15288         
15289         if(this.tickable){
15290             return this.searchField;
15291         }
15292         
15293         return this.el.select('input.form-control',true).first();
15294     },
15295     
15296     onTickableFooterButtonClick : function(e, btn, el)
15297     {
15298         e.preventDefault();
15299         
15300         this.lastItem = Roo.apply([], this.item);
15301         
15302         if(btn && btn.name == 'cancel'){
15303             this.tickItems = Roo.apply([], this.item);
15304             this.collapse();
15305             return;
15306         }
15307         
15308         this.clearItem();
15309         
15310         var _this = this;
15311         
15312         Roo.each(this.tickItems, function(o){
15313             _this.addItem(o);
15314         });
15315         
15316         this.collapse();
15317         
15318     },
15319     
15320     validate : function()
15321     {
15322         if(this.getVisibilityEl().hasClass('hidden')){
15323             return true;
15324         }
15325         
15326         var v = this.getRawValue();
15327         
15328         if(this.multiple){
15329             v = this.getValue();
15330         }
15331         
15332         if(this.disabled || this.allowBlank || v.length){
15333             this.markValid();
15334             return true;
15335         }
15336         
15337         this.markInvalid();
15338         return false;
15339     },
15340     
15341     tickableInputEl : function()
15342     {
15343         if(!this.tickable || !this.editable){
15344             return this.inputEl();
15345         }
15346         
15347         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15348     },
15349     
15350     
15351     getAutoCreateTouchView : function()
15352     {
15353         var id = Roo.id();
15354         
15355         var cfg = {
15356             cls: 'form-group' //input-group
15357         };
15358         
15359         var input =  {
15360             tag: 'input',
15361             id : id,
15362             type : this.inputType,
15363             cls : 'form-control x-combo-noedit',
15364             autocomplete: 'new-password',
15365             placeholder : this.placeholder || '',
15366             readonly : true
15367         };
15368         
15369         if (this.name) {
15370             input.name = this.name;
15371         }
15372         
15373         if (this.size) {
15374             input.cls += ' input-' + this.size;
15375         }
15376         
15377         if (this.disabled) {
15378             input.disabled = true;
15379         }
15380         
15381         var inputblock = {
15382             cls : '',
15383             cn : [
15384                 input
15385             ]
15386         };
15387         
15388         if(this.before){
15389             inputblock.cls += ' input-group';
15390             
15391             inputblock.cn.unshift({
15392                 tag :'span',
15393                 cls : 'input-group-addon input-group-prepend input-group-text',
15394                 html : this.before
15395             });
15396         }
15397         
15398         if(this.removable && !this.multiple){
15399             inputblock.cls += ' roo-removable';
15400             
15401             inputblock.cn.push({
15402                 tag: 'button',
15403                 html : 'x',
15404                 cls : 'roo-combo-removable-btn close'
15405             });
15406         }
15407
15408         if(this.hasFeedback && !this.allowBlank){
15409             
15410             inputblock.cls += ' has-feedback';
15411             
15412             inputblock.cn.push({
15413                 tag: 'span',
15414                 cls: 'glyphicon form-control-feedback'
15415             });
15416             
15417         }
15418         
15419         if (this.after) {
15420             
15421             inputblock.cls += (this.before) ? '' : ' input-group';
15422             
15423             inputblock.cn.push({
15424                 tag :'span',
15425                 cls : 'input-group-addon input-group-append input-group-text',
15426                 html : this.after
15427             });
15428         }
15429
15430         
15431         var ibwrap = inputblock;
15432         
15433         if(this.multiple){
15434             ibwrap = {
15435                 tag: 'ul',
15436                 cls: 'roo-select2-choices',
15437                 cn:[
15438                     {
15439                         tag: 'li',
15440                         cls: 'roo-select2-search-field',
15441                         cn: [
15442
15443                             inputblock
15444                         ]
15445                     }
15446                 ]
15447             };
15448         
15449             
15450         }
15451         
15452         var combobox = {
15453             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15454             cn: [
15455                 {
15456                     tag: 'input',
15457                     type : 'hidden',
15458                     cls: 'form-hidden-field'
15459                 },
15460                 ibwrap
15461             ]
15462         };
15463         
15464         if(!this.multiple && this.showToggleBtn){
15465             
15466             var caret = {
15467                 cls: 'caret'
15468             };
15469             
15470             if (this.caret != false) {
15471                 caret = {
15472                      tag: 'i',
15473                      cls: 'fa fa-' + this.caret
15474                 };
15475                 
15476             }
15477             
15478             combobox.cn.push({
15479                 tag :'span',
15480                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15481                 cn : [
15482                     Roo.bootstrap.version == 3 ? caret : '',
15483                     {
15484                         tag: 'span',
15485                         cls: 'combobox-clear',
15486                         cn  : [
15487                             {
15488                                 tag : 'i',
15489                                 cls: 'icon-remove'
15490                             }
15491                         ]
15492                     }
15493                 ]
15494
15495             })
15496         }
15497         
15498         if(this.multiple){
15499             combobox.cls += ' roo-select2-container-multi';
15500         }
15501         
15502         var align = this.labelAlign || this.parentLabelAlign();
15503         
15504         if (align ==='left' && this.fieldLabel.length) {
15505
15506             cfg.cn = [
15507                 {
15508                    tag : 'i',
15509                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15510                    tooltip : 'This field is required'
15511                 },
15512                 {
15513                     tag: 'label',
15514                     cls : 'control-label col-form-label',
15515                     html : this.fieldLabel
15516
15517                 },
15518                 {
15519                     cls : '', 
15520                     cn: [
15521                         combobox
15522                     ]
15523                 }
15524             ];
15525             
15526             var labelCfg = cfg.cn[1];
15527             var contentCfg = cfg.cn[2];
15528             
15529
15530             if(this.indicatorpos == 'right'){
15531                 cfg.cn = [
15532                     {
15533                         tag: 'label',
15534                         'for' :  id,
15535                         cls : 'control-label col-form-label',
15536                         cn : [
15537                             {
15538                                 tag : 'span',
15539                                 html : this.fieldLabel
15540                             },
15541                             {
15542                                 tag : 'i',
15543                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15544                                 tooltip : 'This field is required'
15545                             }
15546                         ]
15547                     },
15548                     {
15549                         cls : "",
15550                         cn: [
15551                             combobox
15552                         ]
15553                     }
15554
15555                 ];
15556                 
15557                 labelCfg = cfg.cn[0];
15558                 contentCfg = cfg.cn[1];
15559             }
15560             
15561            
15562             
15563             if(this.labelWidth > 12){
15564                 labelCfg.style = "width: " + this.labelWidth + 'px';
15565             }
15566             
15567             if(this.labelWidth < 13 && this.labelmd == 0){
15568                 this.labelmd = this.labelWidth;
15569             }
15570             
15571             if(this.labellg > 0){
15572                 labelCfg.cls += ' col-lg-' + this.labellg;
15573                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15574             }
15575             
15576             if(this.labelmd > 0){
15577                 labelCfg.cls += ' col-md-' + this.labelmd;
15578                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15579             }
15580             
15581             if(this.labelsm > 0){
15582                 labelCfg.cls += ' col-sm-' + this.labelsm;
15583                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15584             }
15585             
15586             if(this.labelxs > 0){
15587                 labelCfg.cls += ' col-xs-' + this.labelxs;
15588                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15589             }
15590                 
15591                 
15592         } else if ( this.fieldLabel.length) {
15593             cfg.cn = [
15594                 {
15595                    tag : 'i',
15596                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15597                    tooltip : 'This field is required'
15598                 },
15599                 {
15600                     tag: 'label',
15601                     cls : 'control-label',
15602                     html : this.fieldLabel
15603
15604                 },
15605                 {
15606                     cls : '', 
15607                     cn: [
15608                         combobox
15609                     ]
15610                 }
15611             ];
15612             
15613             if(this.indicatorpos == 'right'){
15614                 cfg.cn = [
15615                     {
15616                         tag: 'label',
15617                         cls : 'control-label',
15618                         html : this.fieldLabel,
15619                         cn : [
15620                             {
15621                                tag : 'i',
15622                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15623                                tooltip : 'This field is required'
15624                             }
15625                         ]
15626                     },
15627                     {
15628                         cls : '', 
15629                         cn: [
15630                             combobox
15631                         ]
15632                     }
15633                 ];
15634             }
15635         } else {
15636             cfg.cn = combobox;    
15637         }
15638         
15639         
15640         var settings = this;
15641         
15642         ['xs','sm','md','lg'].map(function(size){
15643             if (settings[size]) {
15644                 cfg.cls += ' col-' + size + '-' + settings[size];
15645             }
15646         });
15647         
15648         return cfg;
15649     },
15650     
15651     initTouchView : function()
15652     {
15653         this.renderTouchView();
15654         
15655         this.touchViewEl.on('scroll', function(){
15656             this.el.dom.scrollTop = 0;
15657         }, this);
15658         
15659         this.originalValue = this.getValue();
15660         
15661         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15662         
15663         this.inputEl().on("click", this.showTouchView, this);
15664         if (this.triggerEl) {
15665             this.triggerEl.on("click", this.showTouchView, this);
15666         }
15667         
15668         
15669         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15670         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15671         
15672         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15673         
15674         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15675         this.store.on('load', this.onTouchViewLoad, this);
15676         this.store.on('loadexception', this.onTouchViewLoadException, this);
15677         
15678         if(this.hiddenName){
15679             
15680             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15681             
15682             this.hiddenField.dom.value =
15683                 this.hiddenValue !== undefined ? this.hiddenValue :
15684                 this.value !== undefined ? this.value : '';
15685         
15686             this.el.dom.removeAttribute('name');
15687             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15688         }
15689         
15690         if(this.multiple){
15691             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15692             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15693         }
15694         
15695         if(this.removable && !this.multiple){
15696             var close = this.closeTriggerEl();
15697             if(close){
15698                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15699                 close.on('click', this.removeBtnClick, this, close);
15700             }
15701         }
15702         /*
15703          * fix the bug in Safari iOS8
15704          */
15705         this.inputEl().on("focus", function(e){
15706             document.activeElement.blur();
15707         }, this);
15708         
15709         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15710         
15711         return;
15712         
15713         
15714     },
15715     
15716     renderTouchView : function()
15717     {
15718         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15719         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15720         
15721         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15722         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15723         
15724         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15725         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15726         this.touchViewBodyEl.setStyle('overflow', 'auto');
15727         
15728         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15729         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15730         
15731         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15732         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15733         
15734     },
15735     
15736     showTouchView : function()
15737     {
15738         if(this.disabled){
15739             return;
15740         }
15741         
15742         this.touchViewHeaderEl.hide();
15743
15744         if(this.modalTitle.length){
15745             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15746             this.touchViewHeaderEl.show();
15747         }
15748
15749         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15750         this.touchViewEl.show();
15751
15752         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15753         
15754         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15755         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15756
15757         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15758
15759         if(this.modalTitle.length){
15760             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15761         }
15762         
15763         this.touchViewBodyEl.setHeight(bodyHeight);
15764
15765         if(this.animate){
15766             var _this = this;
15767             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15768         }else{
15769             this.touchViewEl.addClass('in');
15770         }
15771         
15772         if(this._touchViewMask){
15773             Roo.get(document.body).addClass("x-body-masked");
15774             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15775             this._touchViewMask.setStyle('z-index', 10000);
15776             this._touchViewMask.addClass('show');
15777         }
15778         
15779         this.doTouchViewQuery();
15780         
15781     },
15782     
15783     hideTouchView : function()
15784     {
15785         this.touchViewEl.removeClass('in');
15786
15787         if(this.animate){
15788             var _this = this;
15789             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15790         }else{
15791             this.touchViewEl.setStyle('display', 'none');
15792         }
15793         
15794         if(this._touchViewMask){
15795             this._touchViewMask.removeClass('show');
15796             Roo.get(document.body).removeClass("x-body-masked");
15797         }
15798     },
15799     
15800     setTouchViewValue : function()
15801     {
15802         if(this.multiple){
15803             this.clearItem();
15804         
15805             var _this = this;
15806
15807             Roo.each(this.tickItems, function(o){
15808                 this.addItem(o);
15809             }, this);
15810         }
15811         
15812         this.hideTouchView();
15813     },
15814     
15815     doTouchViewQuery : function()
15816     {
15817         var qe = {
15818             query: '',
15819             forceAll: true,
15820             combo: this,
15821             cancel:false
15822         };
15823         
15824         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15825             return false;
15826         }
15827         
15828         if(!this.alwaysQuery || this.mode == 'local'){
15829             this.onTouchViewLoad();
15830             return;
15831         }
15832         
15833         this.store.load();
15834     },
15835     
15836     onTouchViewBeforeLoad : function(combo,opts)
15837     {
15838         return;
15839     },
15840
15841     // private
15842     onTouchViewLoad : function()
15843     {
15844         if(this.store.getCount() < 1){
15845             this.onTouchViewEmptyResults();
15846             return;
15847         }
15848         
15849         this.clearTouchView();
15850         
15851         var rawValue = this.getRawValue();
15852         
15853         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15854         
15855         this.tickItems = [];
15856         
15857         this.store.data.each(function(d, rowIndex){
15858             var row = this.touchViewListGroup.createChild(template);
15859             
15860             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15861                 row.addClass(d.data.cls);
15862             }
15863             
15864             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15865                 var cfg = {
15866                     data : d.data,
15867                     html : d.data[this.displayField]
15868                 };
15869                 
15870                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15871                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15872                 }
15873             }
15874             row.removeClass('selected');
15875             if(!this.multiple && this.valueField &&
15876                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15877             {
15878                 // radio buttons..
15879                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15880                 row.addClass('selected');
15881             }
15882             
15883             if(this.multiple && this.valueField &&
15884                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15885             {
15886                 
15887                 // checkboxes...
15888                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15889                 this.tickItems.push(d.data);
15890             }
15891             
15892             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15893             
15894         }, this);
15895         
15896         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15897         
15898         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15899
15900         if(this.modalTitle.length){
15901             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15902         }
15903
15904         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15905         
15906         if(this.mobile_restrict_height && listHeight < bodyHeight){
15907             this.touchViewBodyEl.setHeight(listHeight);
15908         }
15909         
15910         var _this = this;
15911         
15912         if(firstChecked && listHeight > bodyHeight){
15913             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15914         }
15915         
15916     },
15917     
15918     onTouchViewLoadException : function()
15919     {
15920         this.hideTouchView();
15921     },
15922     
15923     onTouchViewEmptyResults : function()
15924     {
15925         this.clearTouchView();
15926         
15927         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15928         
15929         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15930         
15931     },
15932     
15933     clearTouchView : function()
15934     {
15935         this.touchViewListGroup.dom.innerHTML = '';
15936     },
15937     
15938     onTouchViewClick : function(e, el, o)
15939     {
15940         e.preventDefault();
15941         
15942         var row = o.row;
15943         var rowIndex = o.rowIndex;
15944         
15945         var r = this.store.getAt(rowIndex);
15946         
15947         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15948             
15949             if(!this.multiple){
15950                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15951                     c.dom.removeAttribute('checked');
15952                 }, this);
15953
15954                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15955
15956                 this.setFromData(r.data);
15957
15958                 var close = this.closeTriggerEl();
15959
15960                 if(close){
15961                     close.show();
15962                 }
15963
15964                 this.hideTouchView();
15965
15966                 this.fireEvent('select', this, r, rowIndex);
15967
15968                 return;
15969             }
15970
15971             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15972                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15973                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15974                 return;
15975             }
15976
15977             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15978             this.addItem(r.data);
15979             this.tickItems.push(r.data);
15980         }
15981     },
15982     
15983     getAutoCreateNativeIOS : function()
15984     {
15985         var cfg = {
15986             cls: 'form-group' //input-group,
15987         };
15988         
15989         var combobox =  {
15990             tag: 'select',
15991             cls : 'roo-ios-select'
15992         };
15993         
15994         if (this.name) {
15995             combobox.name = this.name;
15996         }
15997         
15998         if (this.disabled) {
15999             combobox.disabled = true;
16000         }
16001         
16002         var settings = this;
16003         
16004         ['xs','sm','md','lg'].map(function(size){
16005             if (settings[size]) {
16006                 cfg.cls += ' col-' + size + '-' + settings[size];
16007             }
16008         });
16009         
16010         cfg.cn = combobox;
16011         
16012         return cfg;
16013         
16014     },
16015     
16016     initIOSView : function()
16017     {
16018         this.store.on('load', this.onIOSViewLoad, this);
16019         
16020         return;
16021     },
16022     
16023     onIOSViewLoad : function()
16024     {
16025         if(this.store.getCount() < 1){
16026             return;
16027         }
16028         
16029         this.clearIOSView();
16030         
16031         if(this.allowBlank) {
16032             
16033             var default_text = '-- SELECT --';
16034             
16035             if(this.placeholder.length){
16036                 default_text = this.placeholder;
16037             }
16038             
16039             if(this.emptyTitle.length){
16040                 default_text += ' - ' + this.emptyTitle + ' -';
16041             }
16042             
16043             var opt = this.inputEl().createChild({
16044                 tag: 'option',
16045                 value : 0,
16046                 html : default_text
16047             });
16048             
16049             var o = {};
16050             o[this.valueField] = 0;
16051             o[this.displayField] = default_text;
16052             
16053             this.ios_options.push({
16054                 data : o,
16055                 el : opt
16056             });
16057             
16058         }
16059         
16060         this.store.data.each(function(d, rowIndex){
16061             
16062             var html = '';
16063             
16064             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16065                 html = d.data[this.displayField];
16066             }
16067             
16068             var value = '';
16069             
16070             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16071                 value = d.data[this.valueField];
16072             }
16073             
16074             var option = {
16075                 tag: 'option',
16076                 value : value,
16077                 html : html
16078             };
16079             
16080             if(this.value == d.data[this.valueField]){
16081                 option['selected'] = true;
16082             }
16083             
16084             var opt = this.inputEl().createChild(option);
16085             
16086             this.ios_options.push({
16087                 data : d.data,
16088                 el : opt
16089             });
16090             
16091         }, this);
16092         
16093         this.inputEl().on('change', function(){
16094            this.fireEvent('select', this);
16095         }, this);
16096         
16097     },
16098     
16099     clearIOSView: function()
16100     {
16101         this.inputEl().dom.innerHTML = '';
16102         
16103         this.ios_options = [];
16104     },
16105     
16106     setIOSValue: function(v)
16107     {
16108         this.value = v;
16109         
16110         if(!this.ios_options){
16111             return;
16112         }
16113         
16114         Roo.each(this.ios_options, function(opts){
16115            
16116            opts.el.dom.removeAttribute('selected');
16117            
16118            if(opts.data[this.valueField] != v){
16119                return;
16120            }
16121            
16122            opts.el.dom.setAttribute('selected', true);
16123            
16124         }, this);
16125     }
16126
16127     /** 
16128     * @cfg {Boolean} grow 
16129     * @hide 
16130     */
16131     /** 
16132     * @cfg {Number} growMin 
16133     * @hide 
16134     */
16135     /** 
16136     * @cfg {Number} growMax 
16137     * @hide 
16138     */
16139     /**
16140      * @hide
16141      * @method autoSize
16142      */
16143 });
16144
16145 Roo.apply(Roo.bootstrap.ComboBox,  {
16146     
16147     header : {
16148         tag: 'div',
16149         cls: 'modal-header',
16150         cn: [
16151             {
16152                 tag: 'h4',
16153                 cls: 'modal-title'
16154             }
16155         ]
16156     },
16157     
16158     body : {
16159         tag: 'div',
16160         cls: 'modal-body',
16161         cn: [
16162             {
16163                 tag: 'ul',
16164                 cls: 'list-group'
16165             }
16166         ]
16167     },
16168     
16169     listItemRadio : {
16170         tag: 'li',
16171         cls: 'list-group-item',
16172         cn: [
16173             {
16174                 tag: 'span',
16175                 cls: 'roo-combobox-list-group-item-value'
16176             },
16177             {
16178                 tag: 'div',
16179                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16180                 cn: [
16181                     {
16182                         tag: 'input',
16183                         type: 'radio'
16184                     },
16185                     {
16186                         tag: 'label'
16187                     }
16188                 ]
16189             }
16190         ]
16191     },
16192     
16193     listItemCheckbox : {
16194         tag: 'li',
16195         cls: 'list-group-item',
16196         cn: [
16197             {
16198                 tag: 'span',
16199                 cls: 'roo-combobox-list-group-item-value'
16200             },
16201             {
16202                 tag: 'div',
16203                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16204                 cn: [
16205                     {
16206                         tag: 'input',
16207                         type: 'checkbox'
16208                     },
16209                     {
16210                         tag: 'label'
16211                     }
16212                 ]
16213             }
16214         ]
16215     },
16216     
16217     emptyResult : {
16218         tag: 'div',
16219         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16220     },
16221     
16222     footer : {
16223         tag: 'div',
16224         cls: 'modal-footer',
16225         cn: [
16226             {
16227                 tag: 'div',
16228                 cls: 'row',
16229                 cn: [
16230                     {
16231                         tag: 'div',
16232                         cls: 'col-xs-6 text-left',
16233                         cn: {
16234                             tag: 'button',
16235                             cls: 'btn btn-danger roo-touch-view-cancel',
16236                             html: 'Cancel'
16237                         }
16238                     },
16239                     {
16240                         tag: 'div',
16241                         cls: 'col-xs-6 text-right',
16242                         cn: {
16243                             tag: 'button',
16244                             cls: 'btn btn-success roo-touch-view-ok',
16245                             html: 'OK'
16246                         }
16247                     }
16248                 ]
16249             }
16250         ]
16251         
16252     }
16253 });
16254
16255 Roo.apply(Roo.bootstrap.ComboBox,  {
16256     
16257     touchViewTemplate : {
16258         tag: 'div',
16259         cls: 'modal fade roo-combobox-touch-view',
16260         cn: [
16261             {
16262                 tag: 'div',
16263                 cls: 'modal-dialog',
16264                 style : 'position:fixed', // we have to fix position....
16265                 cn: [
16266                     {
16267                         tag: 'div',
16268                         cls: 'modal-content',
16269                         cn: [
16270                             Roo.bootstrap.ComboBox.header,
16271                             Roo.bootstrap.ComboBox.body,
16272                             Roo.bootstrap.ComboBox.footer
16273                         ]
16274                     }
16275                 ]
16276             }
16277         ]
16278     }
16279 });/*
16280  * Based on:
16281  * Ext JS Library 1.1.1
16282  * Copyright(c) 2006-2007, Ext JS, LLC.
16283  *
16284  * Originally Released Under LGPL - original licence link has changed is not relivant.
16285  *
16286  * Fork - LGPL
16287  * <script type="text/javascript">
16288  */
16289
16290 /**
16291  * @class Roo.View
16292  * @extends Roo.util.Observable
16293  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16294  * This class also supports single and multi selection modes. <br>
16295  * Create a data model bound view:
16296  <pre><code>
16297  var store = new Roo.data.Store(...);
16298
16299  var view = new Roo.View({
16300     el : "my-element",
16301     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16302  
16303     singleSelect: true,
16304     selectedClass: "ydataview-selected",
16305     store: store
16306  });
16307
16308  // listen for node click?
16309  view.on("click", function(vw, index, node, e){
16310  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16311  });
16312
16313  // load XML data
16314  dataModel.load("foobar.xml");
16315  </code></pre>
16316  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16317  * <br><br>
16318  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16319  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16320  * 
16321  * Note: old style constructor is still suported (container, template, config)
16322  * 
16323  * @constructor
16324  * Create a new View
16325  * @param {Object} config The config object
16326  * 
16327  */
16328 Roo.View = function(config, depreciated_tpl, depreciated_config){
16329     
16330     this.parent = false;
16331     
16332     if (typeof(depreciated_tpl) == 'undefined') {
16333         // new way.. - universal constructor.
16334         Roo.apply(this, config);
16335         this.el  = Roo.get(this.el);
16336     } else {
16337         // old format..
16338         this.el  = Roo.get(config);
16339         this.tpl = depreciated_tpl;
16340         Roo.apply(this, depreciated_config);
16341     }
16342     this.wrapEl  = this.el.wrap().wrap();
16343     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16344     
16345     
16346     if(typeof(this.tpl) == "string"){
16347         this.tpl = new Roo.Template(this.tpl);
16348     } else {
16349         // support xtype ctors..
16350         this.tpl = new Roo.factory(this.tpl, Roo);
16351     }
16352     
16353     
16354     this.tpl.compile();
16355     
16356     /** @private */
16357     this.addEvents({
16358         /**
16359          * @event beforeclick
16360          * Fires before a click is processed. Returns false to cancel the default action.
16361          * @param {Roo.View} this
16362          * @param {Number} index The index of the target node
16363          * @param {HTMLElement} node The target node
16364          * @param {Roo.EventObject} e The raw event object
16365          */
16366             "beforeclick" : true,
16367         /**
16368          * @event click
16369          * Fires when a template node is clicked.
16370          * @param {Roo.View} this
16371          * @param {Number} index The index of the target node
16372          * @param {HTMLElement} node The target node
16373          * @param {Roo.EventObject} e The raw event object
16374          */
16375             "click" : true,
16376         /**
16377          * @event dblclick
16378          * Fires when a template node is double clicked.
16379          * @param {Roo.View} this
16380          * @param {Number} index The index of the target node
16381          * @param {HTMLElement} node The target node
16382          * @param {Roo.EventObject} e The raw event object
16383          */
16384             "dblclick" : true,
16385         /**
16386          * @event contextmenu
16387          * Fires when a template node is right clicked.
16388          * @param {Roo.View} this
16389          * @param {Number} index The index of the target node
16390          * @param {HTMLElement} node The target node
16391          * @param {Roo.EventObject} e The raw event object
16392          */
16393             "contextmenu" : true,
16394         /**
16395          * @event selectionchange
16396          * Fires when the selected nodes change.
16397          * @param {Roo.View} this
16398          * @param {Array} selections Array of the selected nodes
16399          */
16400             "selectionchange" : true,
16401     
16402         /**
16403          * @event beforeselect
16404          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16405          * @param {Roo.View} this
16406          * @param {HTMLElement} node The node to be selected
16407          * @param {Array} selections Array of currently selected nodes
16408          */
16409             "beforeselect" : true,
16410         /**
16411          * @event preparedata
16412          * Fires on every row to render, to allow you to change the data.
16413          * @param {Roo.View} this
16414          * @param {Object} data to be rendered (change this)
16415          */
16416           "preparedata" : true
16417           
16418           
16419         });
16420
16421
16422
16423     this.el.on({
16424         "click": this.onClick,
16425         "dblclick": this.onDblClick,
16426         "contextmenu": this.onContextMenu,
16427         scope:this
16428     });
16429
16430     this.selections = [];
16431     this.nodes = [];
16432     this.cmp = new Roo.CompositeElementLite([]);
16433     if(this.store){
16434         this.store = Roo.factory(this.store, Roo.data);
16435         this.setStore(this.store, true);
16436     }
16437     
16438     if ( this.footer && this.footer.xtype) {
16439            
16440          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16441         
16442         this.footer.dataSource = this.store;
16443         this.footer.container = fctr;
16444         this.footer = Roo.factory(this.footer, Roo);
16445         fctr.insertFirst(this.el);
16446         
16447         // this is a bit insane - as the paging toolbar seems to detach the el..
16448 //        dom.parentNode.parentNode.parentNode
16449          // they get detached?
16450     }
16451     
16452     
16453     Roo.View.superclass.constructor.call(this);
16454     
16455     
16456 };
16457
16458 Roo.extend(Roo.View, Roo.util.Observable, {
16459     
16460      /**
16461      * @cfg {Roo.data.Store} store Data store to load data from.
16462      */
16463     store : false,
16464     
16465     /**
16466      * @cfg {String|Roo.Element} el The container element.
16467      */
16468     el : '',
16469     
16470     /**
16471      * @cfg {String|Roo.Template} tpl The template used by this View 
16472      */
16473     tpl : false,
16474     /**
16475      * @cfg {String} dataName the named area of the template to use as the data area
16476      *                          Works with domtemplates roo-name="name"
16477      */
16478     dataName: false,
16479     /**
16480      * @cfg {String} selectedClass The css class to add to selected nodes
16481      */
16482     selectedClass : "x-view-selected",
16483      /**
16484      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16485      */
16486     emptyText : "",
16487     
16488     /**
16489      * @cfg {String} text to display on mask (default Loading)
16490      */
16491     mask : false,
16492     /**
16493      * @cfg {Boolean} multiSelect Allow multiple selection
16494      */
16495     multiSelect : false,
16496     /**
16497      * @cfg {Boolean} singleSelect Allow single selection
16498      */
16499     singleSelect:  false,
16500     
16501     /**
16502      * @cfg {Boolean} toggleSelect - selecting 
16503      */
16504     toggleSelect : false,
16505     
16506     /**
16507      * @cfg {Boolean} tickable - selecting 
16508      */
16509     tickable : false,
16510     
16511     /**
16512      * Returns the element this view is bound to.
16513      * @return {Roo.Element}
16514      */
16515     getEl : function(){
16516         return this.wrapEl;
16517     },
16518     
16519     
16520
16521     /**
16522      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16523      */
16524     refresh : function(){
16525         //Roo.log('refresh');
16526         var t = this.tpl;
16527         
16528         // if we are using something like 'domtemplate', then
16529         // the what gets used is:
16530         // t.applySubtemplate(NAME, data, wrapping data..)
16531         // the outer template then get' applied with
16532         //     the store 'extra data'
16533         // and the body get's added to the
16534         //      roo-name="data" node?
16535         //      <span class='roo-tpl-{name}'></span> ?????
16536         
16537         
16538         
16539         this.clearSelections();
16540         this.el.update("");
16541         var html = [];
16542         var records = this.store.getRange();
16543         if(records.length < 1) {
16544             
16545             // is this valid??  = should it render a template??
16546             
16547             this.el.update(this.emptyText);
16548             return;
16549         }
16550         var el = this.el;
16551         if (this.dataName) {
16552             this.el.update(t.apply(this.store.meta)); //????
16553             el = this.el.child('.roo-tpl-' + this.dataName);
16554         }
16555         
16556         for(var i = 0, len = records.length; i < len; i++){
16557             var data = this.prepareData(records[i].data, i, records[i]);
16558             this.fireEvent("preparedata", this, data, i, records[i]);
16559             
16560             var d = Roo.apply({}, data);
16561             
16562             if(this.tickable){
16563                 Roo.apply(d, {'roo-id' : Roo.id()});
16564                 
16565                 var _this = this;
16566             
16567                 Roo.each(this.parent.item, function(item){
16568                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16569                         return;
16570                     }
16571                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16572                 });
16573             }
16574             
16575             html[html.length] = Roo.util.Format.trim(
16576                 this.dataName ?
16577                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16578                     t.apply(d)
16579             );
16580         }
16581         
16582         
16583         
16584         el.update(html.join(""));
16585         this.nodes = el.dom.childNodes;
16586         this.updateIndexes(0);
16587     },
16588     
16589
16590     /**
16591      * Function to override to reformat the data that is sent to
16592      * the template for each node.
16593      * DEPRICATED - use the preparedata event handler.
16594      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16595      * a JSON object for an UpdateManager bound view).
16596      */
16597     prepareData : function(data, index, record)
16598     {
16599         this.fireEvent("preparedata", this, data, index, record);
16600         return data;
16601     },
16602
16603     onUpdate : function(ds, record){
16604         // Roo.log('on update');   
16605         this.clearSelections();
16606         var index = this.store.indexOf(record);
16607         var n = this.nodes[index];
16608         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16609         n.parentNode.removeChild(n);
16610         this.updateIndexes(index, index);
16611     },
16612
16613     
16614     
16615 // --------- FIXME     
16616     onAdd : function(ds, records, index)
16617     {
16618         //Roo.log(['on Add', ds, records, index] );        
16619         this.clearSelections();
16620         if(this.nodes.length == 0){
16621             this.refresh();
16622             return;
16623         }
16624         var n = this.nodes[index];
16625         for(var i = 0, len = records.length; i < len; i++){
16626             var d = this.prepareData(records[i].data, i, records[i]);
16627             if(n){
16628                 this.tpl.insertBefore(n, d);
16629             }else{
16630                 
16631                 this.tpl.append(this.el, d);
16632             }
16633         }
16634         this.updateIndexes(index);
16635     },
16636
16637     onRemove : function(ds, record, index){
16638        // Roo.log('onRemove');
16639         this.clearSelections();
16640         var el = this.dataName  ?
16641             this.el.child('.roo-tpl-' + this.dataName) :
16642             this.el; 
16643         
16644         el.dom.removeChild(this.nodes[index]);
16645         this.updateIndexes(index);
16646     },
16647
16648     /**
16649      * Refresh an individual node.
16650      * @param {Number} index
16651      */
16652     refreshNode : function(index){
16653         this.onUpdate(this.store, this.store.getAt(index));
16654     },
16655
16656     updateIndexes : function(startIndex, endIndex){
16657         var ns = this.nodes;
16658         startIndex = startIndex || 0;
16659         endIndex = endIndex || ns.length - 1;
16660         for(var i = startIndex; i <= endIndex; i++){
16661             ns[i].nodeIndex = i;
16662         }
16663     },
16664
16665     /**
16666      * Changes the data store this view uses and refresh the view.
16667      * @param {Store} store
16668      */
16669     setStore : function(store, initial){
16670         if(!initial && this.store){
16671             this.store.un("datachanged", this.refresh);
16672             this.store.un("add", this.onAdd);
16673             this.store.un("remove", this.onRemove);
16674             this.store.un("update", this.onUpdate);
16675             this.store.un("clear", this.refresh);
16676             this.store.un("beforeload", this.onBeforeLoad);
16677             this.store.un("load", this.onLoad);
16678             this.store.un("loadexception", this.onLoad);
16679         }
16680         if(store){
16681           
16682             store.on("datachanged", this.refresh, this);
16683             store.on("add", this.onAdd, this);
16684             store.on("remove", this.onRemove, this);
16685             store.on("update", this.onUpdate, this);
16686             store.on("clear", this.refresh, this);
16687             store.on("beforeload", this.onBeforeLoad, this);
16688             store.on("load", this.onLoad, this);
16689             store.on("loadexception", this.onLoad, this);
16690         }
16691         
16692         if(store){
16693             this.refresh();
16694         }
16695     },
16696     /**
16697      * onbeforeLoad - masks the loading area.
16698      *
16699      */
16700     onBeforeLoad : function(store,opts)
16701     {
16702          //Roo.log('onBeforeLoad');   
16703         if (!opts.add) {
16704             this.el.update("");
16705         }
16706         this.el.mask(this.mask ? this.mask : "Loading" ); 
16707     },
16708     onLoad : function ()
16709     {
16710         this.el.unmask();
16711     },
16712     
16713
16714     /**
16715      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16716      * @param {HTMLElement} node
16717      * @return {HTMLElement} The template node
16718      */
16719     findItemFromChild : function(node){
16720         var el = this.dataName  ?
16721             this.el.child('.roo-tpl-' + this.dataName,true) :
16722             this.el.dom; 
16723         
16724         if(!node || node.parentNode == el){
16725                     return node;
16726             }
16727             var p = node.parentNode;
16728             while(p && p != el){
16729             if(p.parentNode == el){
16730                 return p;
16731             }
16732             p = p.parentNode;
16733         }
16734             return null;
16735     },
16736
16737     /** @ignore */
16738     onClick : function(e){
16739         var item = this.findItemFromChild(e.getTarget());
16740         if(item){
16741             var index = this.indexOf(item);
16742             if(this.onItemClick(item, index, e) !== false){
16743                 this.fireEvent("click", this, index, item, e);
16744             }
16745         }else{
16746             this.clearSelections();
16747         }
16748     },
16749
16750     /** @ignore */
16751     onContextMenu : function(e){
16752         var item = this.findItemFromChild(e.getTarget());
16753         if(item){
16754             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16755         }
16756     },
16757
16758     /** @ignore */
16759     onDblClick : function(e){
16760         var item = this.findItemFromChild(e.getTarget());
16761         if(item){
16762             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16763         }
16764     },
16765
16766     onItemClick : function(item, index, e)
16767     {
16768         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16769             return false;
16770         }
16771         if (this.toggleSelect) {
16772             var m = this.isSelected(item) ? 'unselect' : 'select';
16773             //Roo.log(m);
16774             var _t = this;
16775             _t[m](item, true, false);
16776             return true;
16777         }
16778         if(this.multiSelect || this.singleSelect){
16779             if(this.multiSelect && e.shiftKey && this.lastSelection){
16780                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16781             }else{
16782                 this.select(item, this.multiSelect && e.ctrlKey);
16783                 this.lastSelection = item;
16784             }
16785             
16786             if(!this.tickable){
16787                 e.preventDefault();
16788             }
16789             
16790         }
16791         return true;
16792     },
16793
16794     /**
16795      * Get the number of selected nodes.
16796      * @return {Number}
16797      */
16798     getSelectionCount : function(){
16799         return this.selections.length;
16800     },
16801
16802     /**
16803      * Get the currently selected nodes.
16804      * @return {Array} An array of HTMLElements
16805      */
16806     getSelectedNodes : function(){
16807         return this.selections;
16808     },
16809
16810     /**
16811      * Get the indexes of the selected nodes.
16812      * @return {Array}
16813      */
16814     getSelectedIndexes : function(){
16815         var indexes = [], s = this.selections;
16816         for(var i = 0, len = s.length; i < len; i++){
16817             indexes.push(s[i].nodeIndex);
16818         }
16819         return indexes;
16820     },
16821
16822     /**
16823      * Clear all selections
16824      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16825      */
16826     clearSelections : function(suppressEvent){
16827         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16828             this.cmp.elements = this.selections;
16829             this.cmp.removeClass(this.selectedClass);
16830             this.selections = [];
16831             if(!suppressEvent){
16832                 this.fireEvent("selectionchange", this, this.selections);
16833             }
16834         }
16835     },
16836
16837     /**
16838      * Returns true if the passed node is selected
16839      * @param {HTMLElement/Number} node The node or node index
16840      * @return {Boolean}
16841      */
16842     isSelected : function(node){
16843         var s = this.selections;
16844         if(s.length < 1){
16845             return false;
16846         }
16847         node = this.getNode(node);
16848         return s.indexOf(node) !== -1;
16849     },
16850
16851     /**
16852      * Selects nodes.
16853      * @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
16854      * @param {Boolean} keepExisting (optional) true to keep existing selections
16855      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16856      */
16857     select : function(nodeInfo, keepExisting, suppressEvent){
16858         if(nodeInfo instanceof Array){
16859             if(!keepExisting){
16860                 this.clearSelections(true);
16861             }
16862             for(var i = 0, len = nodeInfo.length; i < len; i++){
16863                 this.select(nodeInfo[i], true, true);
16864             }
16865             return;
16866         } 
16867         var node = this.getNode(nodeInfo);
16868         if(!node || this.isSelected(node)){
16869             return; // already selected.
16870         }
16871         if(!keepExisting){
16872             this.clearSelections(true);
16873         }
16874         
16875         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16876             Roo.fly(node).addClass(this.selectedClass);
16877             this.selections.push(node);
16878             if(!suppressEvent){
16879                 this.fireEvent("selectionchange", this, this.selections);
16880             }
16881         }
16882         
16883         
16884     },
16885       /**
16886      * Unselects nodes.
16887      * @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
16888      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16889      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16890      */
16891     unselect : function(nodeInfo, keepExisting, suppressEvent)
16892     {
16893         if(nodeInfo instanceof Array){
16894             Roo.each(this.selections, function(s) {
16895                 this.unselect(s, nodeInfo);
16896             }, this);
16897             return;
16898         }
16899         var node = this.getNode(nodeInfo);
16900         if(!node || !this.isSelected(node)){
16901             //Roo.log("not selected");
16902             return; // not selected.
16903         }
16904         // fireevent???
16905         var ns = [];
16906         Roo.each(this.selections, function(s) {
16907             if (s == node ) {
16908                 Roo.fly(node).removeClass(this.selectedClass);
16909
16910                 return;
16911             }
16912             ns.push(s);
16913         },this);
16914         
16915         this.selections= ns;
16916         this.fireEvent("selectionchange", this, this.selections);
16917     },
16918
16919     /**
16920      * Gets a template node.
16921      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16922      * @return {HTMLElement} The node or null if it wasn't found
16923      */
16924     getNode : function(nodeInfo){
16925         if(typeof nodeInfo == "string"){
16926             return document.getElementById(nodeInfo);
16927         }else if(typeof nodeInfo == "number"){
16928             return this.nodes[nodeInfo];
16929         }
16930         return nodeInfo;
16931     },
16932
16933     /**
16934      * Gets a range template nodes.
16935      * @param {Number} startIndex
16936      * @param {Number} endIndex
16937      * @return {Array} An array of nodes
16938      */
16939     getNodes : function(start, end){
16940         var ns = this.nodes;
16941         start = start || 0;
16942         end = typeof end == "undefined" ? ns.length - 1 : end;
16943         var nodes = [];
16944         if(start <= end){
16945             for(var i = start; i <= end; i++){
16946                 nodes.push(ns[i]);
16947             }
16948         } else{
16949             for(var i = start; i >= end; i--){
16950                 nodes.push(ns[i]);
16951             }
16952         }
16953         return nodes;
16954     },
16955
16956     /**
16957      * Finds the index of the passed node
16958      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16959      * @return {Number} The index of the node or -1
16960      */
16961     indexOf : function(node){
16962         node = this.getNode(node);
16963         if(typeof node.nodeIndex == "number"){
16964             return node.nodeIndex;
16965         }
16966         var ns = this.nodes;
16967         for(var i = 0, len = ns.length; i < len; i++){
16968             if(ns[i] == node){
16969                 return i;
16970             }
16971         }
16972         return -1;
16973     }
16974 });
16975 /*
16976  * - LGPL
16977  *
16978  * based on jquery fullcalendar
16979  * 
16980  */
16981
16982 Roo.bootstrap = Roo.bootstrap || {};
16983 /**
16984  * @class Roo.bootstrap.Calendar
16985  * @extends Roo.bootstrap.Component
16986  * Bootstrap Calendar class
16987  * @cfg {Boolean} loadMask (true|false) default false
16988  * @cfg {Object} header generate the user specific header of the calendar, default false
16989
16990  * @constructor
16991  * Create a new Container
16992  * @param {Object} config The config object
16993  */
16994
16995
16996
16997 Roo.bootstrap.Calendar = function(config){
16998     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16999      this.addEvents({
17000         /**
17001              * @event select
17002              * Fires when a date is selected
17003              * @param {DatePicker} this
17004              * @param {Date} date The selected date
17005              */
17006         'select': true,
17007         /**
17008              * @event monthchange
17009              * Fires when the displayed month changes 
17010              * @param {DatePicker} this
17011              * @param {Date} date The selected month
17012              */
17013         'monthchange': true,
17014         /**
17015              * @event evententer
17016              * Fires when mouse over an event
17017              * @param {Calendar} this
17018              * @param {event} Event
17019              */
17020         'evententer': true,
17021         /**
17022              * @event eventleave
17023              * Fires when the mouse leaves an
17024              * @param {Calendar} this
17025              * @param {event}
17026              */
17027         'eventleave': true,
17028         /**
17029              * @event eventclick
17030              * Fires when the mouse click an
17031              * @param {Calendar} this
17032              * @param {event}
17033              */
17034         'eventclick': true
17035         
17036     });
17037
17038 };
17039
17040 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17041     
17042      /**
17043      * @cfg {Number} startDay
17044      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17045      */
17046     startDay : 0,
17047     
17048     loadMask : false,
17049     
17050     header : false,
17051       
17052     getAutoCreate : function(){
17053         
17054         
17055         var fc_button = function(name, corner, style, content ) {
17056             return Roo.apply({},{
17057                 tag : 'span',
17058                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17059                          (corner.length ?
17060                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17061                             ''
17062                         ),
17063                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17064                 unselectable: 'on'
17065             });
17066         };
17067         
17068         var header = {};
17069         
17070         if(!this.header){
17071             header = {
17072                 tag : 'table',
17073                 cls : 'fc-header',
17074                 style : 'width:100%',
17075                 cn : [
17076                     {
17077                         tag: 'tr',
17078                         cn : [
17079                             {
17080                                 tag : 'td',
17081                                 cls : 'fc-header-left',
17082                                 cn : [
17083                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17084                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17085                                     { tag: 'span', cls: 'fc-header-space' },
17086                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17087
17088
17089                                 ]
17090                             },
17091
17092                             {
17093                                 tag : 'td',
17094                                 cls : 'fc-header-center',
17095                                 cn : [
17096                                     {
17097                                         tag: 'span',
17098                                         cls: 'fc-header-title',
17099                                         cn : {
17100                                             tag: 'H2',
17101                                             html : 'month / year'
17102                                         }
17103                                     }
17104
17105                                 ]
17106                             },
17107                             {
17108                                 tag : 'td',
17109                                 cls : 'fc-header-right',
17110                                 cn : [
17111                               /*      fc_button('month', 'left', '', 'month' ),
17112                                     fc_button('week', '', '', 'week' ),
17113                                     fc_button('day', 'right', '', 'day' )
17114                                 */    
17115
17116                                 ]
17117                             }
17118
17119                         ]
17120                     }
17121                 ]
17122             };
17123         }
17124         
17125         header = this.header;
17126         
17127        
17128         var cal_heads = function() {
17129             var ret = [];
17130             // fixme - handle this.
17131             
17132             for (var i =0; i < Date.dayNames.length; i++) {
17133                 var d = Date.dayNames[i];
17134                 ret.push({
17135                     tag: 'th',
17136                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17137                     html : d.substring(0,3)
17138                 });
17139                 
17140             }
17141             ret[0].cls += ' fc-first';
17142             ret[6].cls += ' fc-last';
17143             return ret;
17144         };
17145         var cal_cell = function(n) {
17146             return  {
17147                 tag: 'td',
17148                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17149                 cn : [
17150                     {
17151                         cn : [
17152                             {
17153                                 cls: 'fc-day-number',
17154                                 html: 'D'
17155                             },
17156                             {
17157                                 cls: 'fc-day-content',
17158                              
17159                                 cn : [
17160                                      {
17161                                         style: 'position: relative;' // height: 17px;
17162                                     }
17163                                 ]
17164                             }
17165                             
17166                             
17167                         ]
17168                     }
17169                 ]
17170                 
17171             }
17172         };
17173         var cal_rows = function() {
17174             
17175             var ret = [];
17176             for (var r = 0; r < 6; r++) {
17177                 var row= {
17178                     tag : 'tr',
17179                     cls : 'fc-week',
17180                     cn : []
17181                 };
17182                 
17183                 for (var i =0; i < Date.dayNames.length; i++) {
17184                     var d = Date.dayNames[i];
17185                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17186
17187                 }
17188                 row.cn[0].cls+=' fc-first';
17189                 row.cn[0].cn[0].style = 'min-height:90px';
17190                 row.cn[6].cls+=' fc-last';
17191                 ret.push(row);
17192                 
17193             }
17194             ret[0].cls += ' fc-first';
17195             ret[4].cls += ' fc-prev-last';
17196             ret[5].cls += ' fc-last';
17197             return ret;
17198             
17199         };
17200         
17201         var cal_table = {
17202             tag: 'table',
17203             cls: 'fc-border-separate',
17204             style : 'width:100%',
17205             cellspacing  : 0,
17206             cn : [
17207                 { 
17208                     tag: 'thead',
17209                     cn : [
17210                         { 
17211                             tag: 'tr',
17212                             cls : 'fc-first fc-last',
17213                             cn : cal_heads()
17214                         }
17215                     ]
17216                 },
17217                 { 
17218                     tag: 'tbody',
17219                     cn : cal_rows()
17220                 }
17221                   
17222             ]
17223         };
17224          
17225          var cfg = {
17226             cls : 'fc fc-ltr',
17227             cn : [
17228                 header,
17229                 {
17230                     cls : 'fc-content',
17231                     style : "position: relative;",
17232                     cn : [
17233                         {
17234                             cls : 'fc-view fc-view-month fc-grid',
17235                             style : 'position: relative',
17236                             unselectable : 'on',
17237                             cn : [
17238                                 {
17239                                     cls : 'fc-event-container',
17240                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17241                                 },
17242                                 cal_table
17243                             ]
17244                         }
17245                     ]
17246     
17247                 }
17248            ] 
17249             
17250         };
17251         
17252          
17253         
17254         return cfg;
17255     },
17256     
17257     
17258     initEvents : function()
17259     {
17260         if(!this.store){
17261             throw "can not find store for calendar";
17262         }
17263         
17264         var mark = {
17265             tag: "div",
17266             cls:"x-dlg-mask",
17267             style: "text-align:center",
17268             cn: [
17269                 {
17270                     tag: "div",
17271                     style: "background-color:white;width:50%;margin:250 auto",
17272                     cn: [
17273                         {
17274                             tag: "img",
17275                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17276                         },
17277                         {
17278                             tag: "span",
17279                             html: "Loading"
17280                         }
17281                         
17282                     ]
17283                 }
17284             ]
17285         };
17286         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17287         
17288         var size = this.el.select('.fc-content', true).first().getSize();
17289         this.maskEl.setSize(size.width, size.height);
17290         this.maskEl.enableDisplayMode("block");
17291         if(!this.loadMask){
17292             this.maskEl.hide();
17293         }
17294         
17295         this.store = Roo.factory(this.store, Roo.data);
17296         this.store.on('load', this.onLoad, this);
17297         this.store.on('beforeload', this.onBeforeLoad, this);
17298         
17299         this.resize();
17300         
17301         this.cells = this.el.select('.fc-day',true);
17302         //Roo.log(this.cells);
17303         this.textNodes = this.el.query('.fc-day-number');
17304         this.cells.addClassOnOver('fc-state-hover');
17305         
17306         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17307         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17308         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17309         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17310         
17311         this.on('monthchange', this.onMonthChange, this);
17312         
17313         this.update(new Date().clearTime());
17314     },
17315     
17316     resize : function() {
17317         var sz  = this.el.getSize();
17318         
17319         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17320         this.el.select('.fc-day-content div',true).setHeight(34);
17321     },
17322     
17323     
17324     // private
17325     showPrevMonth : function(e){
17326         this.update(this.activeDate.add("mo", -1));
17327     },
17328     showToday : function(e){
17329         this.update(new Date().clearTime());
17330     },
17331     // private
17332     showNextMonth : function(e){
17333         this.update(this.activeDate.add("mo", 1));
17334     },
17335
17336     // private
17337     showPrevYear : function(){
17338         this.update(this.activeDate.add("y", -1));
17339     },
17340
17341     // private
17342     showNextYear : function(){
17343         this.update(this.activeDate.add("y", 1));
17344     },
17345
17346     
17347    // private
17348     update : function(date)
17349     {
17350         var vd = this.activeDate;
17351         this.activeDate = date;
17352 //        if(vd && this.el){
17353 //            var t = date.getTime();
17354 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17355 //                Roo.log('using add remove');
17356 //                
17357 //                this.fireEvent('monthchange', this, date);
17358 //                
17359 //                this.cells.removeClass("fc-state-highlight");
17360 //                this.cells.each(function(c){
17361 //                   if(c.dateValue == t){
17362 //                       c.addClass("fc-state-highlight");
17363 //                       setTimeout(function(){
17364 //                            try{c.dom.firstChild.focus();}catch(e){}
17365 //                       }, 50);
17366 //                       return false;
17367 //                   }
17368 //                   return true;
17369 //                });
17370 //                return;
17371 //            }
17372 //        }
17373         
17374         var days = date.getDaysInMonth();
17375         
17376         var firstOfMonth = date.getFirstDateOfMonth();
17377         var startingPos = firstOfMonth.getDay()-this.startDay;
17378         
17379         if(startingPos < this.startDay){
17380             startingPos += 7;
17381         }
17382         
17383         var pm = date.add(Date.MONTH, -1);
17384         var prevStart = pm.getDaysInMonth()-startingPos;
17385 //        
17386         this.cells = this.el.select('.fc-day',true);
17387         this.textNodes = this.el.query('.fc-day-number');
17388         this.cells.addClassOnOver('fc-state-hover');
17389         
17390         var cells = this.cells.elements;
17391         var textEls = this.textNodes;
17392         
17393         Roo.each(cells, function(cell){
17394             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17395         });
17396         
17397         days += startingPos;
17398
17399         // convert everything to numbers so it's fast
17400         var day = 86400000;
17401         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17402         //Roo.log(d);
17403         //Roo.log(pm);
17404         //Roo.log(prevStart);
17405         
17406         var today = new Date().clearTime().getTime();
17407         var sel = date.clearTime().getTime();
17408         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17409         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17410         var ddMatch = this.disabledDatesRE;
17411         var ddText = this.disabledDatesText;
17412         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17413         var ddaysText = this.disabledDaysText;
17414         var format = this.format;
17415         
17416         var setCellClass = function(cal, cell){
17417             cell.row = 0;
17418             cell.events = [];
17419             cell.more = [];
17420             //Roo.log('set Cell Class');
17421             cell.title = "";
17422             var t = d.getTime();
17423             
17424             //Roo.log(d);
17425             
17426             cell.dateValue = t;
17427             if(t == today){
17428                 cell.className += " fc-today";
17429                 cell.className += " fc-state-highlight";
17430                 cell.title = cal.todayText;
17431             }
17432             if(t == sel){
17433                 // disable highlight in other month..
17434                 //cell.className += " fc-state-highlight";
17435                 
17436             }
17437             // disabling
17438             if(t < min) {
17439                 cell.className = " fc-state-disabled";
17440                 cell.title = cal.minText;
17441                 return;
17442             }
17443             if(t > max) {
17444                 cell.className = " fc-state-disabled";
17445                 cell.title = cal.maxText;
17446                 return;
17447             }
17448             if(ddays){
17449                 if(ddays.indexOf(d.getDay()) != -1){
17450                     cell.title = ddaysText;
17451                     cell.className = " fc-state-disabled";
17452                 }
17453             }
17454             if(ddMatch && format){
17455                 var fvalue = d.dateFormat(format);
17456                 if(ddMatch.test(fvalue)){
17457                     cell.title = ddText.replace("%0", fvalue);
17458                     cell.className = " fc-state-disabled";
17459                 }
17460             }
17461             
17462             if (!cell.initialClassName) {
17463                 cell.initialClassName = cell.dom.className;
17464             }
17465             
17466             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17467         };
17468
17469         var i = 0;
17470         
17471         for(; i < startingPos; i++) {
17472             textEls[i].innerHTML = (++prevStart);
17473             d.setDate(d.getDate()+1);
17474             
17475             cells[i].className = "fc-past fc-other-month";
17476             setCellClass(this, cells[i]);
17477         }
17478         
17479         var intDay = 0;
17480         
17481         for(; i < days; i++){
17482             intDay = i - startingPos + 1;
17483             textEls[i].innerHTML = (intDay);
17484             d.setDate(d.getDate()+1);
17485             
17486             cells[i].className = ''; // "x-date-active";
17487             setCellClass(this, cells[i]);
17488         }
17489         var extraDays = 0;
17490         
17491         for(; i < 42; i++) {
17492             textEls[i].innerHTML = (++extraDays);
17493             d.setDate(d.getDate()+1);
17494             
17495             cells[i].className = "fc-future fc-other-month";
17496             setCellClass(this, cells[i]);
17497         }
17498         
17499         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17500         
17501         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17502         
17503         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17504         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17505         
17506         if(totalRows != 6){
17507             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17508             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17509         }
17510         
17511         this.fireEvent('monthchange', this, date);
17512         
17513         
17514         /*
17515         if(!this.internalRender){
17516             var main = this.el.dom.firstChild;
17517             var w = main.offsetWidth;
17518             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17519             Roo.fly(main).setWidth(w);
17520             this.internalRender = true;
17521             // opera does not respect the auto grow header center column
17522             // then, after it gets a width opera refuses to recalculate
17523             // without a second pass
17524             if(Roo.isOpera && !this.secondPass){
17525                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17526                 this.secondPass = true;
17527                 this.update.defer(10, this, [date]);
17528             }
17529         }
17530         */
17531         
17532     },
17533     
17534     findCell : function(dt) {
17535         dt = dt.clearTime().getTime();
17536         var ret = false;
17537         this.cells.each(function(c){
17538             //Roo.log("check " +c.dateValue + '?=' + dt);
17539             if(c.dateValue == dt){
17540                 ret = c;
17541                 return false;
17542             }
17543             return true;
17544         });
17545         
17546         return ret;
17547     },
17548     
17549     findCells : function(ev) {
17550         var s = ev.start.clone().clearTime().getTime();
17551        // Roo.log(s);
17552         var e= ev.end.clone().clearTime().getTime();
17553        // Roo.log(e);
17554         var ret = [];
17555         this.cells.each(function(c){
17556              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17557             
17558             if(c.dateValue > e){
17559                 return ;
17560             }
17561             if(c.dateValue < s){
17562                 return ;
17563             }
17564             ret.push(c);
17565         });
17566         
17567         return ret;    
17568     },
17569     
17570 //    findBestRow: function(cells)
17571 //    {
17572 //        var ret = 0;
17573 //        
17574 //        for (var i =0 ; i < cells.length;i++) {
17575 //            ret  = Math.max(cells[i].rows || 0,ret);
17576 //        }
17577 //        return ret;
17578 //        
17579 //    },
17580     
17581     
17582     addItem : function(ev)
17583     {
17584         // look for vertical location slot in
17585         var cells = this.findCells(ev);
17586         
17587 //        ev.row = this.findBestRow(cells);
17588         
17589         // work out the location.
17590         
17591         var crow = false;
17592         var rows = [];
17593         for(var i =0; i < cells.length; i++) {
17594             
17595             cells[i].row = cells[0].row;
17596             
17597             if(i == 0){
17598                 cells[i].row = cells[i].row + 1;
17599             }
17600             
17601             if (!crow) {
17602                 crow = {
17603                     start : cells[i],
17604                     end :  cells[i]
17605                 };
17606                 continue;
17607             }
17608             if (crow.start.getY() == cells[i].getY()) {
17609                 // on same row.
17610                 crow.end = cells[i];
17611                 continue;
17612             }
17613             // different row.
17614             rows.push(crow);
17615             crow = {
17616                 start: cells[i],
17617                 end : cells[i]
17618             };
17619             
17620         }
17621         
17622         rows.push(crow);
17623         ev.els = [];
17624         ev.rows = rows;
17625         ev.cells = cells;
17626         
17627         cells[0].events.push(ev);
17628         
17629         this.calevents.push(ev);
17630     },
17631     
17632     clearEvents: function() {
17633         
17634         if(!this.calevents){
17635             return;
17636         }
17637         
17638         Roo.each(this.cells.elements, function(c){
17639             c.row = 0;
17640             c.events = [];
17641             c.more = [];
17642         });
17643         
17644         Roo.each(this.calevents, function(e) {
17645             Roo.each(e.els, function(el) {
17646                 el.un('mouseenter' ,this.onEventEnter, this);
17647                 el.un('mouseleave' ,this.onEventLeave, this);
17648                 el.remove();
17649             },this);
17650         },this);
17651         
17652         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17653             e.remove();
17654         });
17655         
17656     },
17657     
17658     renderEvents: function()
17659     {   
17660         var _this = this;
17661         
17662         this.cells.each(function(c) {
17663             
17664             if(c.row < 5){
17665                 return;
17666             }
17667             
17668             var ev = c.events;
17669             
17670             var r = 4;
17671             if(c.row != c.events.length){
17672                 r = 4 - (4 - (c.row - c.events.length));
17673             }
17674             
17675             c.events = ev.slice(0, r);
17676             c.more = ev.slice(r);
17677             
17678             if(c.more.length && c.more.length == 1){
17679                 c.events.push(c.more.pop());
17680             }
17681             
17682             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17683             
17684         });
17685             
17686         this.cells.each(function(c) {
17687             
17688             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17689             
17690             
17691             for (var e = 0; e < c.events.length; e++){
17692                 var ev = c.events[e];
17693                 var rows = ev.rows;
17694                 
17695                 for(var i = 0; i < rows.length; i++) {
17696                 
17697                     // how many rows should it span..
17698
17699                     var  cfg = {
17700                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17701                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17702
17703                         unselectable : "on",
17704                         cn : [
17705                             {
17706                                 cls: 'fc-event-inner',
17707                                 cn : [
17708     //                                {
17709     //                                  tag:'span',
17710     //                                  cls: 'fc-event-time',
17711     //                                  html : cells.length > 1 ? '' : ev.time
17712     //                                },
17713                                     {
17714                                       tag:'span',
17715                                       cls: 'fc-event-title',
17716                                       html : String.format('{0}', ev.title)
17717                                     }
17718
17719
17720                                 ]
17721                             },
17722                             {
17723                                 cls: 'ui-resizable-handle ui-resizable-e',
17724                                 html : '&nbsp;&nbsp;&nbsp'
17725                             }
17726
17727                         ]
17728                     };
17729
17730                     if (i == 0) {
17731                         cfg.cls += ' fc-event-start';
17732                     }
17733                     if ((i+1) == rows.length) {
17734                         cfg.cls += ' fc-event-end';
17735                     }
17736
17737                     var ctr = _this.el.select('.fc-event-container',true).first();
17738                     var cg = ctr.createChild(cfg);
17739
17740                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17741                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17742
17743                     var r = (c.more.length) ? 1 : 0;
17744                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17745                     cg.setWidth(ebox.right - sbox.x -2);
17746
17747                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17748                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17749                     cg.on('click', _this.onEventClick, _this, ev);
17750
17751                     ev.els.push(cg);
17752                     
17753                 }
17754                 
17755             }
17756             
17757             
17758             if(c.more.length){
17759                 var  cfg = {
17760                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17761                     style : 'position: absolute',
17762                     unselectable : "on",
17763                     cn : [
17764                         {
17765                             cls: 'fc-event-inner',
17766                             cn : [
17767                                 {
17768                                   tag:'span',
17769                                   cls: 'fc-event-title',
17770                                   html : 'More'
17771                                 }
17772
17773
17774                             ]
17775                         },
17776                         {
17777                             cls: 'ui-resizable-handle ui-resizable-e',
17778                             html : '&nbsp;&nbsp;&nbsp'
17779                         }
17780
17781                     ]
17782                 };
17783
17784                 var ctr = _this.el.select('.fc-event-container',true).first();
17785                 var cg = ctr.createChild(cfg);
17786
17787                 var sbox = c.select('.fc-day-content',true).first().getBox();
17788                 var ebox = c.select('.fc-day-content',true).first().getBox();
17789                 //Roo.log(cg);
17790                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17791                 cg.setWidth(ebox.right - sbox.x -2);
17792
17793                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17794                 
17795             }
17796             
17797         });
17798         
17799         
17800         
17801     },
17802     
17803     onEventEnter: function (e, el,event,d) {
17804         this.fireEvent('evententer', this, el, event);
17805     },
17806     
17807     onEventLeave: function (e, el,event,d) {
17808         this.fireEvent('eventleave', this, el, event);
17809     },
17810     
17811     onEventClick: function (e, el,event,d) {
17812         this.fireEvent('eventclick', this, el, event);
17813     },
17814     
17815     onMonthChange: function () {
17816         this.store.load();
17817     },
17818     
17819     onMoreEventClick: function(e, el, more)
17820     {
17821         var _this = this;
17822         
17823         this.calpopover.placement = 'right';
17824         this.calpopover.setTitle('More');
17825         
17826         this.calpopover.setContent('');
17827         
17828         var ctr = this.calpopover.el.select('.popover-content', true).first();
17829         
17830         Roo.each(more, function(m){
17831             var cfg = {
17832                 cls : 'fc-event-hori fc-event-draggable',
17833                 html : m.title
17834             };
17835             var cg = ctr.createChild(cfg);
17836             
17837             cg.on('click', _this.onEventClick, _this, m);
17838         });
17839         
17840         this.calpopover.show(el);
17841         
17842         
17843     },
17844     
17845     onLoad: function () 
17846     {   
17847         this.calevents = [];
17848         var cal = this;
17849         
17850         if(this.store.getCount() > 0){
17851             this.store.data.each(function(d){
17852                cal.addItem({
17853                     id : d.data.id,
17854                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17855                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17856                     time : d.data.start_time,
17857                     title : d.data.title,
17858                     description : d.data.description,
17859                     venue : d.data.venue
17860                 });
17861             });
17862         }
17863         
17864         this.renderEvents();
17865         
17866         if(this.calevents.length && this.loadMask){
17867             this.maskEl.hide();
17868         }
17869     },
17870     
17871     onBeforeLoad: function()
17872     {
17873         this.clearEvents();
17874         if(this.loadMask){
17875             this.maskEl.show();
17876         }
17877     }
17878 });
17879
17880  
17881  /*
17882  * - LGPL
17883  *
17884  * element
17885  * 
17886  */
17887
17888 /**
17889  * @class Roo.bootstrap.Popover
17890  * @extends Roo.bootstrap.Component
17891  * Bootstrap Popover class
17892  * @cfg {String} html contents of the popover   (or false to use children..)
17893  * @cfg {String} title of popover (or false to hide)
17894  * @cfg {String} placement how it is placed
17895  * @cfg {String} trigger click || hover (or false to trigger manually)
17896  * @cfg {String} over what (parent or false to trigger manually.)
17897  * @cfg {Number} delay - delay before showing
17898  
17899  * @constructor
17900  * Create a new Popover
17901  * @param {Object} config The config object
17902  */
17903
17904 Roo.bootstrap.Popover = function(config){
17905     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17906     
17907     this.addEvents({
17908         // raw events
17909          /**
17910          * @event show
17911          * After the popover show
17912          * 
17913          * @param {Roo.bootstrap.Popover} this
17914          */
17915         "show" : true,
17916         /**
17917          * @event hide
17918          * After the popover hide
17919          * 
17920          * @param {Roo.bootstrap.Popover} this
17921          */
17922         "hide" : true
17923     });
17924 };
17925
17926 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17927     
17928     title: 'Fill in a title',
17929     html: false,
17930     
17931     placement : 'right',
17932     trigger : 'hover', // hover
17933     
17934     delay : 0,
17935     
17936     over: 'parent',
17937     
17938     can_build_overlaid : false,
17939     
17940     getChildContainer : function()
17941     {
17942         return this.el.select('.popover-content',true).first();
17943     },
17944     
17945     getAutoCreate : function(){
17946          
17947         var cfg = {
17948            cls : 'popover roo-dynamic',
17949            style: 'display:block',
17950            cn : [
17951                 {
17952                     cls : 'arrow'
17953                 },
17954                 {
17955                     cls : 'popover-inner',
17956                     cn : [
17957                         {
17958                             tag: 'h3',
17959                             cls: 'popover-title popover-header',
17960                             html : this.title
17961                         },
17962                         {
17963                             cls : 'popover-content popover-body',
17964                             html : this.html
17965                         }
17966                     ]
17967                     
17968                 }
17969            ]
17970         };
17971         
17972         return cfg;
17973     },
17974     setTitle: function(str)
17975     {
17976         this.title = str;
17977         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17978     },
17979     setContent: function(str)
17980     {
17981         this.html = str;
17982         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17983     },
17984     // as it get's added to the bottom of the page.
17985     onRender : function(ct, position)
17986     {
17987         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17988         if(!this.el){
17989             var cfg = Roo.apply({},  this.getAutoCreate());
17990             cfg.id = Roo.id();
17991             
17992             if (this.cls) {
17993                 cfg.cls += ' ' + this.cls;
17994             }
17995             if (this.style) {
17996                 cfg.style = this.style;
17997             }
17998             //Roo.log("adding to ");
17999             this.el = Roo.get(document.body).createChild(cfg, position);
18000 //            Roo.log(this.el);
18001         }
18002         this.initEvents();
18003     },
18004     
18005     initEvents : function()
18006     {
18007         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18008         this.el.enableDisplayMode('block');
18009         this.el.hide();
18010         if (this.over === false) {
18011             return; 
18012         }
18013         if (this.triggers === false) {
18014             return;
18015         }
18016         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18017         var triggers = this.trigger ? this.trigger.split(' ') : [];
18018         Roo.each(triggers, function(trigger) {
18019         
18020             if (trigger == 'click') {
18021                 on_el.on('click', this.toggle, this);
18022             } else if (trigger != 'manual') {
18023                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18024                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18025       
18026                 on_el.on(eventIn  ,this.enter, this);
18027                 on_el.on(eventOut, this.leave, this);
18028             }
18029         }, this);
18030         
18031     },
18032     
18033     
18034     // private
18035     timeout : null,
18036     hoverState : null,
18037     
18038     toggle : function () {
18039         this.hoverState == 'in' ? this.leave() : this.enter();
18040     },
18041     
18042     enter : function () {
18043         
18044         clearTimeout(this.timeout);
18045     
18046         this.hoverState = 'in';
18047     
18048         if (!this.delay || !this.delay.show) {
18049             this.show();
18050             return;
18051         }
18052         var _t = this;
18053         this.timeout = setTimeout(function () {
18054             if (_t.hoverState == 'in') {
18055                 _t.show();
18056             }
18057         }, this.delay.show)
18058     },
18059     
18060     leave : function() {
18061         clearTimeout(this.timeout);
18062     
18063         this.hoverState = 'out';
18064     
18065         if (!this.delay || !this.delay.hide) {
18066             this.hide();
18067             return;
18068         }
18069         var _t = this;
18070         this.timeout = setTimeout(function () {
18071             if (_t.hoverState == 'out') {
18072                 _t.hide();
18073             }
18074         }, this.delay.hide)
18075     },
18076     
18077     show : function (on_el)
18078     {
18079         if (!on_el) {
18080             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18081         }
18082         
18083         // set content.
18084         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18085         if (this.html !== false) {
18086             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18087         }
18088         this.el.removeClass([
18089             'fade','top','bottom', 'left', 'right','in',
18090             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18091         ]);
18092         if (!this.title.length) {
18093             this.el.select('.popover-title',true).hide();
18094         }
18095         
18096         var placement = typeof this.placement == 'function' ?
18097             this.placement.call(this, this.el, on_el) :
18098             this.placement;
18099             
18100         var autoToken = /\s?auto?\s?/i;
18101         var autoPlace = autoToken.test(placement);
18102         if (autoPlace) {
18103             placement = placement.replace(autoToken, '') || 'top';
18104         }
18105         
18106         //this.el.detach()
18107         //this.el.setXY([0,0]);
18108         this.el.show();
18109         this.el.dom.style.display='block';
18110         this.el.addClass(placement);
18111         
18112         //this.el.appendTo(on_el);
18113         
18114         var p = this.getPosition();
18115         var box = this.el.getBox();
18116         
18117         if (autoPlace) {
18118             // fixme..
18119         }
18120         var align = Roo.bootstrap.Popover.alignment[placement];
18121         
18122 //        Roo.log(align);
18123         this.el.alignTo(on_el, align[0],align[1]);
18124         //var arrow = this.el.select('.arrow',true).first();
18125         //arrow.set(align[2], 
18126         
18127         this.el.addClass('in');
18128         
18129         
18130         if (this.el.hasClass('fade')) {
18131             // fade it?
18132         }
18133         
18134         this.hoverState = 'in';
18135         
18136         this.fireEvent('show', this);
18137         
18138     },
18139     hide : function()
18140     {
18141         this.el.setXY([0,0]);
18142         this.el.removeClass('in');
18143         this.el.hide();
18144         this.hoverState = null;
18145         
18146         this.fireEvent('hide', this);
18147     }
18148     
18149 });
18150
18151 Roo.bootstrap.Popover.alignment = {
18152     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18153     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18154     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18155     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18156 };
18157
18158  /*
18159  * - LGPL
18160  *
18161  * Progress
18162  * 
18163  */
18164
18165 /**
18166  * @class Roo.bootstrap.Progress
18167  * @extends Roo.bootstrap.Component
18168  * Bootstrap Progress class
18169  * @cfg {Boolean} striped striped of the progress bar
18170  * @cfg {Boolean} active animated of the progress bar
18171  * 
18172  * 
18173  * @constructor
18174  * Create a new Progress
18175  * @param {Object} config The config object
18176  */
18177
18178 Roo.bootstrap.Progress = function(config){
18179     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18180 };
18181
18182 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18183     
18184     striped : false,
18185     active: false,
18186     
18187     getAutoCreate : function(){
18188         var cfg = {
18189             tag: 'div',
18190             cls: 'progress'
18191         };
18192         
18193         
18194         if(this.striped){
18195             cfg.cls += ' progress-striped';
18196         }
18197       
18198         if(this.active){
18199             cfg.cls += ' active';
18200         }
18201         
18202         
18203         return cfg;
18204     }
18205    
18206 });
18207
18208  
18209
18210  /*
18211  * - LGPL
18212  *
18213  * ProgressBar
18214  * 
18215  */
18216
18217 /**
18218  * @class Roo.bootstrap.ProgressBar
18219  * @extends Roo.bootstrap.Component
18220  * Bootstrap ProgressBar class
18221  * @cfg {Number} aria_valuenow aria-value now
18222  * @cfg {Number} aria_valuemin aria-value min
18223  * @cfg {Number} aria_valuemax aria-value max
18224  * @cfg {String} label label for the progress bar
18225  * @cfg {String} panel (success | info | warning | danger )
18226  * @cfg {String} role role of the progress bar
18227  * @cfg {String} sr_only text
18228  * 
18229  * 
18230  * @constructor
18231  * Create a new ProgressBar
18232  * @param {Object} config The config object
18233  */
18234
18235 Roo.bootstrap.ProgressBar = function(config){
18236     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18237 };
18238
18239 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18240     
18241     aria_valuenow : 0,
18242     aria_valuemin : 0,
18243     aria_valuemax : 100,
18244     label : false,
18245     panel : false,
18246     role : false,
18247     sr_only: false,
18248     
18249     getAutoCreate : function()
18250     {
18251         
18252         var cfg = {
18253             tag: 'div',
18254             cls: 'progress-bar',
18255             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18256         };
18257         
18258         if(this.sr_only){
18259             cfg.cn = {
18260                 tag: 'span',
18261                 cls: 'sr-only',
18262                 html: this.sr_only
18263             }
18264         }
18265         
18266         if(this.role){
18267             cfg.role = this.role;
18268         }
18269         
18270         if(this.aria_valuenow){
18271             cfg['aria-valuenow'] = this.aria_valuenow;
18272         }
18273         
18274         if(this.aria_valuemin){
18275             cfg['aria-valuemin'] = this.aria_valuemin;
18276         }
18277         
18278         if(this.aria_valuemax){
18279             cfg['aria-valuemax'] = this.aria_valuemax;
18280         }
18281         
18282         if(this.label && !this.sr_only){
18283             cfg.html = this.label;
18284         }
18285         
18286         if(this.panel){
18287             cfg.cls += ' progress-bar-' + this.panel;
18288         }
18289         
18290         return cfg;
18291     },
18292     
18293     update : function(aria_valuenow)
18294     {
18295         this.aria_valuenow = aria_valuenow;
18296         
18297         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18298     }
18299    
18300 });
18301
18302  
18303
18304  /*
18305  * - LGPL
18306  *
18307  * column
18308  * 
18309  */
18310
18311 /**
18312  * @class Roo.bootstrap.TabGroup
18313  * @extends Roo.bootstrap.Column
18314  * Bootstrap Column class
18315  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18316  * @cfg {Boolean} carousel true to make the group behave like a carousel
18317  * @cfg {Boolean} bullets show bullets for the panels
18318  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18319  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18320  * @cfg {Boolean} showarrow (true|false) show arrow default true
18321  * 
18322  * @constructor
18323  * Create a new TabGroup
18324  * @param {Object} config The config object
18325  */
18326
18327 Roo.bootstrap.TabGroup = function(config){
18328     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18329     if (!this.navId) {
18330         this.navId = Roo.id();
18331     }
18332     this.tabs = [];
18333     Roo.bootstrap.TabGroup.register(this);
18334     
18335 };
18336
18337 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18338     
18339     carousel : false,
18340     transition : false,
18341     bullets : 0,
18342     timer : 0,
18343     autoslide : false,
18344     slideFn : false,
18345     slideOnTouch : false,
18346     showarrow : true,
18347     
18348     getAutoCreate : function()
18349     {
18350         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18351         
18352         cfg.cls += ' tab-content';
18353         
18354         if (this.carousel) {
18355             cfg.cls += ' carousel slide';
18356             
18357             cfg.cn = [{
18358                cls : 'carousel-inner',
18359                cn : []
18360             }];
18361         
18362             if(this.bullets  && !Roo.isTouch){
18363                 
18364                 var bullets = {
18365                     cls : 'carousel-bullets',
18366                     cn : []
18367                 };
18368                
18369                 if(this.bullets_cls){
18370                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18371                 }
18372                 
18373                 bullets.cn.push({
18374                     cls : 'clear'
18375                 });
18376                 
18377                 cfg.cn[0].cn.push(bullets);
18378             }
18379             
18380             if(this.showarrow){
18381                 cfg.cn[0].cn.push({
18382                     tag : 'div',
18383                     class : 'carousel-arrow',
18384                     cn : [
18385                         {
18386                             tag : 'div',
18387                             class : 'carousel-prev',
18388                             cn : [
18389                                 {
18390                                     tag : 'i',
18391                                     class : 'fa fa-chevron-left'
18392                                 }
18393                             ]
18394                         },
18395                         {
18396                             tag : 'div',
18397                             class : 'carousel-next',
18398                             cn : [
18399                                 {
18400                                     tag : 'i',
18401                                     class : 'fa fa-chevron-right'
18402                                 }
18403                             ]
18404                         }
18405                     ]
18406                 });
18407             }
18408             
18409         }
18410         
18411         return cfg;
18412     },
18413     
18414     initEvents:  function()
18415     {
18416 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18417 //            this.el.on("touchstart", this.onTouchStart, this);
18418 //        }
18419         
18420         if(this.autoslide){
18421             var _this = this;
18422             
18423             this.slideFn = window.setInterval(function() {
18424                 _this.showPanelNext();
18425             }, this.timer);
18426         }
18427         
18428         if(this.showarrow){
18429             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18430             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18431         }
18432         
18433         
18434     },
18435     
18436 //    onTouchStart : function(e, el, o)
18437 //    {
18438 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18439 //            return;
18440 //        }
18441 //        
18442 //        this.showPanelNext();
18443 //    },
18444     
18445     
18446     getChildContainer : function()
18447     {
18448         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18449     },
18450     
18451     /**
18452     * register a Navigation item
18453     * @param {Roo.bootstrap.NavItem} the navitem to add
18454     */
18455     register : function(item)
18456     {
18457         this.tabs.push( item);
18458         item.navId = this.navId; // not really needed..
18459         this.addBullet();
18460     
18461     },
18462     
18463     getActivePanel : function()
18464     {
18465         var r = false;
18466         Roo.each(this.tabs, function(t) {
18467             if (t.active) {
18468                 r = t;
18469                 return false;
18470             }
18471             return null;
18472         });
18473         return r;
18474         
18475     },
18476     getPanelByName : function(n)
18477     {
18478         var r = false;
18479         Roo.each(this.tabs, function(t) {
18480             if (t.tabId == n) {
18481                 r = t;
18482                 return false;
18483             }
18484             return null;
18485         });
18486         return r;
18487     },
18488     indexOfPanel : function(p)
18489     {
18490         var r = false;
18491         Roo.each(this.tabs, function(t,i) {
18492             if (t.tabId == p.tabId) {
18493                 r = i;
18494                 return false;
18495             }
18496             return null;
18497         });
18498         return r;
18499     },
18500     /**
18501      * show a specific panel
18502      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18503      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18504      */
18505     showPanel : function (pan)
18506     {
18507         if(this.transition || typeof(pan) == 'undefined'){
18508             Roo.log("waiting for the transitionend");
18509             return false;
18510         }
18511         
18512         if (typeof(pan) == 'number') {
18513             pan = this.tabs[pan];
18514         }
18515         
18516         if (typeof(pan) == 'string') {
18517             pan = this.getPanelByName(pan);
18518         }
18519         
18520         var cur = this.getActivePanel();
18521         
18522         if(!pan || !cur){
18523             Roo.log('pan or acitve pan is undefined');
18524             return false;
18525         }
18526         
18527         if (pan.tabId == this.getActivePanel().tabId) {
18528             return true;
18529         }
18530         
18531         if (false === cur.fireEvent('beforedeactivate')) {
18532             return false;
18533         }
18534         
18535         if(this.bullets > 0 && !Roo.isTouch){
18536             this.setActiveBullet(this.indexOfPanel(pan));
18537         }
18538         
18539         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18540             
18541             //class="carousel-item carousel-item-next carousel-item-left"
18542             
18543             this.transition = true;
18544             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18545             var lr = dir == 'next' ? 'left' : 'right';
18546             pan.el.addClass(dir); // or prev
18547             pan.el.addClass('carousel-item-' + dir); // or prev
18548             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18549             cur.el.addClass(lr); // or right
18550             pan.el.addClass(lr);
18551             cur.el.addClass('carousel-item-' +lr); // or right
18552             pan.el.addClass('carousel-item-' +lr);
18553             
18554             
18555             var _this = this;
18556             cur.el.on('transitionend', function() {
18557                 Roo.log("trans end?");
18558                 
18559                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18560                 pan.setActive(true);
18561                 
18562                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18563                 cur.setActive(false);
18564                 
18565                 _this.transition = false;
18566                 
18567             }, this, { single:  true } );
18568             
18569             return true;
18570         }
18571         
18572         cur.setActive(false);
18573         pan.setActive(true);
18574         
18575         return true;
18576         
18577     },
18578     showPanelNext : function()
18579     {
18580         var i = this.indexOfPanel(this.getActivePanel());
18581         
18582         if (i >= this.tabs.length - 1 && !this.autoslide) {
18583             return;
18584         }
18585         
18586         if (i >= this.tabs.length - 1 && this.autoslide) {
18587             i = -1;
18588         }
18589         
18590         this.showPanel(this.tabs[i+1]);
18591     },
18592     
18593     showPanelPrev : function()
18594     {
18595         var i = this.indexOfPanel(this.getActivePanel());
18596         
18597         if (i  < 1 && !this.autoslide) {
18598             return;
18599         }
18600         
18601         if (i < 1 && this.autoslide) {
18602             i = this.tabs.length;
18603         }
18604         
18605         this.showPanel(this.tabs[i-1]);
18606     },
18607     
18608     
18609     addBullet: function()
18610     {
18611         if(!this.bullets || Roo.isTouch){
18612             return;
18613         }
18614         var ctr = this.el.select('.carousel-bullets',true).first();
18615         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18616         var bullet = ctr.createChild({
18617             cls : 'bullet bullet-' + i
18618         },ctr.dom.lastChild);
18619         
18620         
18621         var _this = this;
18622         
18623         bullet.on('click', (function(e, el, o, ii, t){
18624
18625             e.preventDefault();
18626
18627             this.showPanel(ii);
18628
18629             if(this.autoslide && this.slideFn){
18630                 clearInterval(this.slideFn);
18631                 this.slideFn = window.setInterval(function() {
18632                     _this.showPanelNext();
18633                 }, this.timer);
18634             }
18635
18636         }).createDelegate(this, [i, bullet], true));
18637                 
18638         
18639     },
18640      
18641     setActiveBullet : function(i)
18642     {
18643         if(Roo.isTouch){
18644             return;
18645         }
18646         
18647         Roo.each(this.el.select('.bullet', true).elements, function(el){
18648             el.removeClass('selected');
18649         });
18650
18651         var bullet = this.el.select('.bullet-' + i, true).first();
18652         
18653         if(!bullet){
18654             return;
18655         }
18656         
18657         bullet.addClass('selected');
18658     }
18659     
18660     
18661   
18662 });
18663
18664  
18665
18666  
18667  
18668 Roo.apply(Roo.bootstrap.TabGroup, {
18669     
18670     groups: {},
18671      /**
18672     * register a Navigation Group
18673     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18674     */
18675     register : function(navgrp)
18676     {
18677         this.groups[navgrp.navId] = navgrp;
18678         
18679     },
18680     /**
18681     * fetch a Navigation Group based on the navigation ID
18682     * if one does not exist , it will get created.
18683     * @param {string} the navgroup to add
18684     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18685     */
18686     get: function(navId) {
18687         if (typeof(this.groups[navId]) == 'undefined') {
18688             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18689         }
18690         return this.groups[navId] ;
18691     }
18692     
18693     
18694     
18695 });
18696
18697  /*
18698  * - LGPL
18699  *
18700  * TabPanel
18701  * 
18702  */
18703
18704 /**
18705  * @class Roo.bootstrap.TabPanel
18706  * @extends Roo.bootstrap.Component
18707  * Bootstrap TabPanel class
18708  * @cfg {Boolean} active panel active
18709  * @cfg {String} html panel content
18710  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18711  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18712  * @cfg {String} href click to link..
18713  * 
18714  * 
18715  * @constructor
18716  * Create a new TabPanel
18717  * @param {Object} config The config object
18718  */
18719
18720 Roo.bootstrap.TabPanel = function(config){
18721     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18722     this.addEvents({
18723         /**
18724              * @event changed
18725              * Fires when the active status changes
18726              * @param {Roo.bootstrap.TabPanel} this
18727              * @param {Boolean} state the new state
18728             
18729          */
18730         'changed': true,
18731         /**
18732              * @event beforedeactivate
18733              * Fires before a tab is de-activated - can be used to do validation on a form.
18734              * @param {Roo.bootstrap.TabPanel} this
18735              * @return {Boolean} false if there is an error
18736             
18737          */
18738         'beforedeactivate': true
18739      });
18740     
18741     this.tabId = this.tabId || Roo.id();
18742   
18743 };
18744
18745 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18746     
18747     active: false,
18748     html: false,
18749     tabId: false,
18750     navId : false,
18751     href : '',
18752     
18753     getAutoCreate : function(){
18754         
18755         
18756         var cfg = {
18757             tag: 'div',
18758             // item is needed for carousel - not sure if it has any effect otherwise
18759             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18760             html: this.html || ''
18761         };
18762         
18763         if(this.active){
18764             cfg.cls += ' active';
18765         }
18766         
18767         if(this.tabId){
18768             cfg.tabId = this.tabId;
18769         }
18770         
18771         
18772         
18773         return cfg;
18774     },
18775     
18776     initEvents:  function()
18777     {
18778         var p = this.parent();
18779         
18780         this.navId = this.navId || p.navId;
18781         
18782         if (typeof(this.navId) != 'undefined') {
18783             // not really needed.. but just in case.. parent should be a NavGroup.
18784             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18785             
18786             tg.register(this);
18787             
18788             var i = tg.tabs.length - 1;
18789             
18790             if(this.active && tg.bullets > 0 && i < tg.bullets){
18791                 tg.setActiveBullet(i);
18792             }
18793         }
18794         
18795         this.el.on('click', this.onClick, this);
18796         
18797         if(Roo.isTouch){
18798             this.el.on("touchstart", this.onTouchStart, this);
18799             this.el.on("touchmove", this.onTouchMove, this);
18800             this.el.on("touchend", this.onTouchEnd, this);
18801         }
18802         
18803     },
18804     
18805     onRender : function(ct, position)
18806     {
18807         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18808     },
18809     
18810     setActive : function(state)
18811     {
18812         Roo.log("panel - set active " + this.tabId + "=" + state);
18813         
18814         this.active = state;
18815         if (!state) {
18816             this.el.removeClass('active');
18817             
18818         } else  if (!this.el.hasClass('active')) {
18819             this.el.addClass('active');
18820         }
18821         
18822         this.fireEvent('changed', this, state);
18823     },
18824     
18825     onClick : function(e)
18826     {
18827         e.preventDefault();
18828         
18829         if(!this.href.length){
18830             return;
18831         }
18832         
18833         window.location.href = this.href;
18834     },
18835     
18836     startX : 0,
18837     startY : 0,
18838     endX : 0,
18839     endY : 0,
18840     swiping : false,
18841     
18842     onTouchStart : function(e)
18843     {
18844         this.swiping = false;
18845         
18846         this.startX = e.browserEvent.touches[0].clientX;
18847         this.startY = e.browserEvent.touches[0].clientY;
18848     },
18849     
18850     onTouchMove : function(e)
18851     {
18852         this.swiping = true;
18853         
18854         this.endX = e.browserEvent.touches[0].clientX;
18855         this.endY = e.browserEvent.touches[0].clientY;
18856     },
18857     
18858     onTouchEnd : function(e)
18859     {
18860         if(!this.swiping){
18861             this.onClick(e);
18862             return;
18863         }
18864         
18865         var tabGroup = this.parent();
18866         
18867         if(this.endX > this.startX){ // swiping right
18868             tabGroup.showPanelPrev();
18869             return;
18870         }
18871         
18872         if(this.startX > this.endX){ // swiping left
18873             tabGroup.showPanelNext();
18874             return;
18875         }
18876     }
18877     
18878     
18879 });
18880  
18881
18882  
18883
18884  /*
18885  * - LGPL
18886  *
18887  * DateField
18888  * 
18889  */
18890
18891 /**
18892  * @class Roo.bootstrap.DateField
18893  * @extends Roo.bootstrap.Input
18894  * Bootstrap DateField class
18895  * @cfg {Number} weekStart default 0
18896  * @cfg {String} viewMode default empty, (months|years)
18897  * @cfg {String} minViewMode default empty, (months|years)
18898  * @cfg {Number} startDate default -Infinity
18899  * @cfg {Number} endDate default Infinity
18900  * @cfg {Boolean} todayHighlight default false
18901  * @cfg {Boolean} todayBtn default false
18902  * @cfg {Boolean} calendarWeeks default false
18903  * @cfg {Object} daysOfWeekDisabled default empty
18904  * @cfg {Boolean} singleMode default false (true | false)
18905  * 
18906  * @cfg {Boolean} keyboardNavigation default true
18907  * @cfg {String} language default en
18908  * 
18909  * @constructor
18910  * Create a new DateField
18911  * @param {Object} config The config object
18912  */
18913
18914 Roo.bootstrap.DateField = function(config){
18915     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18916      this.addEvents({
18917             /**
18918              * @event show
18919              * Fires when this field show.
18920              * @param {Roo.bootstrap.DateField} this
18921              * @param {Mixed} date The date value
18922              */
18923             show : true,
18924             /**
18925              * @event show
18926              * Fires when this field hide.
18927              * @param {Roo.bootstrap.DateField} this
18928              * @param {Mixed} date The date value
18929              */
18930             hide : true,
18931             /**
18932              * @event select
18933              * Fires when select a date.
18934              * @param {Roo.bootstrap.DateField} this
18935              * @param {Mixed} date The date value
18936              */
18937             select : true,
18938             /**
18939              * @event beforeselect
18940              * Fires when before select a date.
18941              * @param {Roo.bootstrap.DateField} this
18942              * @param {Mixed} date The date value
18943              */
18944             beforeselect : true
18945         });
18946 };
18947
18948 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18949     
18950     /**
18951      * @cfg {String} format
18952      * The default date format string which can be overriden for localization support.  The format must be
18953      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18954      */
18955     format : "m/d/y",
18956     /**
18957      * @cfg {String} altFormats
18958      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18959      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18960      */
18961     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18962     
18963     weekStart : 0,
18964     
18965     viewMode : '',
18966     
18967     minViewMode : '',
18968     
18969     todayHighlight : false,
18970     
18971     todayBtn: false,
18972     
18973     language: 'en',
18974     
18975     keyboardNavigation: true,
18976     
18977     calendarWeeks: false,
18978     
18979     startDate: -Infinity,
18980     
18981     endDate: Infinity,
18982     
18983     daysOfWeekDisabled: [],
18984     
18985     _events: [],
18986     
18987     singleMode : false,
18988     
18989     UTCDate: function()
18990     {
18991         return new Date(Date.UTC.apply(Date, arguments));
18992     },
18993     
18994     UTCToday: function()
18995     {
18996         var today = new Date();
18997         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18998     },
18999     
19000     getDate: function() {
19001             var d = this.getUTCDate();
19002             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19003     },
19004     
19005     getUTCDate: function() {
19006             return this.date;
19007     },
19008     
19009     setDate: function(d) {
19010             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19011     },
19012     
19013     setUTCDate: function(d) {
19014             this.date = d;
19015             this.setValue(this.formatDate(this.date));
19016     },
19017         
19018     onRender: function(ct, position)
19019     {
19020         
19021         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19022         
19023         this.language = this.language || 'en';
19024         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19025         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19026         
19027         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19028         this.format = this.format || 'm/d/y';
19029         this.isInline = false;
19030         this.isInput = true;
19031         this.component = this.el.select('.add-on', true).first() || false;
19032         this.component = (this.component && this.component.length === 0) ? false : this.component;
19033         this.hasInput = this.component && this.inputEl().length;
19034         
19035         if (typeof(this.minViewMode === 'string')) {
19036             switch (this.minViewMode) {
19037                 case 'months':
19038                     this.minViewMode = 1;
19039                     break;
19040                 case 'years':
19041                     this.minViewMode = 2;
19042                     break;
19043                 default:
19044                     this.minViewMode = 0;
19045                     break;
19046             }
19047         }
19048         
19049         if (typeof(this.viewMode === 'string')) {
19050             switch (this.viewMode) {
19051                 case 'months':
19052                     this.viewMode = 1;
19053                     break;
19054                 case 'years':
19055                     this.viewMode = 2;
19056                     break;
19057                 default:
19058                     this.viewMode = 0;
19059                     break;
19060             }
19061         }
19062                 
19063         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19064         
19065 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19066         
19067         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19068         
19069         this.picker().on('mousedown', this.onMousedown, this);
19070         this.picker().on('click', this.onClick, this);
19071         
19072         this.picker().addClass('datepicker-dropdown');
19073         
19074         this.startViewMode = this.viewMode;
19075         
19076         if(this.singleMode){
19077             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19078                 v.setVisibilityMode(Roo.Element.DISPLAY);
19079                 v.hide();
19080             });
19081             
19082             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19083                 v.setStyle('width', '189px');
19084             });
19085         }
19086         
19087         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19088             if(!this.calendarWeeks){
19089                 v.remove();
19090                 return;
19091             }
19092             
19093             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19094             v.attr('colspan', function(i, val){
19095                 return parseInt(val) + 1;
19096             });
19097         });
19098                         
19099         
19100         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19101         
19102         this.setStartDate(this.startDate);
19103         this.setEndDate(this.endDate);
19104         
19105         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19106         
19107         this.fillDow();
19108         this.fillMonths();
19109         this.update();
19110         this.showMode();
19111         
19112         if(this.isInline) {
19113             this.showPopup();
19114         }
19115     },
19116     
19117     picker : function()
19118     {
19119         return this.pickerEl;
19120 //        return this.el.select('.datepicker', true).first();
19121     },
19122     
19123     fillDow: function()
19124     {
19125         var dowCnt = this.weekStart;
19126         
19127         var dow = {
19128             tag: 'tr',
19129             cn: [
19130                 
19131             ]
19132         };
19133         
19134         if(this.calendarWeeks){
19135             dow.cn.push({
19136                 tag: 'th',
19137                 cls: 'cw',
19138                 html: '&nbsp;'
19139             })
19140         }
19141         
19142         while (dowCnt < this.weekStart + 7) {
19143             dow.cn.push({
19144                 tag: 'th',
19145                 cls: 'dow',
19146                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19147             });
19148         }
19149         
19150         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19151     },
19152     
19153     fillMonths: function()
19154     {    
19155         var i = 0;
19156         var months = this.picker().select('>.datepicker-months td', true).first();
19157         
19158         months.dom.innerHTML = '';
19159         
19160         while (i < 12) {
19161             var month = {
19162                 tag: 'span',
19163                 cls: 'month',
19164                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19165             };
19166             
19167             months.createChild(month);
19168         }
19169         
19170     },
19171     
19172     update: function()
19173     {
19174         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;
19175         
19176         if (this.date < this.startDate) {
19177             this.viewDate = new Date(this.startDate);
19178         } else if (this.date > this.endDate) {
19179             this.viewDate = new Date(this.endDate);
19180         } else {
19181             this.viewDate = new Date(this.date);
19182         }
19183         
19184         this.fill();
19185     },
19186     
19187     fill: function() 
19188     {
19189         var d = new Date(this.viewDate),
19190                 year = d.getUTCFullYear(),
19191                 month = d.getUTCMonth(),
19192                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19193                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19194                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19195                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19196                 currentDate = this.date && this.date.valueOf(),
19197                 today = this.UTCToday();
19198         
19199         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19200         
19201 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19202         
19203 //        this.picker.select('>tfoot th.today').
19204 //                                              .text(dates[this.language].today)
19205 //                                              .toggle(this.todayBtn !== false);
19206     
19207         this.updateNavArrows();
19208         this.fillMonths();
19209                                                 
19210         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19211         
19212         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19213          
19214         prevMonth.setUTCDate(day);
19215         
19216         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19217         
19218         var nextMonth = new Date(prevMonth);
19219         
19220         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19221         
19222         nextMonth = nextMonth.valueOf();
19223         
19224         var fillMonths = false;
19225         
19226         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19227         
19228         while(prevMonth.valueOf() <= nextMonth) {
19229             var clsName = '';
19230             
19231             if (prevMonth.getUTCDay() === this.weekStart) {
19232                 if(fillMonths){
19233                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19234                 }
19235                     
19236                 fillMonths = {
19237                     tag: 'tr',
19238                     cn: []
19239                 };
19240                 
19241                 if(this.calendarWeeks){
19242                     // ISO 8601: First week contains first thursday.
19243                     // ISO also states week starts on Monday, but we can be more abstract here.
19244                     var
19245                     // Start of current week: based on weekstart/current date
19246                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19247                     // Thursday of this week
19248                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19249                     // First Thursday of year, year from thursday
19250                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19251                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19252                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19253                     
19254                     fillMonths.cn.push({
19255                         tag: 'td',
19256                         cls: 'cw',
19257                         html: calWeek
19258                     });
19259                 }
19260             }
19261             
19262             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19263                 clsName += ' old';
19264             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19265                 clsName += ' new';
19266             }
19267             if (this.todayHighlight &&
19268                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19269                 prevMonth.getUTCMonth() == today.getMonth() &&
19270                 prevMonth.getUTCDate() == today.getDate()) {
19271                 clsName += ' today';
19272             }
19273             
19274             if (currentDate && prevMonth.valueOf() === currentDate) {
19275                 clsName += ' active';
19276             }
19277             
19278             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19279                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19280                     clsName += ' disabled';
19281             }
19282             
19283             fillMonths.cn.push({
19284                 tag: 'td',
19285                 cls: 'day ' + clsName,
19286                 html: prevMonth.getDate()
19287             });
19288             
19289             prevMonth.setDate(prevMonth.getDate()+1);
19290         }
19291           
19292         var currentYear = this.date && this.date.getUTCFullYear();
19293         var currentMonth = this.date && this.date.getUTCMonth();
19294         
19295         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19296         
19297         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19298             v.removeClass('active');
19299             
19300             if(currentYear === year && k === currentMonth){
19301                 v.addClass('active');
19302             }
19303             
19304             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19305                 v.addClass('disabled');
19306             }
19307             
19308         });
19309         
19310         
19311         year = parseInt(year/10, 10) * 10;
19312         
19313         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19314         
19315         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19316         
19317         year -= 1;
19318         for (var i = -1; i < 11; i++) {
19319             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19320                 tag: 'span',
19321                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19322                 html: year
19323             });
19324             
19325             year += 1;
19326         }
19327     },
19328     
19329     showMode: function(dir) 
19330     {
19331         if (dir) {
19332             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19333         }
19334         
19335         Roo.each(this.picker().select('>div',true).elements, function(v){
19336             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19337             v.hide();
19338         });
19339         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19340     },
19341     
19342     place: function()
19343     {
19344         if(this.isInline) {
19345             return;
19346         }
19347         
19348         this.picker().removeClass(['bottom', 'top']);
19349         
19350         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19351             /*
19352              * place to the top of element!
19353              *
19354              */
19355             
19356             this.picker().addClass('top');
19357             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19358             
19359             return;
19360         }
19361         
19362         this.picker().addClass('bottom');
19363         
19364         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19365     },
19366     
19367     parseDate : function(value)
19368     {
19369         if(!value || value instanceof Date){
19370             return value;
19371         }
19372         var v = Date.parseDate(value, this.format);
19373         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19374             v = Date.parseDate(value, 'Y-m-d');
19375         }
19376         if(!v && this.altFormats){
19377             if(!this.altFormatsArray){
19378                 this.altFormatsArray = this.altFormats.split("|");
19379             }
19380             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19381                 v = Date.parseDate(value, this.altFormatsArray[i]);
19382             }
19383         }
19384         return v;
19385     },
19386     
19387     formatDate : function(date, fmt)
19388     {   
19389         return (!date || !(date instanceof Date)) ?
19390         date : date.dateFormat(fmt || this.format);
19391     },
19392     
19393     onFocus : function()
19394     {
19395         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19396         this.showPopup();
19397     },
19398     
19399     onBlur : function()
19400     {
19401         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19402         
19403         var d = this.inputEl().getValue();
19404         
19405         this.setValue(d);
19406                 
19407         this.hidePopup();
19408     },
19409     
19410     showPopup : function()
19411     {
19412         this.picker().show();
19413         this.update();
19414         this.place();
19415         
19416         this.fireEvent('showpopup', this, this.date);
19417     },
19418     
19419     hidePopup : function()
19420     {
19421         if(this.isInline) {
19422             return;
19423         }
19424         this.picker().hide();
19425         this.viewMode = this.startViewMode;
19426         this.showMode();
19427         
19428         this.fireEvent('hidepopup', this, this.date);
19429         
19430     },
19431     
19432     onMousedown: function(e)
19433     {
19434         e.stopPropagation();
19435         e.preventDefault();
19436     },
19437     
19438     keyup: function(e)
19439     {
19440         Roo.bootstrap.DateField.superclass.keyup.call(this);
19441         this.update();
19442     },
19443
19444     setValue: function(v)
19445     {
19446         if(this.fireEvent('beforeselect', this, v) !== false){
19447             var d = new Date(this.parseDate(v) ).clearTime();
19448         
19449             if(isNaN(d.getTime())){
19450                 this.date = this.viewDate = '';
19451                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19452                 return;
19453             }
19454
19455             v = this.formatDate(d);
19456
19457             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19458
19459             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19460
19461             this.update();
19462
19463             this.fireEvent('select', this, this.date);
19464         }
19465     },
19466     
19467     getValue: function()
19468     {
19469         return this.formatDate(this.date);
19470     },
19471     
19472     fireKey: function(e)
19473     {
19474         if (!this.picker().isVisible()){
19475             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19476                 this.showPopup();
19477             }
19478             return;
19479         }
19480         
19481         var dateChanged = false,
19482         dir, day, month,
19483         newDate, newViewDate;
19484         
19485         switch(e.keyCode){
19486             case 27: // escape
19487                 this.hidePopup();
19488                 e.preventDefault();
19489                 break;
19490             case 37: // left
19491             case 39: // right
19492                 if (!this.keyboardNavigation) {
19493                     break;
19494                 }
19495                 dir = e.keyCode == 37 ? -1 : 1;
19496                 
19497                 if (e.ctrlKey){
19498                     newDate = this.moveYear(this.date, dir);
19499                     newViewDate = this.moveYear(this.viewDate, dir);
19500                 } else if (e.shiftKey){
19501                     newDate = this.moveMonth(this.date, dir);
19502                     newViewDate = this.moveMonth(this.viewDate, dir);
19503                 } else {
19504                     newDate = new Date(this.date);
19505                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19506                     newViewDate = new Date(this.viewDate);
19507                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19508                 }
19509                 if (this.dateWithinRange(newDate)){
19510                     this.date = newDate;
19511                     this.viewDate = newViewDate;
19512                     this.setValue(this.formatDate(this.date));
19513 //                    this.update();
19514                     e.preventDefault();
19515                     dateChanged = true;
19516                 }
19517                 break;
19518             case 38: // up
19519             case 40: // down
19520                 if (!this.keyboardNavigation) {
19521                     break;
19522                 }
19523                 dir = e.keyCode == 38 ? -1 : 1;
19524                 if (e.ctrlKey){
19525                     newDate = this.moveYear(this.date, dir);
19526                     newViewDate = this.moveYear(this.viewDate, dir);
19527                 } else if (e.shiftKey){
19528                     newDate = this.moveMonth(this.date, dir);
19529                     newViewDate = this.moveMonth(this.viewDate, dir);
19530                 } else {
19531                     newDate = new Date(this.date);
19532                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19533                     newViewDate = new Date(this.viewDate);
19534                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19535                 }
19536                 if (this.dateWithinRange(newDate)){
19537                     this.date = newDate;
19538                     this.viewDate = newViewDate;
19539                     this.setValue(this.formatDate(this.date));
19540 //                    this.update();
19541                     e.preventDefault();
19542                     dateChanged = true;
19543                 }
19544                 break;
19545             case 13: // enter
19546                 this.setValue(this.formatDate(this.date));
19547                 this.hidePopup();
19548                 e.preventDefault();
19549                 break;
19550             case 9: // tab
19551                 this.setValue(this.formatDate(this.date));
19552                 this.hidePopup();
19553                 break;
19554             case 16: // shift
19555             case 17: // ctrl
19556             case 18: // alt
19557                 break;
19558             default :
19559                 this.hidePopup();
19560                 
19561         }
19562     },
19563     
19564     
19565     onClick: function(e) 
19566     {
19567         e.stopPropagation();
19568         e.preventDefault();
19569         
19570         var target = e.getTarget();
19571         
19572         if(target.nodeName.toLowerCase() === 'i'){
19573             target = Roo.get(target).dom.parentNode;
19574         }
19575         
19576         var nodeName = target.nodeName;
19577         var className = target.className;
19578         var html = target.innerHTML;
19579         //Roo.log(nodeName);
19580         
19581         switch(nodeName.toLowerCase()) {
19582             case 'th':
19583                 switch(className) {
19584                     case 'switch':
19585                         this.showMode(1);
19586                         break;
19587                     case 'prev':
19588                     case 'next':
19589                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19590                         switch(this.viewMode){
19591                                 case 0:
19592                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19593                                         break;
19594                                 case 1:
19595                                 case 2:
19596                                         this.viewDate = this.moveYear(this.viewDate, dir);
19597                                         break;
19598                         }
19599                         this.fill();
19600                         break;
19601                     case 'today':
19602                         var date = new Date();
19603                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19604 //                        this.fill()
19605                         this.setValue(this.formatDate(this.date));
19606                         
19607                         this.hidePopup();
19608                         break;
19609                 }
19610                 break;
19611             case 'span':
19612                 if (className.indexOf('disabled') < 0) {
19613                     this.viewDate.setUTCDate(1);
19614                     if (className.indexOf('month') > -1) {
19615                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19616                     } else {
19617                         var year = parseInt(html, 10) || 0;
19618                         this.viewDate.setUTCFullYear(year);
19619                         
19620                     }
19621                     
19622                     if(this.singleMode){
19623                         this.setValue(this.formatDate(this.viewDate));
19624                         this.hidePopup();
19625                         return;
19626                     }
19627                     
19628                     this.showMode(-1);
19629                     this.fill();
19630                 }
19631                 break;
19632                 
19633             case 'td':
19634                 //Roo.log(className);
19635                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19636                     var day = parseInt(html, 10) || 1;
19637                     var year = this.viewDate.getUTCFullYear(),
19638                         month = this.viewDate.getUTCMonth();
19639
19640                     if (className.indexOf('old') > -1) {
19641                         if(month === 0 ){
19642                             month = 11;
19643                             year -= 1;
19644                         }else{
19645                             month -= 1;
19646                         }
19647                     } else if (className.indexOf('new') > -1) {
19648                         if (month == 11) {
19649                             month = 0;
19650                             year += 1;
19651                         } else {
19652                             month += 1;
19653                         }
19654                     }
19655                     //Roo.log([year,month,day]);
19656                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19657                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19658 //                    this.fill();
19659                     //Roo.log(this.formatDate(this.date));
19660                     this.setValue(this.formatDate(this.date));
19661                     this.hidePopup();
19662                 }
19663                 break;
19664         }
19665     },
19666     
19667     setStartDate: function(startDate)
19668     {
19669         this.startDate = startDate || -Infinity;
19670         if (this.startDate !== -Infinity) {
19671             this.startDate = this.parseDate(this.startDate);
19672         }
19673         this.update();
19674         this.updateNavArrows();
19675     },
19676
19677     setEndDate: function(endDate)
19678     {
19679         this.endDate = endDate || Infinity;
19680         if (this.endDate !== Infinity) {
19681             this.endDate = this.parseDate(this.endDate);
19682         }
19683         this.update();
19684         this.updateNavArrows();
19685     },
19686     
19687     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19688     {
19689         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19690         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19691             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19692         }
19693         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19694             return parseInt(d, 10);
19695         });
19696         this.update();
19697         this.updateNavArrows();
19698     },
19699     
19700     updateNavArrows: function() 
19701     {
19702         if(this.singleMode){
19703             return;
19704         }
19705         
19706         var d = new Date(this.viewDate),
19707         year = d.getUTCFullYear(),
19708         month = d.getUTCMonth();
19709         
19710         Roo.each(this.picker().select('.prev', true).elements, function(v){
19711             v.show();
19712             switch (this.viewMode) {
19713                 case 0:
19714
19715                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19716                         v.hide();
19717                     }
19718                     break;
19719                 case 1:
19720                 case 2:
19721                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19722                         v.hide();
19723                     }
19724                     break;
19725             }
19726         });
19727         
19728         Roo.each(this.picker().select('.next', true).elements, function(v){
19729             v.show();
19730             switch (this.viewMode) {
19731                 case 0:
19732
19733                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19734                         v.hide();
19735                     }
19736                     break;
19737                 case 1:
19738                 case 2:
19739                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19740                         v.hide();
19741                     }
19742                     break;
19743             }
19744         })
19745     },
19746     
19747     moveMonth: function(date, dir)
19748     {
19749         if (!dir) {
19750             return date;
19751         }
19752         var new_date = new Date(date.valueOf()),
19753         day = new_date.getUTCDate(),
19754         month = new_date.getUTCMonth(),
19755         mag = Math.abs(dir),
19756         new_month, test;
19757         dir = dir > 0 ? 1 : -1;
19758         if (mag == 1){
19759             test = dir == -1
19760             // If going back one month, make sure month is not current month
19761             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19762             ? function(){
19763                 return new_date.getUTCMonth() == month;
19764             }
19765             // If going forward one month, make sure month is as expected
19766             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19767             : function(){
19768                 return new_date.getUTCMonth() != new_month;
19769             };
19770             new_month = month + dir;
19771             new_date.setUTCMonth(new_month);
19772             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19773             if (new_month < 0 || new_month > 11) {
19774                 new_month = (new_month + 12) % 12;
19775             }
19776         } else {
19777             // For magnitudes >1, move one month at a time...
19778             for (var i=0; i<mag; i++) {
19779                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19780                 new_date = this.moveMonth(new_date, dir);
19781             }
19782             // ...then reset the day, keeping it in the new month
19783             new_month = new_date.getUTCMonth();
19784             new_date.setUTCDate(day);
19785             test = function(){
19786                 return new_month != new_date.getUTCMonth();
19787             };
19788         }
19789         // Common date-resetting loop -- if date is beyond end of month, make it
19790         // end of month
19791         while (test()){
19792             new_date.setUTCDate(--day);
19793             new_date.setUTCMonth(new_month);
19794         }
19795         return new_date;
19796     },
19797
19798     moveYear: function(date, dir)
19799     {
19800         return this.moveMonth(date, dir*12);
19801     },
19802
19803     dateWithinRange: function(date)
19804     {
19805         return date >= this.startDate && date <= this.endDate;
19806     },
19807
19808     
19809     remove: function() 
19810     {
19811         this.picker().remove();
19812     },
19813     
19814     validateValue : function(value)
19815     {
19816         if(this.getVisibilityEl().hasClass('hidden')){
19817             return true;
19818         }
19819         
19820         if(value.length < 1)  {
19821             if(this.allowBlank){
19822                 return true;
19823             }
19824             return false;
19825         }
19826         
19827         if(value.length < this.minLength){
19828             return false;
19829         }
19830         if(value.length > this.maxLength){
19831             return false;
19832         }
19833         if(this.vtype){
19834             var vt = Roo.form.VTypes;
19835             if(!vt[this.vtype](value, this)){
19836                 return false;
19837             }
19838         }
19839         if(typeof this.validator == "function"){
19840             var msg = this.validator(value);
19841             if(msg !== true){
19842                 return false;
19843             }
19844         }
19845         
19846         if(this.regex && !this.regex.test(value)){
19847             return false;
19848         }
19849         
19850         if(typeof(this.parseDate(value)) == 'undefined'){
19851             return false;
19852         }
19853         
19854         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19855             return false;
19856         }      
19857         
19858         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19859             return false;
19860         } 
19861         
19862         
19863         return true;
19864     },
19865     
19866     reset : function()
19867     {
19868         this.date = this.viewDate = '';
19869         
19870         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19871     }
19872    
19873 });
19874
19875 Roo.apply(Roo.bootstrap.DateField,  {
19876     
19877     head : {
19878         tag: 'thead',
19879         cn: [
19880         {
19881             tag: 'tr',
19882             cn: [
19883             {
19884                 tag: 'th',
19885                 cls: 'prev',
19886                 html: '<i class="fa fa-arrow-left"/>'
19887             },
19888             {
19889                 tag: 'th',
19890                 cls: 'switch',
19891                 colspan: '5'
19892             },
19893             {
19894                 tag: 'th',
19895                 cls: 'next',
19896                 html: '<i class="fa fa-arrow-right"/>'
19897             }
19898
19899             ]
19900         }
19901         ]
19902     },
19903     
19904     content : {
19905         tag: 'tbody',
19906         cn: [
19907         {
19908             tag: 'tr',
19909             cn: [
19910             {
19911                 tag: 'td',
19912                 colspan: '7'
19913             }
19914             ]
19915         }
19916         ]
19917     },
19918     
19919     footer : {
19920         tag: 'tfoot',
19921         cn: [
19922         {
19923             tag: 'tr',
19924             cn: [
19925             {
19926                 tag: 'th',
19927                 colspan: '7',
19928                 cls: 'today'
19929             }
19930                     
19931             ]
19932         }
19933         ]
19934     },
19935     
19936     dates:{
19937         en: {
19938             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19939             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19940             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19941             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19942             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19943             today: "Today"
19944         }
19945     },
19946     
19947     modes: [
19948     {
19949         clsName: 'days',
19950         navFnc: 'Month',
19951         navStep: 1
19952     },
19953     {
19954         clsName: 'months',
19955         navFnc: 'FullYear',
19956         navStep: 1
19957     },
19958     {
19959         clsName: 'years',
19960         navFnc: 'FullYear',
19961         navStep: 10
19962     }]
19963 });
19964
19965 Roo.apply(Roo.bootstrap.DateField,  {
19966   
19967     template : {
19968         tag: 'div',
19969         cls: 'datepicker dropdown-menu roo-dynamic',
19970         cn: [
19971         {
19972             tag: 'div',
19973             cls: 'datepicker-days',
19974             cn: [
19975             {
19976                 tag: 'table',
19977                 cls: 'table-condensed',
19978                 cn:[
19979                 Roo.bootstrap.DateField.head,
19980                 {
19981                     tag: 'tbody'
19982                 },
19983                 Roo.bootstrap.DateField.footer
19984                 ]
19985             }
19986             ]
19987         },
19988         {
19989             tag: 'div',
19990             cls: 'datepicker-months',
19991             cn: [
19992             {
19993                 tag: 'table',
19994                 cls: 'table-condensed',
19995                 cn:[
19996                 Roo.bootstrap.DateField.head,
19997                 Roo.bootstrap.DateField.content,
19998                 Roo.bootstrap.DateField.footer
19999                 ]
20000             }
20001             ]
20002         },
20003         {
20004             tag: 'div',
20005             cls: 'datepicker-years',
20006             cn: [
20007             {
20008                 tag: 'table',
20009                 cls: 'table-condensed',
20010                 cn:[
20011                 Roo.bootstrap.DateField.head,
20012                 Roo.bootstrap.DateField.content,
20013                 Roo.bootstrap.DateField.footer
20014                 ]
20015             }
20016             ]
20017         }
20018         ]
20019     }
20020 });
20021
20022  
20023
20024  /*
20025  * - LGPL
20026  *
20027  * TimeField
20028  * 
20029  */
20030
20031 /**
20032  * @class Roo.bootstrap.TimeField
20033  * @extends Roo.bootstrap.Input
20034  * Bootstrap DateField class
20035  * 
20036  * 
20037  * @constructor
20038  * Create a new TimeField
20039  * @param {Object} config The config object
20040  */
20041
20042 Roo.bootstrap.TimeField = function(config){
20043     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20044     this.addEvents({
20045             /**
20046              * @event show
20047              * Fires when this field show.
20048              * @param {Roo.bootstrap.DateField} thisthis
20049              * @param {Mixed} date The date value
20050              */
20051             show : true,
20052             /**
20053              * @event show
20054              * Fires when this field hide.
20055              * @param {Roo.bootstrap.DateField} this
20056              * @param {Mixed} date The date value
20057              */
20058             hide : true,
20059             /**
20060              * @event select
20061              * Fires when select a date.
20062              * @param {Roo.bootstrap.DateField} this
20063              * @param {Mixed} date The date value
20064              */
20065             select : true
20066         });
20067 };
20068
20069 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20070     
20071     /**
20072      * @cfg {String} format
20073      * The default time format string which can be overriden for localization support.  The format must be
20074      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20075      */
20076     format : "H:i",
20077        
20078     onRender: function(ct, position)
20079     {
20080         
20081         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20082                 
20083         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20084         
20085         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20086         
20087         this.pop = this.picker().select('>.datepicker-time',true).first();
20088         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20089         
20090         this.picker().on('mousedown', this.onMousedown, this);
20091         this.picker().on('click', this.onClick, this);
20092         
20093         this.picker().addClass('datepicker-dropdown');
20094     
20095         this.fillTime();
20096         this.update();
20097             
20098         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20099         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20100         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20101         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20102         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20103         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20104
20105     },
20106     
20107     fireKey: function(e){
20108         if (!this.picker().isVisible()){
20109             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20110                 this.show();
20111             }
20112             return;
20113         }
20114
20115         e.preventDefault();
20116         
20117         switch(e.keyCode){
20118             case 27: // escape
20119                 this.hide();
20120                 break;
20121             case 37: // left
20122             case 39: // right
20123                 this.onTogglePeriod();
20124                 break;
20125             case 38: // up
20126                 this.onIncrementMinutes();
20127                 break;
20128             case 40: // down
20129                 this.onDecrementMinutes();
20130                 break;
20131             case 13: // enter
20132             case 9: // tab
20133                 this.setTime();
20134                 break;
20135         }
20136     },
20137     
20138     onClick: function(e) {
20139         e.stopPropagation();
20140         e.preventDefault();
20141     },
20142     
20143     picker : function()
20144     {
20145         return this.el.select('.datepicker', true).first();
20146     },
20147     
20148     fillTime: function()
20149     {    
20150         var time = this.pop.select('tbody', true).first();
20151         
20152         time.dom.innerHTML = '';
20153         
20154         time.createChild({
20155             tag: 'tr',
20156             cn: [
20157                 {
20158                     tag: 'td',
20159                     cn: [
20160                         {
20161                             tag: 'a',
20162                             href: '#',
20163                             cls: 'btn',
20164                             cn: [
20165                                 {
20166                                     tag: 'span',
20167                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20168                                 }
20169                             ]
20170                         } 
20171                     ]
20172                 },
20173                 {
20174                     tag: 'td',
20175                     cls: 'separator'
20176                 },
20177                 {
20178                     tag: 'td',
20179                     cn: [
20180                         {
20181                             tag: 'a',
20182                             href: '#',
20183                             cls: 'btn',
20184                             cn: [
20185                                 {
20186                                     tag: 'span',
20187                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20188                                 }
20189                             ]
20190                         }
20191                     ]
20192                 },
20193                 {
20194                     tag: 'td',
20195                     cls: 'separator'
20196                 }
20197             ]
20198         });
20199         
20200         time.createChild({
20201             tag: 'tr',
20202             cn: [
20203                 {
20204                     tag: 'td',
20205                     cn: [
20206                         {
20207                             tag: 'span',
20208                             cls: 'timepicker-hour',
20209                             html: '00'
20210                         }  
20211                     ]
20212                 },
20213                 {
20214                     tag: 'td',
20215                     cls: 'separator',
20216                     html: ':'
20217                 },
20218                 {
20219                     tag: 'td',
20220                     cn: [
20221                         {
20222                             tag: 'span',
20223                             cls: 'timepicker-minute',
20224                             html: '00'
20225                         }  
20226                     ]
20227                 },
20228                 {
20229                     tag: 'td',
20230                     cls: 'separator'
20231                 },
20232                 {
20233                     tag: 'td',
20234                     cn: [
20235                         {
20236                             tag: 'button',
20237                             type: 'button',
20238                             cls: 'btn btn-primary period',
20239                             html: 'AM'
20240                             
20241                         }
20242                     ]
20243                 }
20244             ]
20245         });
20246         
20247         time.createChild({
20248             tag: 'tr',
20249             cn: [
20250                 {
20251                     tag: 'td',
20252                     cn: [
20253                         {
20254                             tag: 'a',
20255                             href: '#',
20256                             cls: 'btn',
20257                             cn: [
20258                                 {
20259                                     tag: 'span',
20260                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20261                                 }
20262                             ]
20263                         }
20264                     ]
20265                 },
20266                 {
20267                     tag: 'td',
20268                     cls: 'separator'
20269                 },
20270                 {
20271                     tag: 'td',
20272                     cn: [
20273                         {
20274                             tag: 'a',
20275                             href: '#',
20276                             cls: 'btn',
20277                             cn: [
20278                                 {
20279                                     tag: 'span',
20280                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20281                                 }
20282                             ]
20283                         }
20284                     ]
20285                 },
20286                 {
20287                     tag: 'td',
20288                     cls: 'separator'
20289                 }
20290             ]
20291         });
20292         
20293     },
20294     
20295     update: function()
20296     {
20297         
20298         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20299         
20300         this.fill();
20301     },
20302     
20303     fill: function() 
20304     {
20305         var hours = this.time.getHours();
20306         var minutes = this.time.getMinutes();
20307         var period = 'AM';
20308         
20309         if(hours > 11){
20310             period = 'PM';
20311         }
20312         
20313         if(hours == 0){
20314             hours = 12;
20315         }
20316         
20317         
20318         if(hours > 12){
20319             hours = hours - 12;
20320         }
20321         
20322         if(hours < 10){
20323             hours = '0' + hours;
20324         }
20325         
20326         if(minutes < 10){
20327             minutes = '0' + minutes;
20328         }
20329         
20330         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20331         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20332         this.pop.select('button', true).first().dom.innerHTML = period;
20333         
20334     },
20335     
20336     place: function()
20337     {   
20338         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20339         
20340         var cls = ['bottom'];
20341         
20342         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20343             cls.pop();
20344             cls.push('top');
20345         }
20346         
20347         cls.push('right');
20348         
20349         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20350             cls.pop();
20351             cls.push('left');
20352         }
20353         
20354         this.picker().addClass(cls.join('-'));
20355         
20356         var _this = this;
20357         
20358         Roo.each(cls, function(c){
20359             if(c == 'bottom'){
20360                 _this.picker().setTop(_this.inputEl().getHeight());
20361                 return;
20362             }
20363             if(c == 'top'){
20364                 _this.picker().setTop(0 - _this.picker().getHeight());
20365                 return;
20366             }
20367             
20368             if(c == 'left'){
20369                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20370                 return;
20371             }
20372             if(c == 'right'){
20373                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20374                 return;
20375             }
20376         });
20377         
20378     },
20379   
20380     onFocus : function()
20381     {
20382         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20383         this.show();
20384     },
20385     
20386     onBlur : function()
20387     {
20388         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20389         this.hide();
20390     },
20391     
20392     show : function()
20393     {
20394         this.picker().show();
20395         this.pop.show();
20396         this.update();
20397         this.place();
20398         
20399         this.fireEvent('show', this, this.date);
20400     },
20401     
20402     hide : function()
20403     {
20404         this.picker().hide();
20405         this.pop.hide();
20406         
20407         this.fireEvent('hide', this, this.date);
20408     },
20409     
20410     setTime : function()
20411     {
20412         this.hide();
20413         this.setValue(this.time.format(this.format));
20414         
20415         this.fireEvent('select', this, this.date);
20416         
20417         
20418     },
20419     
20420     onMousedown: function(e){
20421         e.stopPropagation();
20422         e.preventDefault();
20423     },
20424     
20425     onIncrementHours: function()
20426     {
20427         Roo.log('onIncrementHours');
20428         this.time = this.time.add(Date.HOUR, 1);
20429         this.update();
20430         
20431     },
20432     
20433     onDecrementHours: function()
20434     {
20435         Roo.log('onDecrementHours');
20436         this.time = this.time.add(Date.HOUR, -1);
20437         this.update();
20438     },
20439     
20440     onIncrementMinutes: function()
20441     {
20442         Roo.log('onIncrementMinutes');
20443         this.time = this.time.add(Date.MINUTE, 1);
20444         this.update();
20445     },
20446     
20447     onDecrementMinutes: function()
20448     {
20449         Roo.log('onDecrementMinutes');
20450         this.time = this.time.add(Date.MINUTE, -1);
20451         this.update();
20452     },
20453     
20454     onTogglePeriod: function()
20455     {
20456         Roo.log('onTogglePeriod');
20457         this.time = this.time.add(Date.HOUR, 12);
20458         this.update();
20459     }
20460     
20461    
20462 });
20463
20464 Roo.apply(Roo.bootstrap.TimeField,  {
20465     
20466     content : {
20467         tag: 'tbody',
20468         cn: [
20469             {
20470                 tag: 'tr',
20471                 cn: [
20472                 {
20473                     tag: 'td',
20474                     colspan: '7'
20475                 }
20476                 ]
20477             }
20478         ]
20479     },
20480     
20481     footer : {
20482         tag: 'tfoot',
20483         cn: [
20484             {
20485                 tag: 'tr',
20486                 cn: [
20487                 {
20488                     tag: 'th',
20489                     colspan: '7',
20490                     cls: '',
20491                     cn: [
20492                         {
20493                             tag: 'button',
20494                             cls: 'btn btn-info ok',
20495                             html: 'OK'
20496                         }
20497                     ]
20498                 }
20499
20500                 ]
20501             }
20502         ]
20503     }
20504 });
20505
20506 Roo.apply(Roo.bootstrap.TimeField,  {
20507   
20508     template : {
20509         tag: 'div',
20510         cls: 'datepicker dropdown-menu',
20511         cn: [
20512             {
20513                 tag: 'div',
20514                 cls: 'datepicker-time',
20515                 cn: [
20516                 {
20517                     tag: 'table',
20518                     cls: 'table-condensed',
20519                     cn:[
20520                     Roo.bootstrap.TimeField.content,
20521                     Roo.bootstrap.TimeField.footer
20522                     ]
20523                 }
20524                 ]
20525             }
20526         ]
20527     }
20528 });
20529
20530  
20531
20532  /*
20533  * - LGPL
20534  *
20535  * MonthField
20536  * 
20537  */
20538
20539 /**
20540  * @class Roo.bootstrap.MonthField
20541  * @extends Roo.bootstrap.Input
20542  * Bootstrap MonthField class
20543  * 
20544  * @cfg {String} language default en
20545  * 
20546  * @constructor
20547  * Create a new MonthField
20548  * @param {Object} config The config object
20549  */
20550
20551 Roo.bootstrap.MonthField = function(config){
20552     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20553     
20554     this.addEvents({
20555         /**
20556          * @event show
20557          * Fires when this field show.
20558          * @param {Roo.bootstrap.MonthField} this
20559          * @param {Mixed} date The date value
20560          */
20561         show : true,
20562         /**
20563          * @event show
20564          * Fires when this field hide.
20565          * @param {Roo.bootstrap.MonthField} this
20566          * @param {Mixed} date The date value
20567          */
20568         hide : true,
20569         /**
20570          * @event select
20571          * Fires when select a date.
20572          * @param {Roo.bootstrap.MonthField} this
20573          * @param {String} oldvalue The old value
20574          * @param {String} newvalue The new value
20575          */
20576         select : true
20577     });
20578 };
20579
20580 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20581     
20582     onRender: function(ct, position)
20583     {
20584         
20585         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20586         
20587         this.language = this.language || 'en';
20588         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20589         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20590         
20591         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20592         this.isInline = false;
20593         this.isInput = true;
20594         this.component = this.el.select('.add-on', true).first() || false;
20595         this.component = (this.component && this.component.length === 0) ? false : this.component;
20596         this.hasInput = this.component && this.inputEL().length;
20597         
20598         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20599         
20600         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20601         
20602         this.picker().on('mousedown', this.onMousedown, this);
20603         this.picker().on('click', this.onClick, this);
20604         
20605         this.picker().addClass('datepicker-dropdown');
20606         
20607         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20608             v.setStyle('width', '189px');
20609         });
20610         
20611         this.fillMonths();
20612         
20613         this.update();
20614         
20615         if(this.isInline) {
20616             this.show();
20617         }
20618         
20619     },
20620     
20621     setValue: function(v, suppressEvent)
20622     {   
20623         var o = this.getValue();
20624         
20625         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20626         
20627         this.update();
20628
20629         if(suppressEvent !== true){
20630             this.fireEvent('select', this, o, v);
20631         }
20632         
20633     },
20634     
20635     getValue: function()
20636     {
20637         return this.value;
20638     },
20639     
20640     onClick: function(e) 
20641     {
20642         e.stopPropagation();
20643         e.preventDefault();
20644         
20645         var target = e.getTarget();
20646         
20647         if(target.nodeName.toLowerCase() === 'i'){
20648             target = Roo.get(target).dom.parentNode;
20649         }
20650         
20651         var nodeName = target.nodeName;
20652         var className = target.className;
20653         var html = target.innerHTML;
20654         
20655         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20656             return;
20657         }
20658         
20659         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20660         
20661         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20662         
20663         this.hide();
20664                         
20665     },
20666     
20667     picker : function()
20668     {
20669         return this.pickerEl;
20670     },
20671     
20672     fillMonths: function()
20673     {    
20674         var i = 0;
20675         var months = this.picker().select('>.datepicker-months td', true).first();
20676         
20677         months.dom.innerHTML = '';
20678         
20679         while (i < 12) {
20680             var month = {
20681                 tag: 'span',
20682                 cls: 'month',
20683                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20684             };
20685             
20686             months.createChild(month);
20687         }
20688         
20689     },
20690     
20691     update: function()
20692     {
20693         var _this = this;
20694         
20695         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20696             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20697         }
20698         
20699         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20700             e.removeClass('active');
20701             
20702             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20703                 e.addClass('active');
20704             }
20705         })
20706     },
20707     
20708     place: function()
20709     {
20710         if(this.isInline) {
20711             return;
20712         }
20713         
20714         this.picker().removeClass(['bottom', 'top']);
20715         
20716         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20717             /*
20718              * place to the top of element!
20719              *
20720              */
20721             
20722             this.picker().addClass('top');
20723             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20724             
20725             return;
20726         }
20727         
20728         this.picker().addClass('bottom');
20729         
20730         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20731     },
20732     
20733     onFocus : function()
20734     {
20735         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20736         this.show();
20737     },
20738     
20739     onBlur : function()
20740     {
20741         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20742         
20743         var d = this.inputEl().getValue();
20744         
20745         this.setValue(d);
20746                 
20747         this.hide();
20748     },
20749     
20750     show : function()
20751     {
20752         this.picker().show();
20753         this.picker().select('>.datepicker-months', true).first().show();
20754         this.update();
20755         this.place();
20756         
20757         this.fireEvent('show', this, this.date);
20758     },
20759     
20760     hide : function()
20761     {
20762         if(this.isInline) {
20763             return;
20764         }
20765         this.picker().hide();
20766         this.fireEvent('hide', this, this.date);
20767         
20768     },
20769     
20770     onMousedown: function(e)
20771     {
20772         e.stopPropagation();
20773         e.preventDefault();
20774     },
20775     
20776     keyup: function(e)
20777     {
20778         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20779         this.update();
20780     },
20781
20782     fireKey: function(e)
20783     {
20784         if (!this.picker().isVisible()){
20785             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20786                 this.show();
20787             }
20788             return;
20789         }
20790         
20791         var dir;
20792         
20793         switch(e.keyCode){
20794             case 27: // escape
20795                 this.hide();
20796                 e.preventDefault();
20797                 break;
20798             case 37: // left
20799             case 39: // right
20800                 dir = e.keyCode == 37 ? -1 : 1;
20801                 
20802                 this.vIndex = this.vIndex + dir;
20803                 
20804                 if(this.vIndex < 0){
20805                     this.vIndex = 0;
20806                 }
20807                 
20808                 if(this.vIndex > 11){
20809                     this.vIndex = 11;
20810                 }
20811                 
20812                 if(isNaN(this.vIndex)){
20813                     this.vIndex = 0;
20814                 }
20815                 
20816                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20817                 
20818                 break;
20819             case 38: // up
20820             case 40: // down
20821                 
20822                 dir = e.keyCode == 38 ? -1 : 1;
20823                 
20824                 this.vIndex = this.vIndex + dir * 4;
20825                 
20826                 if(this.vIndex < 0){
20827                     this.vIndex = 0;
20828                 }
20829                 
20830                 if(this.vIndex > 11){
20831                     this.vIndex = 11;
20832                 }
20833                 
20834                 if(isNaN(this.vIndex)){
20835                     this.vIndex = 0;
20836                 }
20837                 
20838                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20839                 break;
20840                 
20841             case 13: // enter
20842                 
20843                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20844                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20845                 }
20846                 
20847                 this.hide();
20848                 e.preventDefault();
20849                 break;
20850             case 9: // tab
20851                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20852                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20853                 }
20854                 this.hide();
20855                 break;
20856             case 16: // shift
20857             case 17: // ctrl
20858             case 18: // alt
20859                 break;
20860             default :
20861                 this.hide();
20862                 
20863         }
20864     },
20865     
20866     remove: function() 
20867     {
20868         this.picker().remove();
20869     }
20870    
20871 });
20872
20873 Roo.apply(Roo.bootstrap.MonthField,  {
20874     
20875     content : {
20876         tag: 'tbody',
20877         cn: [
20878         {
20879             tag: 'tr',
20880             cn: [
20881             {
20882                 tag: 'td',
20883                 colspan: '7'
20884             }
20885             ]
20886         }
20887         ]
20888     },
20889     
20890     dates:{
20891         en: {
20892             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20893             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20894         }
20895     }
20896 });
20897
20898 Roo.apply(Roo.bootstrap.MonthField,  {
20899   
20900     template : {
20901         tag: 'div',
20902         cls: 'datepicker dropdown-menu roo-dynamic',
20903         cn: [
20904             {
20905                 tag: 'div',
20906                 cls: 'datepicker-months',
20907                 cn: [
20908                 {
20909                     tag: 'table',
20910                     cls: 'table-condensed',
20911                     cn:[
20912                         Roo.bootstrap.DateField.content
20913                     ]
20914                 }
20915                 ]
20916             }
20917         ]
20918     }
20919 });
20920
20921  
20922
20923  
20924  /*
20925  * - LGPL
20926  *
20927  * CheckBox
20928  * 
20929  */
20930
20931 /**
20932  * @class Roo.bootstrap.CheckBox
20933  * @extends Roo.bootstrap.Input
20934  * Bootstrap CheckBox class
20935  * 
20936  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20937  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20938  * @cfg {String} boxLabel The text that appears beside the checkbox
20939  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20940  * @cfg {Boolean} checked initnal the element
20941  * @cfg {Boolean} inline inline the element (default false)
20942  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20943  * @cfg {String} tooltip label tooltip
20944  * 
20945  * @constructor
20946  * Create a new CheckBox
20947  * @param {Object} config The config object
20948  */
20949
20950 Roo.bootstrap.CheckBox = function(config){
20951     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20952    
20953     this.addEvents({
20954         /**
20955         * @event check
20956         * Fires when the element is checked or unchecked.
20957         * @param {Roo.bootstrap.CheckBox} this This input
20958         * @param {Boolean} checked The new checked value
20959         */
20960        check : true,
20961        /**
20962         * @event click
20963         * Fires when the element is click.
20964         * @param {Roo.bootstrap.CheckBox} this This input
20965         */
20966        click : true
20967     });
20968     
20969 };
20970
20971 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20972   
20973     inputType: 'checkbox',
20974     inputValue: 1,
20975     valueOff: 0,
20976     boxLabel: false,
20977     checked: false,
20978     weight : false,
20979     inline: false,
20980     tooltip : '',
20981     
20982     getAutoCreate : function()
20983     {
20984         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20985         
20986         var id = Roo.id();
20987         
20988         var cfg = {};
20989         
20990         cfg.cls = 'form-group ' + this.inputType; //input-group
20991         
20992         if(this.inline){
20993             cfg.cls += ' ' + this.inputType + '-inline';
20994         }
20995         
20996         var input =  {
20997             tag: 'input',
20998             id : id,
20999             type : this.inputType,
21000             value : this.inputValue,
21001             cls : 'roo-' + this.inputType, //'form-box',
21002             placeholder : this.placeholder || ''
21003             
21004         };
21005         
21006         if(this.inputType != 'radio'){
21007             var hidden =  {
21008                 tag: 'input',
21009                 type : 'hidden',
21010                 cls : 'roo-hidden-value',
21011                 value : this.checked ? this.inputValue : this.valueOff
21012             };
21013         }
21014         
21015             
21016         if (this.weight) { // Validity check?
21017             cfg.cls += " " + this.inputType + "-" + this.weight;
21018         }
21019         
21020         if (this.disabled) {
21021             input.disabled=true;
21022         }
21023         
21024         if(this.checked){
21025             input.checked = this.checked;
21026         }
21027         
21028         if (this.name) {
21029             
21030             input.name = this.name;
21031             
21032             if(this.inputType != 'radio'){
21033                 hidden.name = this.name;
21034                 input.name = '_hidden_' + this.name;
21035             }
21036         }
21037         
21038         if (this.size) {
21039             input.cls += ' input-' + this.size;
21040         }
21041         
21042         var settings=this;
21043         
21044         ['xs','sm','md','lg'].map(function(size){
21045             if (settings[size]) {
21046                 cfg.cls += ' col-' + size + '-' + settings[size];
21047             }
21048         });
21049         
21050         var inputblock = input;
21051          
21052         if (this.before || this.after) {
21053             
21054             inputblock = {
21055                 cls : 'input-group',
21056                 cn :  [] 
21057             };
21058             
21059             if (this.before) {
21060                 inputblock.cn.push({
21061                     tag :'span',
21062                     cls : 'input-group-addon',
21063                     html : this.before
21064                 });
21065             }
21066             
21067             inputblock.cn.push(input);
21068             
21069             if(this.inputType != 'radio'){
21070                 inputblock.cn.push(hidden);
21071             }
21072             
21073             if (this.after) {
21074                 inputblock.cn.push({
21075                     tag :'span',
21076                     cls : 'input-group-addon',
21077                     html : this.after
21078                 });
21079             }
21080             
21081         }
21082         
21083         if (align ==='left' && this.fieldLabel.length) {
21084 //                Roo.log("left and has label");
21085             cfg.cn = [
21086                 {
21087                     tag: 'label',
21088                     'for' :  id,
21089                     cls : 'control-label',
21090                     html : this.fieldLabel
21091                 },
21092                 {
21093                     cls : "", 
21094                     cn: [
21095                         inputblock
21096                     ]
21097                 }
21098             ];
21099             
21100             if(this.labelWidth > 12){
21101                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21102             }
21103             
21104             if(this.labelWidth < 13 && this.labelmd == 0){
21105                 this.labelmd = this.labelWidth;
21106             }
21107             
21108             if(this.labellg > 0){
21109                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21110                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21111             }
21112             
21113             if(this.labelmd > 0){
21114                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21115                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21116             }
21117             
21118             if(this.labelsm > 0){
21119                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21120                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21121             }
21122             
21123             if(this.labelxs > 0){
21124                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21125                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21126             }
21127             
21128         } else if ( this.fieldLabel.length) {
21129 //                Roo.log(" label");
21130                 cfg.cn = [
21131                    
21132                     {
21133                         tag: this.boxLabel ? 'span' : 'label',
21134                         'for': id,
21135                         cls: 'control-label box-input-label',
21136                         //cls : 'input-group-addon',
21137                         html : this.fieldLabel
21138                     },
21139                     
21140                     inputblock
21141                     
21142                 ];
21143
21144         } else {
21145             
21146 //                Roo.log(" no label && no align");
21147                 cfg.cn = [  inputblock ] ;
21148                 
21149                 
21150         }
21151         
21152         if(this.boxLabel){
21153              var boxLabelCfg = {
21154                 tag: 'label',
21155                 //'for': id, // box label is handled by onclick - so no for...
21156                 cls: 'box-label',
21157                 html: this.boxLabel
21158             };
21159             
21160             if(this.tooltip){
21161                 boxLabelCfg.tooltip = this.tooltip;
21162             }
21163              
21164             cfg.cn.push(boxLabelCfg);
21165         }
21166         
21167         if(this.inputType != 'radio'){
21168             cfg.cn.push(hidden);
21169         }
21170         
21171         return cfg;
21172         
21173     },
21174     
21175     /**
21176      * return the real input element.
21177      */
21178     inputEl: function ()
21179     {
21180         return this.el.select('input.roo-' + this.inputType,true).first();
21181     },
21182     hiddenEl: function ()
21183     {
21184         return this.el.select('input.roo-hidden-value',true).first();
21185     },
21186     
21187     labelEl: function()
21188     {
21189         return this.el.select('label.control-label',true).first();
21190     },
21191     /* depricated... */
21192     
21193     label: function()
21194     {
21195         return this.labelEl();
21196     },
21197     
21198     boxLabelEl: function()
21199     {
21200         return this.el.select('label.box-label',true).first();
21201     },
21202     
21203     initEvents : function()
21204     {
21205 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21206         
21207         this.inputEl().on('click', this.onClick,  this);
21208         
21209         if (this.boxLabel) { 
21210             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21211         }
21212         
21213         this.startValue = this.getValue();
21214         
21215         if(this.groupId){
21216             Roo.bootstrap.CheckBox.register(this);
21217         }
21218     },
21219     
21220     onClick : function(e)
21221     {   
21222         if(this.fireEvent('click', this, e) !== false){
21223             this.setChecked(!this.checked);
21224         }
21225         
21226     },
21227     
21228     setChecked : function(state,suppressEvent)
21229     {
21230         this.startValue = this.getValue();
21231
21232         if(this.inputType == 'radio'){
21233             
21234             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21235                 e.dom.checked = false;
21236             });
21237             
21238             this.inputEl().dom.checked = true;
21239             
21240             this.inputEl().dom.value = this.inputValue;
21241             
21242             if(suppressEvent !== true){
21243                 this.fireEvent('check', this, true);
21244             }
21245             
21246             this.validate();
21247             
21248             return;
21249         }
21250         
21251         this.checked = state;
21252         
21253         this.inputEl().dom.checked = state;
21254         
21255         
21256         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21257         
21258         if(suppressEvent !== true){
21259             this.fireEvent('check', this, state);
21260         }
21261         
21262         this.validate();
21263     },
21264     
21265     getValue : function()
21266     {
21267         if(this.inputType == 'radio'){
21268             return this.getGroupValue();
21269         }
21270         
21271         return this.hiddenEl().dom.value;
21272         
21273     },
21274     
21275     getGroupValue : function()
21276     {
21277         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21278             return '';
21279         }
21280         
21281         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21282     },
21283     
21284     setValue : function(v,suppressEvent)
21285     {
21286         if(this.inputType == 'radio'){
21287             this.setGroupValue(v, suppressEvent);
21288             return;
21289         }
21290         
21291         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21292         
21293         this.validate();
21294     },
21295     
21296     setGroupValue : function(v, suppressEvent)
21297     {
21298         this.startValue = this.getValue();
21299         
21300         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21301             e.dom.checked = false;
21302             
21303             if(e.dom.value == v){
21304                 e.dom.checked = true;
21305             }
21306         });
21307         
21308         if(suppressEvent !== true){
21309             this.fireEvent('check', this, true);
21310         }
21311
21312         this.validate();
21313         
21314         return;
21315     },
21316     
21317     validate : function()
21318     {
21319         if(this.getVisibilityEl().hasClass('hidden')){
21320             return true;
21321         }
21322         
21323         if(
21324                 this.disabled || 
21325                 (this.inputType == 'radio' && this.validateRadio()) ||
21326                 (this.inputType == 'checkbox' && this.validateCheckbox())
21327         ){
21328             this.markValid();
21329             return true;
21330         }
21331         
21332         this.markInvalid();
21333         return false;
21334     },
21335     
21336     validateRadio : function()
21337     {
21338         if(this.getVisibilityEl().hasClass('hidden')){
21339             return true;
21340         }
21341         
21342         if(this.allowBlank){
21343             return true;
21344         }
21345         
21346         var valid = false;
21347         
21348         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21349             if(!e.dom.checked){
21350                 return;
21351             }
21352             
21353             valid = true;
21354             
21355             return false;
21356         });
21357         
21358         return valid;
21359     },
21360     
21361     validateCheckbox : function()
21362     {
21363         if(!this.groupId){
21364             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21365             //return (this.getValue() == this.inputValue) ? true : false;
21366         }
21367         
21368         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21369         
21370         if(!group){
21371             return false;
21372         }
21373         
21374         var r = false;
21375         
21376         for(var i in group){
21377             if(group[i].el.isVisible(true)){
21378                 r = false;
21379                 break;
21380             }
21381             
21382             r = true;
21383         }
21384         
21385         for(var i in group){
21386             if(r){
21387                 break;
21388             }
21389             
21390             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21391         }
21392         
21393         return r;
21394     },
21395     
21396     /**
21397      * Mark this field as valid
21398      */
21399     markValid : function()
21400     {
21401         var _this = this;
21402         
21403         this.fireEvent('valid', this);
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.markValid();
21413         }
21414
21415         if(this.inputType == 'radio'){
21416             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21417                 var fg = e.findParent('.form-group', false, true);
21418                 if (Roo.bootstrap.version == 3) {
21419                     fg.removeClass([_this.invalidClass, _this.validClass]);
21420                     fg.addClass(_this.validClass);
21421                 } else {
21422                     fg.removeClass(['is-valid', 'is-invalid']);
21423                     fg.addClass('is-valid');
21424                 }
21425             });
21426             
21427             return;
21428         }
21429
21430         if(!this.groupId){
21431             var fg = this.el.findParent('.form-group', false, true);
21432             if (Roo.bootstrap.version == 3) {
21433                 fg.removeClass([this.invalidClass, this.validClass]);
21434                 fg.addClass(this.validClass);
21435             } else {
21436                 fg.removeClass(['is-valid', 'is-invalid']);
21437                 fg.addClass('is-valid');
21438             }
21439             return;
21440         }
21441         
21442         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21443         
21444         if(!group){
21445             return;
21446         }
21447         
21448         for(var i in group){
21449             var fg = group[i].el.findParent('.form-group', false, true);
21450             if (Roo.bootstrap.version == 3) {
21451                 fg.removeClass([this.invalidClass, this.validClass]);
21452                 fg.addClass(this.validClass);
21453             } else {
21454                 fg.removeClass(['is-valid', 'is-invalid']);
21455                 fg.addClass('is-valid');
21456             }
21457         }
21458     },
21459     
21460      /**
21461      * Mark this field as invalid
21462      * @param {String} msg The validation message
21463      */
21464     markInvalid : function(msg)
21465     {
21466         if(this.allowBlank){
21467             return;
21468         }
21469         
21470         var _this = this;
21471         
21472         this.fireEvent('invalid', this, msg);
21473         
21474         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21475         
21476         if(this.groupId){
21477             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21478         }
21479         
21480         if(label){
21481             label.markInvalid();
21482         }
21483             
21484         if(this.inputType == 'radio'){
21485             
21486             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21487                 var fg = e.findParent('.form-group', false, true);
21488                 if (Roo.bootstrap.version == 3) {
21489                     fg.removeClass([_this.invalidClass, _this.validClass]);
21490                     fg.addClass(_this.invalidClass);
21491                 } else {
21492                     fg.removeClass(['is-invalid', 'is-valid']);
21493                     fg.addClass('is-invalid');
21494                 }
21495             });
21496             
21497             return;
21498         }
21499         
21500         if(!this.groupId){
21501             var fg = this.el.findParent('.form-group', false, true);
21502             if (Roo.bootstrap.version == 3) {
21503                 fg.removeClass([_this.invalidClass, _this.validClass]);
21504                 fg.addClass(_this.invalidClass);
21505             } else {
21506                 fg.removeClass(['is-invalid', 'is-valid']);
21507                 fg.addClass('is-invalid');
21508             }
21509             return;
21510         }
21511         
21512         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21513         
21514         if(!group){
21515             return;
21516         }
21517         
21518         for(var i in group){
21519             var fg = group[i].el.findParent('.form-group', false, true);
21520             if (Roo.bootstrap.version == 3) {
21521                 fg.removeClass([_this.invalidClass, _this.validClass]);
21522                 fg.addClass(_this.invalidClass);
21523             } else {
21524                 fg.removeClass(['is-invalid', 'is-valid']);
21525                 fg.addClass('is-invalid');
21526             }
21527         }
21528         
21529     },
21530     
21531     clearInvalid : function()
21532     {
21533         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21534         
21535         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21536         
21537         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21538         
21539         if (label && label.iconEl) {
21540             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21541             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21542         }
21543     },
21544     
21545     disable : function()
21546     {
21547         if(this.inputType != 'radio'){
21548             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21549             return;
21550         }
21551         
21552         var _this = this;
21553         
21554         if(this.rendered){
21555             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21556                 _this.getActionEl().addClass(this.disabledClass);
21557                 e.dom.disabled = true;
21558             });
21559         }
21560         
21561         this.disabled = true;
21562         this.fireEvent("disable", this);
21563         return this;
21564     },
21565
21566     enable : function()
21567     {
21568         if(this.inputType != 'radio'){
21569             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21570             return;
21571         }
21572         
21573         var _this = this;
21574         
21575         if(this.rendered){
21576             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21577                 _this.getActionEl().removeClass(this.disabledClass);
21578                 e.dom.disabled = false;
21579             });
21580         }
21581         
21582         this.disabled = false;
21583         this.fireEvent("enable", this);
21584         return this;
21585     },
21586     
21587     setBoxLabel : function(v)
21588     {
21589         this.boxLabel = v;
21590         
21591         if(this.rendered){
21592             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21593         }
21594     }
21595
21596 });
21597
21598 Roo.apply(Roo.bootstrap.CheckBox, {
21599     
21600     groups: {},
21601     
21602      /**
21603     * register a CheckBox Group
21604     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21605     */
21606     register : function(checkbox)
21607     {
21608         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21609             this.groups[checkbox.groupId] = {};
21610         }
21611         
21612         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21613             return;
21614         }
21615         
21616         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21617         
21618     },
21619     /**
21620     * fetch a CheckBox Group based on the group ID
21621     * @param {string} the group ID
21622     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21623     */
21624     get: function(groupId) {
21625         if (typeof(this.groups[groupId]) == 'undefined') {
21626             return false;
21627         }
21628         
21629         return this.groups[groupId] ;
21630     }
21631     
21632     
21633 });
21634 /*
21635  * - LGPL
21636  *
21637  * RadioItem
21638  * 
21639  */
21640
21641 /**
21642  * @class Roo.bootstrap.Radio
21643  * @extends Roo.bootstrap.Component
21644  * Bootstrap Radio class
21645  * @cfg {String} boxLabel - the label associated
21646  * @cfg {String} value - the value of radio
21647  * 
21648  * @constructor
21649  * Create a new Radio
21650  * @param {Object} config The config object
21651  */
21652 Roo.bootstrap.Radio = function(config){
21653     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21654     
21655 };
21656
21657 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21658     
21659     boxLabel : '',
21660     
21661     value : '',
21662     
21663     getAutoCreate : function()
21664     {
21665         var cfg = {
21666             tag : 'div',
21667             cls : 'form-group radio',
21668             cn : [
21669                 {
21670                     tag : 'label',
21671                     cls : 'box-label',
21672                     html : this.boxLabel
21673                 }
21674             ]
21675         };
21676         
21677         return cfg;
21678     },
21679     
21680     initEvents : function() 
21681     {
21682         this.parent().register(this);
21683         
21684         this.el.on('click', this.onClick, this);
21685         
21686     },
21687     
21688     onClick : function(e)
21689     {
21690         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21691             this.setChecked(true);
21692         }
21693     },
21694     
21695     setChecked : function(state, suppressEvent)
21696     {
21697         this.parent().setValue(this.value, suppressEvent);
21698         
21699     },
21700     
21701     setBoxLabel : function(v)
21702     {
21703         this.boxLabel = v;
21704         
21705         if(this.rendered){
21706             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21707         }
21708     }
21709     
21710 });
21711  
21712
21713  /*
21714  * - LGPL
21715  *
21716  * Input
21717  * 
21718  */
21719
21720 /**
21721  * @class Roo.bootstrap.SecurePass
21722  * @extends Roo.bootstrap.Input
21723  * Bootstrap SecurePass class
21724  *
21725  * 
21726  * @constructor
21727  * Create a new SecurePass
21728  * @param {Object} config The config object
21729  */
21730  
21731 Roo.bootstrap.SecurePass = function (config) {
21732     // these go here, so the translation tool can replace them..
21733     this.errors = {
21734         PwdEmpty: "Please type a password, and then retype it to confirm.",
21735         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21736         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21737         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21738         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21739         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21740         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21741         TooWeak: "Your password is Too Weak."
21742     },
21743     this.meterLabel = "Password strength:";
21744     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21745     this.meterClass = [
21746         "roo-password-meter-tooweak", 
21747         "roo-password-meter-weak", 
21748         "roo-password-meter-medium", 
21749         "roo-password-meter-strong", 
21750         "roo-password-meter-grey"
21751     ];
21752     
21753     this.errors = {};
21754     
21755     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21756 }
21757
21758 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21759     /**
21760      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21761      * {
21762      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21763      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21764      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21765      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21766      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21767      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21768      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21769      * })
21770      */
21771     // private
21772     
21773     meterWidth: 300,
21774     errorMsg :'',    
21775     errors: false,
21776     imageRoot: '/',
21777     /**
21778      * @cfg {String/Object} Label for the strength meter (defaults to
21779      * 'Password strength:')
21780      */
21781     // private
21782     meterLabel: '',
21783     /**
21784      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21785      * ['Weak', 'Medium', 'Strong'])
21786      */
21787     // private    
21788     pwdStrengths: false,    
21789     // private
21790     strength: 0,
21791     // private
21792     _lastPwd: null,
21793     // private
21794     kCapitalLetter: 0,
21795     kSmallLetter: 1,
21796     kDigit: 2,
21797     kPunctuation: 3,
21798     
21799     insecure: false,
21800     // private
21801     initEvents: function ()
21802     {
21803         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21804
21805         if (this.el.is('input[type=password]') && Roo.isSafari) {
21806             this.el.on('keydown', this.SafariOnKeyDown, this);
21807         }
21808
21809         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21810     },
21811     // private
21812     onRender: function (ct, position)
21813     {
21814         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21815         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21816         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21817
21818         this.trigger.createChild({
21819                    cn: [
21820                     {
21821                     //id: 'PwdMeter',
21822                     tag: 'div',
21823                     cls: 'roo-password-meter-grey col-xs-12',
21824                     style: {
21825                         //width: 0,
21826                         //width: this.meterWidth + 'px'                                                
21827                         }
21828                     },
21829                     {                            
21830                          cls: 'roo-password-meter-text'                          
21831                     }
21832                 ]            
21833         });
21834
21835          
21836         if (this.hideTrigger) {
21837             this.trigger.setDisplayed(false);
21838         }
21839         this.setSize(this.width || '', this.height || '');
21840     },
21841     // private
21842     onDestroy: function ()
21843     {
21844         if (this.trigger) {
21845             this.trigger.removeAllListeners();
21846             this.trigger.remove();
21847         }
21848         if (this.wrap) {
21849             this.wrap.remove();
21850         }
21851         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21852     },
21853     // private
21854     checkStrength: function ()
21855     {
21856         var pwd = this.inputEl().getValue();
21857         if (pwd == this._lastPwd) {
21858             return;
21859         }
21860
21861         var strength;
21862         if (this.ClientSideStrongPassword(pwd)) {
21863             strength = 3;
21864         } else if (this.ClientSideMediumPassword(pwd)) {
21865             strength = 2;
21866         } else if (this.ClientSideWeakPassword(pwd)) {
21867             strength = 1;
21868         } else {
21869             strength = 0;
21870         }
21871         
21872         Roo.log('strength1: ' + strength);
21873         
21874         //var pm = this.trigger.child('div/div/div').dom;
21875         var pm = this.trigger.child('div/div');
21876         pm.removeClass(this.meterClass);
21877         pm.addClass(this.meterClass[strength]);
21878                 
21879         
21880         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21881                 
21882         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21883         
21884         this._lastPwd = pwd;
21885     },
21886     reset: function ()
21887     {
21888         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21889         
21890         this._lastPwd = '';
21891         
21892         var pm = this.trigger.child('div/div');
21893         pm.removeClass(this.meterClass);
21894         pm.addClass('roo-password-meter-grey');        
21895         
21896         
21897         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21898         
21899         pt.innerHTML = '';
21900         this.inputEl().dom.type='password';
21901     },
21902     // private
21903     validateValue: function (value)
21904     {
21905         
21906         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21907             return false;
21908         }
21909         if (value.length == 0) {
21910             if (this.allowBlank) {
21911                 this.clearInvalid();
21912                 return true;
21913             }
21914
21915             this.markInvalid(this.errors.PwdEmpty);
21916             this.errorMsg = this.errors.PwdEmpty;
21917             return false;
21918         }
21919         
21920         if(this.insecure){
21921             return true;
21922         }
21923         
21924         if ('[\x21-\x7e]*'.match(value)) {
21925             this.markInvalid(this.errors.PwdBadChar);
21926             this.errorMsg = this.errors.PwdBadChar;
21927             return false;
21928         }
21929         if (value.length < 6) {
21930             this.markInvalid(this.errors.PwdShort);
21931             this.errorMsg = this.errors.PwdShort;
21932             return false;
21933         }
21934         if (value.length > 16) {
21935             this.markInvalid(this.errors.PwdLong);
21936             this.errorMsg = this.errors.PwdLong;
21937             return false;
21938         }
21939         var strength;
21940         if (this.ClientSideStrongPassword(value)) {
21941             strength = 3;
21942         } else if (this.ClientSideMediumPassword(value)) {
21943             strength = 2;
21944         } else if (this.ClientSideWeakPassword(value)) {
21945             strength = 1;
21946         } else {
21947             strength = 0;
21948         }
21949
21950         
21951         if (strength < 2) {
21952             //this.markInvalid(this.errors.TooWeak);
21953             this.errorMsg = this.errors.TooWeak;
21954             //return false;
21955         }
21956         
21957         
21958         console.log('strength2: ' + strength);
21959         
21960         //var pm = this.trigger.child('div/div/div').dom;
21961         
21962         var pm = this.trigger.child('div/div');
21963         pm.removeClass(this.meterClass);
21964         pm.addClass(this.meterClass[strength]);
21965                 
21966         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21967                 
21968         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21969         
21970         this.errorMsg = ''; 
21971         return true;
21972     },
21973     // private
21974     CharacterSetChecks: function (type)
21975     {
21976         this.type = type;
21977         this.fResult = false;
21978     },
21979     // private
21980     isctype: function (character, type)
21981     {
21982         switch (type) {  
21983             case this.kCapitalLetter:
21984                 if (character >= 'A' && character <= 'Z') {
21985                     return true;
21986                 }
21987                 break;
21988             
21989             case this.kSmallLetter:
21990                 if (character >= 'a' && character <= 'z') {
21991                     return true;
21992                 }
21993                 break;
21994             
21995             case this.kDigit:
21996                 if (character >= '0' && character <= '9') {
21997                     return true;
21998                 }
21999                 break;
22000             
22001             case this.kPunctuation:
22002                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22003                     return true;
22004                 }
22005                 break;
22006             
22007             default:
22008                 return false;
22009         }
22010
22011     },
22012     // private
22013     IsLongEnough: function (pwd, size)
22014     {
22015         return !(pwd == null || isNaN(size) || pwd.length < size);
22016     },
22017     // private
22018     SpansEnoughCharacterSets: function (word, nb)
22019     {
22020         if (!this.IsLongEnough(word, nb))
22021         {
22022             return false;
22023         }
22024
22025         var characterSetChecks = new Array(
22026             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22027             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22028         );
22029         
22030         for (var index = 0; index < word.length; ++index) {
22031             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22032                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22033                     characterSetChecks[nCharSet].fResult = true;
22034                     break;
22035                 }
22036             }
22037         }
22038
22039         var nCharSets = 0;
22040         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22041             if (characterSetChecks[nCharSet].fResult) {
22042                 ++nCharSets;
22043             }
22044         }
22045
22046         if (nCharSets < nb) {
22047             return false;
22048         }
22049         return true;
22050     },
22051     // private
22052     ClientSideStrongPassword: function (pwd)
22053     {
22054         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22055     },
22056     // private
22057     ClientSideMediumPassword: function (pwd)
22058     {
22059         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22060     },
22061     // private
22062     ClientSideWeakPassword: function (pwd)
22063     {
22064         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22065     }
22066           
22067 })//<script type="text/javascript">
22068
22069 /*
22070  * Based  Ext JS Library 1.1.1
22071  * Copyright(c) 2006-2007, Ext JS, LLC.
22072  * LGPL
22073  *
22074  */
22075  
22076 /**
22077  * @class Roo.HtmlEditorCore
22078  * @extends Roo.Component
22079  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22080  *
22081  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22082  */
22083
22084 Roo.HtmlEditorCore = function(config){
22085     
22086     
22087     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22088     
22089     
22090     this.addEvents({
22091         /**
22092          * @event initialize
22093          * Fires when the editor is fully initialized (including the iframe)
22094          * @param {Roo.HtmlEditorCore} this
22095          */
22096         initialize: true,
22097         /**
22098          * @event activate
22099          * Fires when the editor is first receives the focus. Any insertion must wait
22100          * until after this event.
22101          * @param {Roo.HtmlEditorCore} this
22102          */
22103         activate: true,
22104          /**
22105          * @event beforesync
22106          * Fires before the textarea is updated with content from the editor iframe. Return false
22107          * to cancel the sync.
22108          * @param {Roo.HtmlEditorCore} this
22109          * @param {String} html
22110          */
22111         beforesync: true,
22112          /**
22113          * @event beforepush
22114          * Fires before the iframe editor is updated with content from the textarea. Return false
22115          * to cancel the push.
22116          * @param {Roo.HtmlEditorCore} this
22117          * @param {String} html
22118          */
22119         beforepush: true,
22120          /**
22121          * @event sync
22122          * Fires when the textarea is updated with content from the editor iframe.
22123          * @param {Roo.HtmlEditorCore} this
22124          * @param {String} html
22125          */
22126         sync: true,
22127          /**
22128          * @event push
22129          * Fires when the iframe editor is updated with content from the textarea.
22130          * @param {Roo.HtmlEditorCore} this
22131          * @param {String} html
22132          */
22133         push: true,
22134         
22135         /**
22136          * @event editorevent
22137          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22138          * @param {Roo.HtmlEditorCore} this
22139          */
22140         editorevent: true
22141         
22142     });
22143     
22144     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22145     
22146     // defaults : white / black...
22147     this.applyBlacklists();
22148     
22149     
22150     
22151 };
22152
22153
22154 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22155
22156
22157      /**
22158      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22159      */
22160     
22161     owner : false,
22162     
22163      /**
22164      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22165      *                        Roo.resizable.
22166      */
22167     resizable : false,
22168      /**
22169      * @cfg {Number} height (in pixels)
22170      */   
22171     height: 300,
22172    /**
22173      * @cfg {Number} width (in pixels)
22174      */   
22175     width: 500,
22176     
22177     /**
22178      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22179      * 
22180      */
22181     stylesheets: false,
22182     
22183     // id of frame..
22184     frameId: false,
22185     
22186     // private properties
22187     validationEvent : false,
22188     deferHeight: true,
22189     initialized : false,
22190     activated : false,
22191     sourceEditMode : false,
22192     onFocus : Roo.emptyFn,
22193     iframePad:3,
22194     hideMode:'offsets',
22195     
22196     clearUp: true,
22197     
22198     // blacklist + whitelisted elements..
22199     black: false,
22200     white: false,
22201      
22202     bodyCls : '',
22203
22204     /**
22205      * Protected method that will not generally be called directly. It
22206      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22207      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22208      */
22209     getDocMarkup : function(){
22210         // body styles..
22211         var st = '';
22212         
22213         // inherit styels from page...?? 
22214         if (this.stylesheets === false) {
22215             
22216             Roo.get(document.head).select('style').each(function(node) {
22217                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22218             });
22219             
22220             Roo.get(document.head).select('link').each(function(node) { 
22221                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22222             });
22223             
22224         } else if (!this.stylesheets.length) {
22225                 // simple..
22226                 st = '<style type="text/css">' +
22227                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22228                    '</style>';
22229         } else { 
22230             st = '<style type="text/css">' +
22231                     this.stylesheets +
22232                 '</style>';
22233         }
22234         
22235         st +=  '<style type="text/css">' +
22236             'IMG { cursor: pointer } ' +
22237         '</style>';
22238
22239         var cls = 'roo-htmleditor-body';
22240         
22241         if(this.bodyCls.length){
22242             cls += ' ' + this.bodyCls;
22243         }
22244         
22245         return '<html><head>' + st  +
22246             //<style type="text/css">' +
22247             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22248             //'</style>' +
22249             ' </head><body class="' +  cls + '"></body></html>';
22250     },
22251
22252     // private
22253     onRender : function(ct, position)
22254     {
22255         var _t = this;
22256         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22257         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22258         
22259         
22260         this.el.dom.style.border = '0 none';
22261         this.el.dom.setAttribute('tabIndex', -1);
22262         this.el.addClass('x-hidden hide');
22263         
22264         
22265         
22266         if(Roo.isIE){ // fix IE 1px bogus margin
22267             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22268         }
22269        
22270         
22271         this.frameId = Roo.id();
22272         
22273          
22274         
22275         var iframe = this.owner.wrap.createChild({
22276             tag: 'iframe',
22277             cls: 'form-control', // bootstrap..
22278             id: this.frameId,
22279             name: this.frameId,
22280             frameBorder : 'no',
22281             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22282         }, this.el
22283         );
22284         
22285         
22286         this.iframe = iframe.dom;
22287
22288          this.assignDocWin();
22289         
22290         this.doc.designMode = 'on';
22291        
22292         this.doc.open();
22293         this.doc.write(this.getDocMarkup());
22294         this.doc.close();
22295
22296         
22297         var task = { // must defer to wait for browser to be ready
22298             run : function(){
22299                 //console.log("run task?" + this.doc.readyState);
22300                 this.assignDocWin();
22301                 if(this.doc.body || this.doc.readyState == 'complete'){
22302                     try {
22303                         this.doc.designMode="on";
22304                     } catch (e) {
22305                         return;
22306                     }
22307                     Roo.TaskMgr.stop(task);
22308                     this.initEditor.defer(10, this);
22309                 }
22310             },
22311             interval : 10,
22312             duration: 10000,
22313             scope: this
22314         };
22315         Roo.TaskMgr.start(task);
22316
22317     },
22318
22319     // private
22320     onResize : function(w, h)
22321     {
22322          Roo.log('resize: ' +w + ',' + h );
22323         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22324         if(!this.iframe){
22325             return;
22326         }
22327         if(typeof w == 'number'){
22328             
22329             this.iframe.style.width = w + 'px';
22330         }
22331         if(typeof h == 'number'){
22332             
22333             this.iframe.style.height = h + 'px';
22334             if(this.doc){
22335                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22336             }
22337         }
22338         
22339     },
22340
22341     /**
22342      * Toggles the editor between standard and source edit mode.
22343      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22344      */
22345     toggleSourceEdit : function(sourceEditMode){
22346         
22347         this.sourceEditMode = sourceEditMode === true;
22348         
22349         if(this.sourceEditMode){
22350  
22351             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22352             
22353         }else{
22354             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22355             //this.iframe.className = '';
22356             this.deferFocus();
22357         }
22358         //this.setSize(this.owner.wrap.getSize());
22359         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22360     },
22361
22362     
22363   
22364
22365     /**
22366      * Protected method that will not generally be called directly. If you need/want
22367      * custom HTML cleanup, this is the method you should override.
22368      * @param {String} html The HTML to be cleaned
22369      * return {String} The cleaned HTML
22370      */
22371     cleanHtml : function(html){
22372         html = String(html);
22373         if(html.length > 5){
22374             if(Roo.isSafari){ // strip safari nonsense
22375                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22376             }
22377         }
22378         if(html == '&nbsp;'){
22379             html = '';
22380         }
22381         return html;
22382     },
22383
22384     /**
22385      * HTML Editor -> Textarea
22386      * Protected method that will not generally be called directly. Syncs the contents
22387      * of the editor iframe with the textarea.
22388      */
22389     syncValue : function(){
22390         if(this.initialized){
22391             var bd = (this.doc.body || this.doc.documentElement);
22392             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22393             var html = bd.innerHTML;
22394             if(Roo.isSafari){
22395                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22396                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22397                 if(m && m[1]){
22398                     html = '<div style="'+m[0]+'">' + html + '</div>';
22399                 }
22400             }
22401             html = this.cleanHtml(html);
22402             // fix up the special chars.. normaly like back quotes in word...
22403             // however we do not want to do this with chinese..
22404             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22405                 var cc = b.charCodeAt();
22406                 if (
22407                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22408                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22409                     (cc >= 0xf900 && cc < 0xfb00 )
22410                 ) {
22411                         return b;
22412                 }
22413                 return "&#"+cc+";" 
22414             });
22415             if(this.owner.fireEvent('beforesync', this, html) !== false){
22416                 this.el.dom.value = html;
22417                 this.owner.fireEvent('sync', this, html);
22418             }
22419         }
22420     },
22421
22422     /**
22423      * Protected method that will not generally be called directly. Pushes the value of the textarea
22424      * into the iframe editor.
22425      */
22426     pushValue : function(){
22427         if(this.initialized){
22428             var v = this.el.dom.value.trim();
22429             
22430 //            if(v.length < 1){
22431 //                v = '&#160;';
22432 //            }
22433             
22434             if(this.owner.fireEvent('beforepush', this, v) !== false){
22435                 var d = (this.doc.body || this.doc.documentElement);
22436                 d.innerHTML = v;
22437                 this.cleanUpPaste();
22438                 this.el.dom.value = d.innerHTML;
22439                 this.owner.fireEvent('push', this, v);
22440             }
22441         }
22442     },
22443
22444     // private
22445     deferFocus : function(){
22446         this.focus.defer(10, this);
22447     },
22448
22449     // doc'ed in Field
22450     focus : function(){
22451         if(this.win && !this.sourceEditMode){
22452             this.win.focus();
22453         }else{
22454             this.el.focus();
22455         }
22456     },
22457     
22458     assignDocWin: function()
22459     {
22460         var iframe = this.iframe;
22461         
22462          if(Roo.isIE){
22463             this.doc = iframe.contentWindow.document;
22464             this.win = iframe.contentWindow;
22465         } else {
22466 //            if (!Roo.get(this.frameId)) {
22467 //                return;
22468 //            }
22469 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22470 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22471             
22472             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22473                 return;
22474             }
22475             
22476             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22477             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22478         }
22479     },
22480     
22481     // private
22482     initEditor : function(){
22483         //console.log("INIT EDITOR");
22484         this.assignDocWin();
22485         
22486         
22487         
22488         this.doc.designMode="on";
22489         this.doc.open();
22490         this.doc.write(this.getDocMarkup());
22491         this.doc.close();
22492         
22493         var dbody = (this.doc.body || this.doc.documentElement);
22494         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22495         // this copies styles from the containing element into thsi one..
22496         // not sure why we need all of this..
22497         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22498         
22499         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22500         //ss['background-attachment'] = 'fixed'; // w3c
22501         dbody.bgProperties = 'fixed'; // ie
22502         //Roo.DomHelper.applyStyles(dbody, ss);
22503         Roo.EventManager.on(this.doc, {
22504             //'mousedown': this.onEditorEvent,
22505             'mouseup': this.onEditorEvent,
22506             'dblclick': this.onEditorEvent,
22507             'click': this.onEditorEvent,
22508             'keyup': this.onEditorEvent,
22509             buffer:100,
22510             scope: this
22511         });
22512         if(Roo.isGecko){
22513             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22514         }
22515         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22516             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22517         }
22518         this.initialized = true;
22519
22520         this.owner.fireEvent('initialize', this);
22521         this.pushValue();
22522     },
22523
22524     // private
22525     onDestroy : function(){
22526         
22527         
22528         
22529         if(this.rendered){
22530             
22531             //for (var i =0; i < this.toolbars.length;i++) {
22532             //    // fixme - ask toolbars for heights?
22533             //    this.toolbars[i].onDestroy();
22534            // }
22535             
22536             //this.wrap.dom.innerHTML = '';
22537             //this.wrap.remove();
22538         }
22539     },
22540
22541     // private
22542     onFirstFocus : function(){
22543         
22544         this.assignDocWin();
22545         
22546         
22547         this.activated = true;
22548          
22549     
22550         if(Roo.isGecko){ // prevent silly gecko errors
22551             this.win.focus();
22552             var s = this.win.getSelection();
22553             if(!s.focusNode || s.focusNode.nodeType != 3){
22554                 var r = s.getRangeAt(0);
22555                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22556                 r.collapse(true);
22557                 this.deferFocus();
22558             }
22559             try{
22560                 this.execCmd('useCSS', true);
22561                 this.execCmd('styleWithCSS', false);
22562             }catch(e){}
22563         }
22564         this.owner.fireEvent('activate', this);
22565     },
22566
22567     // private
22568     adjustFont: function(btn){
22569         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22570         //if(Roo.isSafari){ // safari
22571         //    adjust *= 2;
22572        // }
22573         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22574         if(Roo.isSafari){ // safari
22575             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22576             v =  (v < 10) ? 10 : v;
22577             v =  (v > 48) ? 48 : v;
22578             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22579             
22580         }
22581         
22582         
22583         v = Math.max(1, v+adjust);
22584         
22585         this.execCmd('FontSize', v  );
22586     },
22587
22588     onEditorEvent : function(e)
22589     {
22590         this.owner.fireEvent('editorevent', this, e);
22591       //  this.updateToolbar();
22592         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22593     },
22594
22595     insertTag : function(tg)
22596     {
22597         // could be a bit smarter... -> wrap the current selected tRoo..
22598         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22599             
22600             range = this.createRange(this.getSelection());
22601             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22602             wrappingNode.appendChild(range.extractContents());
22603             range.insertNode(wrappingNode);
22604
22605             return;
22606             
22607             
22608             
22609         }
22610         this.execCmd("formatblock",   tg);
22611         
22612     },
22613     
22614     insertText : function(txt)
22615     {
22616         
22617         
22618         var range = this.createRange();
22619         range.deleteContents();
22620                //alert(Sender.getAttribute('label'));
22621                
22622         range.insertNode(this.doc.createTextNode(txt));
22623     } ,
22624     
22625      
22626
22627     /**
22628      * Executes a Midas editor command on the editor document and performs necessary focus and
22629      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22630      * @param {String} cmd The Midas command
22631      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22632      */
22633     relayCmd : function(cmd, value){
22634         this.win.focus();
22635         this.execCmd(cmd, value);
22636         this.owner.fireEvent('editorevent', this);
22637         //this.updateToolbar();
22638         this.owner.deferFocus();
22639     },
22640
22641     /**
22642      * Executes a Midas editor command directly on the editor document.
22643      * For visual commands, you should use {@link #relayCmd} instead.
22644      * <b>This should only be called after the editor is initialized.</b>
22645      * @param {String} cmd The Midas command
22646      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22647      */
22648     execCmd : function(cmd, value){
22649         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22650         this.syncValue();
22651     },
22652  
22653  
22654    
22655     /**
22656      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22657      * to insert tRoo.
22658      * @param {String} text | dom node.. 
22659      */
22660     insertAtCursor : function(text)
22661     {
22662         
22663         if(!this.activated){
22664             return;
22665         }
22666         /*
22667         if(Roo.isIE){
22668             this.win.focus();
22669             var r = this.doc.selection.createRange();
22670             if(r){
22671                 r.collapse(true);
22672                 r.pasteHTML(text);
22673                 this.syncValue();
22674                 this.deferFocus();
22675             
22676             }
22677             return;
22678         }
22679         */
22680         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22681             this.win.focus();
22682             
22683             
22684             // from jquery ui (MIT licenced)
22685             var range, node;
22686             var win = this.win;
22687             
22688             if (win.getSelection && win.getSelection().getRangeAt) {
22689                 range = win.getSelection().getRangeAt(0);
22690                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22691                 range.insertNode(node);
22692             } else if (win.document.selection && win.document.selection.createRange) {
22693                 // no firefox support
22694                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22695                 win.document.selection.createRange().pasteHTML(txt);
22696             } else {
22697                 // no firefox support
22698                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22699                 this.execCmd('InsertHTML', txt);
22700             } 
22701             
22702             this.syncValue();
22703             
22704             this.deferFocus();
22705         }
22706     },
22707  // private
22708     mozKeyPress : function(e){
22709         if(e.ctrlKey){
22710             var c = e.getCharCode(), cmd;
22711           
22712             if(c > 0){
22713                 c = String.fromCharCode(c).toLowerCase();
22714                 switch(c){
22715                     case 'b':
22716                         cmd = 'bold';
22717                         break;
22718                     case 'i':
22719                         cmd = 'italic';
22720                         break;
22721                     
22722                     case 'u':
22723                         cmd = 'underline';
22724                         break;
22725                     
22726                     case 'v':
22727                         this.cleanUpPaste.defer(100, this);
22728                         return;
22729                         
22730                 }
22731                 if(cmd){
22732                     this.win.focus();
22733                     this.execCmd(cmd);
22734                     this.deferFocus();
22735                     e.preventDefault();
22736                 }
22737                 
22738             }
22739         }
22740     },
22741
22742     // private
22743     fixKeys : function(){ // load time branching for fastest keydown performance
22744         if(Roo.isIE){
22745             return function(e){
22746                 var k = e.getKey(), r;
22747                 if(k == e.TAB){
22748                     e.stopEvent();
22749                     r = this.doc.selection.createRange();
22750                     if(r){
22751                         r.collapse(true);
22752                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22753                         this.deferFocus();
22754                     }
22755                     return;
22756                 }
22757                 
22758                 if(k == e.ENTER){
22759                     r = this.doc.selection.createRange();
22760                     if(r){
22761                         var target = r.parentElement();
22762                         if(!target || target.tagName.toLowerCase() != 'li'){
22763                             e.stopEvent();
22764                             r.pasteHTML('<br />');
22765                             r.collapse(false);
22766                             r.select();
22767                         }
22768                     }
22769                 }
22770                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22771                     this.cleanUpPaste.defer(100, this);
22772                     return;
22773                 }
22774                 
22775                 
22776             };
22777         }else if(Roo.isOpera){
22778             return function(e){
22779                 var k = e.getKey();
22780                 if(k == e.TAB){
22781                     e.stopEvent();
22782                     this.win.focus();
22783                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22784                     this.deferFocus();
22785                 }
22786                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22787                     this.cleanUpPaste.defer(100, this);
22788                     return;
22789                 }
22790                 
22791             };
22792         }else if(Roo.isSafari){
22793             return function(e){
22794                 var k = e.getKey();
22795                 
22796                 if(k == e.TAB){
22797                     e.stopEvent();
22798                     this.execCmd('InsertText','\t');
22799                     this.deferFocus();
22800                     return;
22801                 }
22802                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22803                     this.cleanUpPaste.defer(100, this);
22804                     return;
22805                 }
22806                 
22807              };
22808         }
22809     }(),
22810     
22811     getAllAncestors: function()
22812     {
22813         var p = this.getSelectedNode();
22814         var a = [];
22815         if (!p) {
22816             a.push(p); // push blank onto stack..
22817             p = this.getParentElement();
22818         }
22819         
22820         
22821         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22822             a.push(p);
22823             p = p.parentNode;
22824         }
22825         a.push(this.doc.body);
22826         return a;
22827     },
22828     lastSel : false,
22829     lastSelNode : false,
22830     
22831     
22832     getSelection : function() 
22833     {
22834         this.assignDocWin();
22835         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22836     },
22837     
22838     getSelectedNode: function() 
22839     {
22840         // this may only work on Gecko!!!
22841         
22842         // should we cache this!!!!
22843         
22844         
22845         
22846          
22847         var range = this.createRange(this.getSelection()).cloneRange();
22848         
22849         if (Roo.isIE) {
22850             var parent = range.parentElement();
22851             while (true) {
22852                 var testRange = range.duplicate();
22853                 testRange.moveToElementText(parent);
22854                 if (testRange.inRange(range)) {
22855                     break;
22856                 }
22857                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22858                     break;
22859                 }
22860                 parent = parent.parentElement;
22861             }
22862             return parent;
22863         }
22864         
22865         // is ancestor a text element.
22866         var ac =  range.commonAncestorContainer;
22867         if (ac.nodeType == 3) {
22868             ac = ac.parentNode;
22869         }
22870         
22871         var ar = ac.childNodes;
22872          
22873         var nodes = [];
22874         var other_nodes = [];
22875         var has_other_nodes = false;
22876         for (var i=0;i<ar.length;i++) {
22877             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22878                 continue;
22879             }
22880             // fullly contained node.
22881             
22882             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22883                 nodes.push(ar[i]);
22884                 continue;
22885             }
22886             
22887             // probably selected..
22888             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22889                 other_nodes.push(ar[i]);
22890                 continue;
22891             }
22892             // outer..
22893             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22894                 continue;
22895             }
22896             
22897             
22898             has_other_nodes = true;
22899         }
22900         if (!nodes.length && other_nodes.length) {
22901             nodes= other_nodes;
22902         }
22903         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22904             return false;
22905         }
22906         
22907         return nodes[0];
22908     },
22909     createRange: function(sel)
22910     {
22911         // this has strange effects when using with 
22912         // top toolbar - not sure if it's a great idea.
22913         //this.editor.contentWindow.focus();
22914         if (typeof sel != "undefined") {
22915             try {
22916                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22917             } catch(e) {
22918                 return this.doc.createRange();
22919             }
22920         } else {
22921             return this.doc.createRange();
22922         }
22923     },
22924     getParentElement: function()
22925     {
22926         
22927         this.assignDocWin();
22928         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22929         
22930         var range = this.createRange(sel);
22931          
22932         try {
22933             var p = range.commonAncestorContainer;
22934             while (p.nodeType == 3) { // text node
22935                 p = p.parentNode;
22936             }
22937             return p;
22938         } catch (e) {
22939             return null;
22940         }
22941     
22942     },
22943     /***
22944      *
22945      * Range intersection.. the hard stuff...
22946      *  '-1' = before
22947      *  '0' = hits..
22948      *  '1' = after.
22949      *         [ -- selected range --- ]
22950      *   [fail]                        [fail]
22951      *
22952      *    basically..
22953      *      if end is before start or  hits it. fail.
22954      *      if start is after end or hits it fail.
22955      *
22956      *   if either hits (but other is outside. - then it's not 
22957      *   
22958      *    
22959      **/
22960     
22961     
22962     // @see http://www.thismuchiknow.co.uk/?p=64.
22963     rangeIntersectsNode : function(range, node)
22964     {
22965         var nodeRange = node.ownerDocument.createRange();
22966         try {
22967             nodeRange.selectNode(node);
22968         } catch (e) {
22969             nodeRange.selectNodeContents(node);
22970         }
22971     
22972         var rangeStartRange = range.cloneRange();
22973         rangeStartRange.collapse(true);
22974     
22975         var rangeEndRange = range.cloneRange();
22976         rangeEndRange.collapse(false);
22977     
22978         var nodeStartRange = nodeRange.cloneRange();
22979         nodeStartRange.collapse(true);
22980     
22981         var nodeEndRange = nodeRange.cloneRange();
22982         nodeEndRange.collapse(false);
22983     
22984         return rangeStartRange.compareBoundaryPoints(
22985                  Range.START_TO_START, nodeEndRange) == -1 &&
22986                rangeEndRange.compareBoundaryPoints(
22987                  Range.START_TO_START, nodeStartRange) == 1;
22988         
22989          
22990     },
22991     rangeCompareNode : function(range, node)
22992     {
22993         var nodeRange = node.ownerDocument.createRange();
22994         try {
22995             nodeRange.selectNode(node);
22996         } catch (e) {
22997             nodeRange.selectNodeContents(node);
22998         }
22999         
23000         
23001         range.collapse(true);
23002     
23003         nodeRange.collapse(true);
23004      
23005         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23006         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23007          
23008         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23009         
23010         var nodeIsBefore   =  ss == 1;
23011         var nodeIsAfter    = ee == -1;
23012         
23013         if (nodeIsBefore && nodeIsAfter) {
23014             return 0; // outer
23015         }
23016         if (!nodeIsBefore && nodeIsAfter) {
23017             return 1; //right trailed.
23018         }
23019         
23020         if (nodeIsBefore && !nodeIsAfter) {
23021             return 2;  // left trailed.
23022         }
23023         // fully contined.
23024         return 3;
23025     },
23026
23027     // private? - in a new class?
23028     cleanUpPaste :  function()
23029     {
23030         // cleans up the whole document..
23031         Roo.log('cleanuppaste');
23032         
23033         this.cleanUpChildren(this.doc.body);
23034         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23035         if (clean != this.doc.body.innerHTML) {
23036             this.doc.body.innerHTML = clean;
23037         }
23038         
23039     },
23040     
23041     cleanWordChars : function(input) {// change the chars to hex code
23042         var he = Roo.HtmlEditorCore;
23043         
23044         var output = input;
23045         Roo.each(he.swapCodes, function(sw) { 
23046             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23047             
23048             output = output.replace(swapper, sw[1]);
23049         });
23050         
23051         return output;
23052     },
23053     
23054     
23055     cleanUpChildren : function (n)
23056     {
23057         if (!n.childNodes.length) {
23058             return;
23059         }
23060         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23061            this.cleanUpChild(n.childNodes[i]);
23062         }
23063     },
23064     
23065     
23066         
23067     
23068     cleanUpChild : function (node)
23069     {
23070         var ed = this;
23071         //console.log(node);
23072         if (node.nodeName == "#text") {
23073             // clean up silly Windows -- stuff?
23074             return; 
23075         }
23076         if (node.nodeName == "#comment") {
23077             node.parentNode.removeChild(node);
23078             // clean up silly Windows -- stuff?
23079             return; 
23080         }
23081         var lcname = node.tagName.toLowerCase();
23082         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23083         // whitelist of tags..
23084         
23085         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23086             // remove node.
23087             node.parentNode.removeChild(node);
23088             return;
23089             
23090         }
23091         
23092         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23093         
23094         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23095         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23096         
23097         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23098         //    remove_keep_children = true;
23099         //}
23100         
23101         if (remove_keep_children) {
23102             this.cleanUpChildren(node);
23103             // inserts everything just before this node...
23104             while (node.childNodes.length) {
23105                 var cn = node.childNodes[0];
23106                 node.removeChild(cn);
23107                 node.parentNode.insertBefore(cn, node);
23108             }
23109             node.parentNode.removeChild(node);
23110             return;
23111         }
23112         
23113         if (!node.attributes || !node.attributes.length) {
23114             this.cleanUpChildren(node);
23115             return;
23116         }
23117         
23118         function cleanAttr(n,v)
23119         {
23120             
23121             if (v.match(/^\./) || v.match(/^\//)) {
23122                 return;
23123             }
23124             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23125                 return;
23126             }
23127             if (v.match(/^#/)) {
23128                 return;
23129             }
23130 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23131             node.removeAttribute(n);
23132             
23133         }
23134         
23135         var cwhite = this.cwhite;
23136         var cblack = this.cblack;
23137             
23138         function cleanStyle(n,v)
23139         {
23140             if (v.match(/expression/)) { //XSS?? should we even bother..
23141                 node.removeAttribute(n);
23142                 return;
23143             }
23144             
23145             var parts = v.split(/;/);
23146             var clean = [];
23147             
23148             Roo.each(parts, function(p) {
23149                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23150                 if (!p.length) {
23151                     return true;
23152                 }
23153                 var l = p.split(':').shift().replace(/\s+/g,'');
23154                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23155                 
23156                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23157 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23158                     //node.removeAttribute(n);
23159                     return true;
23160                 }
23161                 //Roo.log()
23162                 // only allow 'c whitelisted system attributes'
23163                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23164 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23165                     //node.removeAttribute(n);
23166                     return true;
23167                 }
23168                 
23169                 
23170                  
23171                 
23172                 clean.push(p);
23173                 return true;
23174             });
23175             if (clean.length) { 
23176                 node.setAttribute(n, clean.join(';'));
23177             } else {
23178                 node.removeAttribute(n);
23179             }
23180             
23181         }
23182         
23183         
23184         for (var i = node.attributes.length-1; i > -1 ; i--) {
23185             var a = node.attributes[i];
23186             //console.log(a);
23187             
23188             if (a.name.toLowerCase().substr(0,2)=='on')  {
23189                 node.removeAttribute(a.name);
23190                 continue;
23191             }
23192             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23193                 node.removeAttribute(a.name);
23194                 continue;
23195             }
23196             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23197                 cleanAttr(a.name,a.value); // fixme..
23198                 continue;
23199             }
23200             if (a.name == 'style') {
23201                 cleanStyle(a.name,a.value);
23202                 continue;
23203             }
23204             /// clean up MS crap..
23205             // tecnically this should be a list of valid class'es..
23206             
23207             
23208             if (a.name == 'class') {
23209                 if (a.value.match(/^Mso/)) {
23210                     node.className = '';
23211                 }
23212                 
23213                 if (a.value.match(/^body$/)) {
23214                     node.className = '';
23215                 }
23216                 continue;
23217             }
23218             
23219             // style cleanup!?
23220             // class cleanup?
23221             
23222         }
23223         
23224         
23225         this.cleanUpChildren(node);
23226         
23227         
23228     },
23229     
23230     /**
23231      * Clean up MS wordisms...
23232      */
23233     cleanWord : function(node)
23234     {
23235         
23236         
23237         if (!node) {
23238             this.cleanWord(this.doc.body);
23239             return;
23240         }
23241         if (node.nodeName == "#text") {
23242             // clean up silly Windows -- stuff?
23243             return; 
23244         }
23245         if (node.nodeName == "#comment") {
23246             node.parentNode.removeChild(node);
23247             // clean up silly Windows -- stuff?
23248             return; 
23249         }
23250         
23251         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23252             node.parentNode.removeChild(node);
23253             return;
23254         }
23255         
23256         // remove - but keep children..
23257         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23258             while (node.childNodes.length) {
23259                 var cn = node.childNodes[0];
23260                 node.removeChild(cn);
23261                 node.parentNode.insertBefore(cn, node);
23262             }
23263             node.parentNode.removeChild(node);
23264             this.iterateChildren(node, this.cleanWord);
23265             return;
23266         }
23267         // clean styles
23268         if (node.className.length) {
23269             
23270             var cn = node.className.split(/\W+/);
23271             var cna = [];
23272             Roo.each(cn, function(cls) {
23273                 if (cls.match(/Mso[a-zA-Z]+/)) {
23274                     return;
23275                 }
23276                 cna.push(cls);
23277             });
23278             node.className = cna.length ? cna.join(' ') : '';
23279             if (!cna.length) {
23280                 node.removeAttribute("class");
23281             }
23282         }
23283         
23284         if (node.hasAttribute("lang")) {
23285             node.removeAttribute("lang");
23286         }
23287         
23288         if (node.hasAttribute("style")) {
23289             
23290             var styles = node.getAttribute("style").split(";");
23291             var nstyle = [];
23292             Roo.each(styles, function(s) {
23293                 if (!s.match(/:/)) {
23294                     return;
23295                 }
23296                 var kv = s.split(":");
23297                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23298                     return;
23299                 }
23300                 // what ever is left... we allow.
23301                 nstyle.push(s);
23302             });
23303             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23304             if (!nstyle.length) {
23305                 node.removeAttribute('style');
23306             }
23307         }
23308         this.iterateChildren(node, this.cleanWord);
23309         
23310         
23311         
23312     },
23313     /**
23314      * iterateChildren of a Node, calling fn each time, using this as the scole..
23315      * @param {DomNode} node node to iterate children of.
23316      * @param {Function} fn method of this class to call on each item.
23317      */
23318     iterateChildren : function(node, fn)
23319     {
23320         if (!node.childNodes.length) {
23321                 return;
23322         }
23323         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23324            fn.call(this, node.childNodes[i])
23325         }
23326     },
23327     
23328     
23329     /**
23330      * cleanTableWidths.
23331      *
23332      * Quite often pasting from word etc.. results in tables with column and widths.
23333      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23334      *
23335      */
23336     cleanTableWidths : function(node)
23337     {
23338          
23339          
23340         if (!node) {
23341             this.cleanTableWidths(this.doc.body);
23342             return;
23343         }
23344         
23345         // ignore list...
23346         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23347             return; 
23348         }
23349         Roo.log(node.tagName);
23350         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23351             this.iterateChildren(node, this.cleanTableWidths);
23352             return;
23353         }
23354         if (node.hasAttribute('width')) {
23355             node.removeAttribute('width');
23356         }
23357         
23358          
23359         if (node.hasAttribute("style")) {
23360             // pretty basic...
23361             
23362             var styles = node.getAttribute("style").split(";");
23363             var nstyle = [];
23364             Roo.each(styles, function(s) {
23365                 if (!s.match(/:/)) {
23366                     return;
23367                 }
23368                 var kv = s.split(":");
23369                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23370                     return;
23371                 }
23372                 // what ever is left... we allow.
23373                 nstyle.push(s);
23374             });
23375             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23376             if (!nstyle.length) {
23377                 node.removeAttribute('style');
23378             }
23379         }
23380         
23381         this.iterateChildren(node, this.cleanTableWidths);
23382         
23383         
23384     },
23385     
23386     
23387     
23388     
23389     domToHTML : function(currentElement, depth, nopadtext) {
23390         
23391         depth = depth || 0;
23392         nopadtext = nopadtext || false;
23393     
23394         if (!currentElement) {
23395             return this.domToHTML(this.doc.body);
23396         }
23397         
23398         //Roo.log(currentElement);
23399         var j;
23400         var allText = false;
23401         var nodeName = currentElement.nodeName;
23402         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23403         
23404         if  (nodeName == '#text') {
23405             
23406             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23407         }
23408         
23409         
23410         var ret = '';
23411         if (nodeName != 'BODY') {
23412              
23413             var i = 0;
23414             // Prints the node tagName, such as <A>, <IMG>, etc
23415             if (tagName) {
23416                 var attr = [];
23417                 for(i = 0; i < currentElement.attributes.length;i++) {
23418                     // quoting?
23419                     var aname = currentElement.attributes.item(i).name;
23420                     if (!currentElement.attributes.item(i).value.length) {
23421                         continue;
23422                     }
23423                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23424                 }
23425                 
23426                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23427             } 
23428             else {
23429                 
23430                 // eack
23431             }
23432         } else {
23433             tagName = false;
23434         }
23435         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23436             return ret;
23437         }
23438         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23439             nopadtext = true;
23440         }
23441         
23442         
23443         // Traverse the tree
23444         i = 0;
23445         var currentElementChild = currentElement.childNodes.item(i);
23446         var allText = true;
23447         var innerHTML  = '';
23448         lastnode = '';
23449         while (currentElementChild) {
23450             // Formatting code (indent the tree so it looks nice on the screen)
23451             var nopad = nopadtext;
23452             if (lastnode == 'SPAN') {
23453                 nopad  = true;
23454             }
23455             // text
23456             if  (currentElementChild.nodeName == '#text') {
23457                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23458                 toadd = nopadtext ? toadd : toadd.trim();
23459                 if (!nopad && toadd.length > 80) {
23460                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23461                 }
23462                 innerHTML  += toadd;
23463                 
23464                 i++;
23465                 currentElementChild = currentElement.childNodes.item(i);
23466                 lastNode = '';
23467                 continue;
23468             }
23469             allText = false;
23470             
23471             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23472                 
23473             // Recursively traverse the tree structure of the child node
23474             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23475             lastnode = currentElementChild.nodeName;
23476             i++;
23477             currentElementChild=currentElement.childNodes.item(i);
23478         }
23479         
23480         ret += innerHTML;
23481         
23482         if (!allText) {
23483                 // The remaining code is mostly for formatting the tree
23484             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23485         }
23486         
23487         
23488         if (tagName) {
23489             ret+= "</"+tagName+">";
23490         }
23491         return ret;
23492         
23493     },
23494         
23495     applyBlacklists : function()
23496     {
23497         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23498         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23499         
23500         this.white = [];
23501         this.black = [];
23502         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23503             if (b.indexOf(tag) > -1) {
23504                 return;
23505             }
23506             this.white.push(tag);
23507             
23508         }, this);
23509         
23510         Roo.each(w, function(tag) {
23511             if (b.indexOf(tag) > -1) {
23512                 return;
23513             }
23514             if (this.white.indexOf(tag) > -1) {
23515                 return;
23516             }
23517             this.white.push(tag);
23518             
23519         }, this);
23520         
23521         
23522         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23523             if (w.indexOf(tag) > -1) {
23524                 return;
23525             }
23526             this.black.push(tag);
23527             
23528         }, this);
23529         
23530         Roo.each(b, function(tag) {
23531             if (w.indexOf(tag) > -1) {
23532                 return;
23533             }
23534             if (this.black.indexOf(tag) > -1) {
23535                 return;
23536             }
23537             this.black.push(tag);
23538             
23539         }, this);
23540         
23541         
23542         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23543         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23544         
23545         this.cwhite = [];
23546         this.cblack = [];
23547         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23548             if (b.indexOf(tag) > -1) {
23549                 return;
23550             }
23551             this.cwhite.push(tag);
23552             
23553         }, this);
23554         
23555         Roo.each(w, function(tag) {
23556             if (b.indexOf(tag) > -1) {
23557                 return;
23558             }
23559             if (this.cwhite.indexOf(tag) > -1) {
23560                 return;
23561             }
23562             this.cwhite.push(tag);
23563             
23564         }, this);
23565         
23566         
23567         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23568             if (w.indexOf(tag) > -1) {
23569                 return;
23570             }
23571             this.cblack.push(tag);
23572             
23573         }, this);
23574         
23575         Roo.each(b, function(tag) {
23576             if (w.indexOf(tag) > -1) {
23577                 return;
23578             }
23579             if (this.cblack.indexOf(tag) > -1) {
23580                 return;
23581             }
23582             this.cblack.push(tag);
23583             
23584         }, this);
23585     },
23586     
23587     setStylesheets : function(stylesheets)
23588     {
23589         if(typeof(stylesheets) == 'string'){
23590             Roo.get(this.iframe.contentDocument.head).createChild({
23591                 tag : 'link',
23592                 rel : 'stylesheet',
23593                 type : 'text/css',
23594                 href : stylesheets
23595             });
23596             
23597             return;
23598         }
23599         var _this = this;
23600      
23601         Roo.each(stylesheets, function(s) {
23602             if(!s.length){
23603                 return;
23604             }
23605             
23606             Roo.get(_this.iframe.contentDocument.head).createChild({
23607                 tag : 'link',
23608                 rel : 'stylesheet',
23609                 type : 'text/css',
23610                 href : s
23611             });
23612         });
23613
23614         
23615     },
23616     
23617     removeStylesheets : function()
23618     {
23619         var _this = this;
23620         
23621         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23622             s.remove();
23623         });
23624     },
23625     
23626     setStyle : function(style)
23627     {
23628         Roo.get(this.iframe.contentDocument.head).createChild({
23629             tag : 'style',
23630             type : 'text/css',
23631             html : style
23632         });
23633
23634         return;
23635     }
23636     
23637     // hide stuff that is not compatible
23638     /**
23639      * @event blur
23640      * @hide
23641      */
23642     /**
23643      * @event change
23644      * @hide
23645      */
23646     /**
23647      * @event focus
23648      * @hide
23649      */
23650     /**
23651      * @event specialkey
23652      * @hide
23653      */
23654     /**
23655      * @cfg {String} fieldClass @hide
23656      */
23657     /**
23658      * @cfg {String} focusClass @hide
23659      */
23660     /**
23661      * @cfg {String} autoCreate @hide
23662      */
23663     /**
23664      * @cfg {String} inputType @hide
23665      */
23666     /**
23667      * @cfg {String} invalidClass @hide
23668      */
23669     /**
23670      * @cfg {String} invalidText @hide
23671      */
23672     /**
23673      * @cfg {String} msgFx @hide
23674      */
23675     /**
23676      * @cfg {String} validateOnBlur @hide
23677      */
23678 });
23679
23680 Roo.HtmlEditorCore.white = [
23681         'area', 'br', 'img', 'input', 'hr', 'wbr',
23682         
23683        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23684        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23685        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23686        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23687        'table',   'ul',         'xmp', 
23688        
23689        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23690       'thead',   'tr', 
23691      
23692       'dir', 'menu', 'ol', 'ul', 'dl',
23693        
23694       'embed',  'object'
23695 ];
23696
23697
23698 Roo.HtmlEditorCore.black = [
23699     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23700         'applet', // 
23701         'base',   'basefont', 'bgsound', 'blink',  'body', 
23702         'frame',  'frameset', 'head',    'html',   'ilayer', 
23703         'iframe', 'layer',  'link',     'meta',    'object',   
23704         'script', 'style' ,'title',  'xml' // clean later..
23705 ];
23706 Roo.HtmlEditorCore.clean = [
23707     'script', 'style', 'title', 'xml'
23708 ];
23709 Roo.HtmlEditorCore.remove = [
23710     'font'
23711 ];
23712 // attributes..
23713
23714 Roo.HtmlEditorCore.ablack = [
23715     'on'
23716 ];
23717     
23718 Roo.HtmlEditorCore.aclean = [ 
23719     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23720 ];
23721
23722 // protocols..
23723 Roo.HtmlEditorCore.pwhite= [
23724         'http',  'https',  'mailto'
23725 ];
23726
23727 // white listed style attributes.
23728 Roo.HtmlEditorCore.cwhite= [
23729       //  'text-align', /// default is to allow most things..
23730       
23731          
23732 //        'font-size'//??
23733 ];
23734
23735 // black listed style attributes.
23736 Roo.HtmlEditorCore.cblack= [
23737       //  'font-size' -- this can be set by the project 
23738 ];
23739
23740
23741 Roo.HtmlEditorCore.swapCodes   =[ 
23742     [    8211, "--" ], 
23743     [    8212, "--" ], 
23744     [    8216,  "'" ],  
23745     [    8217, "'" ],  
23746     [    8220, '"' ],  
23747     [    8221, '"' ],  
23748     [    8226, "*" ],  
23749     [    8230, "..." ]
23750 ]; 
23751
23752     /*
23753  * - LGPL
23754  *
23755  * HtmlEditor
23756  * 
23757  */
23758
23759 /**
23760  * @class Roo.bootstrap.HtmlEditor
23761  * @extends Roo.bootstrap.TextArea
23762  * Bootstrap HtmlEditor class
23763
23764  * @constructor
23765  * Create a new HtmlEditor
23766  * @param {Object} config The config object
23767  */
23768
23769 Roo.bootstrap.HtmlEditor = function(config){
23770     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23771     if (!this.toolbars) {
23772         this.toolbars = [];
23773     }
23774     
23775     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23776     this.addEvents({
23777             /**
23778              * @event initialize
23779              * Fires when the editor is fully initialized (including the iframe)
23780              * @param {HtmlEditor} this
23781              */
23782             initialize: true,
23783             /**
23784              * @event activate
23785              * Fires when the editor is first receives the focus. Any insertion must wait
23786              * until after this event.
23787              * @param {HtmlEditor} this
23788              */
23789             activate: true,
23790              /**
23791              * @event beforesync
23792              * Fires before the textarea is updated with content from the editor iframe. Return false
23793              * to cancel the sync.
23794              * @param {HtmlEditor} this
23795              * @param {String} html
23796              */
23797             beforesync: true,
23798              /**
23799              * @event beforepush
23800              * Fires before the iframe editor is updated with content from the textarea. Return false
23801              * to cancel the push.
23802              * @param {HtmlEditor} this
23803              * @param {String} html
23804              */
23805             beforepush: true,
23806              /**
23807              * @event sync
23808              * Fires when the textarea is updated with content from the editor iframe.
23809              * @param {HtmlEditor} this
23810              * @param {String} html
23811              */
23812             sync: true,
23813              /**
23814              * @event push
23815              * Fires when the iframe editor is updated with content from the textarea.
23816              * @param {HtmlEditor} this
23817              * @param {String} html
23818              */
23819             push: true,
23820              /**
23821              * @event editmodechange
23822              * Fires when the editor switches edit modes
23823              * @param {HtmlEditor} this
23824              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23825              */
23826             editmodechange: true,
23827             /**
23828              * @event editorevent
23829              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23830              * @param {HtmlEditor} this
23831              */
23832             editorevent: true,
23833             /**
23834              * @event firstfocus
23835              * Fires when on first focus - needed by toolbars..
23836              * @param {HtmlEditor} this
23837              */
23838             firstfocus: true,
23839             /**
23840              * @event autosave
23841              * Auto save the htmlEditor value as a file into Events
23842              * @param {HtmlEditor} this
23843              */
23844             autosave: true,
23845             /**
23846              * @event savedpreview
23847              * preview the saved version of htmlEditor
23848              * @param {HtmlEditor} this
23849              */
23850             savedpreview: true
23851         });
23852 };
23853
23854
23855 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23856     
23857     
23858       /**
23859      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23860      */
23861     toolbars : false,
23862     
23863      /**
23864     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23865     */
23866     btns : [],
23867    
23868      /**
23869      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23870      *                        Roo.resizable.
23871      */
23872     resizable : false,
23873      /**
23874      * @cfg {Number} height (in pixels)
23875      */   
23876     height: 300,
23877    /**
23878      * @cfg {Number} width (in pixels)
23879      */   
23880     width: false,
23881     
23882     /**
23883      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23884      * 
23885      */
23886     stylesheets: false,
23887     
23888     // id of frame..
23889     frameId: false,
23890     
23891     // private properties
23892     validationEvent : false,
23893     deferHeight: true,
23894     initialized : false,
23895     activated : false,
23896     
23897     onFocus : Roo.emptyFn,
23898     iframePad:3,
23899     hideMode:'offsets',
23900     
23901     tbContainer : false,
23902     
23903     bodyCls : '',
23904     
23905     toolbarContainer :function() {
23906         return this.wrap.select('.x-html-editor-tb',true).first();
23907     },
23908
23909     /**
23910      * Protected method that will not generally be called directly. It
23911      * is called when the editor creates its toolbar. Override this method if you need to
23912      * add custom toolbar buttons.
23913      * @param {HtmlEditor} editor
23914      */
23915     createToolbar : function(){
23916         Roo.log('renewing');
23917         Roo.log("create toolbars");
23918         
23919         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23920         this.toolbars[0].render(this.toolbarContainer());
23921         
23922         return;
23923         
23924 //        if (!editor.toolbars || !editor.toolbars.length) {
23925 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23926 //        }
23927 //        
23928 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23929 //            editor.toolbars[i] = Roo.factory(
23930 //                    typeof(editor.toolbars[i]) == 'string' ?
23931 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23932 //                Roo.bootstrap.HtmlEditor);
23933 //            editor.toolbars[i].init(editor);
23934 //        }
23935     },
23936
23937      
23938     // private
23939     onRender : function(ct, position)
23940     {
23941        // Roo.log("Call onRender: " + this.xtype);
23942         var _t = this;
23943         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23944       
23945         this.wrap = this.inputEl().wrap({
23946             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23947         });
23948         
23949         this.editorcore.onRender(ct, position);
23950          
23951         if (this.resizable) {
23952             this.resizeEl = new Roo.Resizable(this.wrap, {
23953                 pinned : true,
23954                 wrap: true,
23955                 dynamic : true,
23956                 minHeight : this.height,
23957                 height: this.height,
23958                 handles : this.resizable,
23959                 width: this.width,
23960                 listeners : {
23961                     resize : function(r, w, h) {
23962                         _t.onResize(w,h); // -something
23963                     }
23964                 }
23965             });
23966             
23967         }
23968         this.createToolbar(this);
23969        
23970         
23971         if(!this.width && this.resizable){
23972             this.setSize(this.wrap.getSize());
23973         }
23974         if (this.resizeEl) {
23975             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23976             // should trigger onReize..
23977         }
23978         
23979     },
23980
23981     // private
23982     onResize : function(w, h)
23983     {
23984         Roo.log('resize: ' +w + ',' + h );
23985         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23986         var ew = false;
23987         var eh = false;
23988         
23989         if(this.inputEl() ){
23990             if(typeof w == 'number'){
23991                 var aw = w - this.wrap.getFrameWidth('lr');
23992                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23993                 ew = aw;
23994             }
23995             if(typeof h == 'number'){
23996                  var tbh = -11;  // fixme it needs to tool bar size!
23997                 for (var i =0; i < this.toolbars.length;i++) {
23998                     // fixme - ask toolbars for heights?
23999                     tbh += this.toolbars[i].el.getHeight();
24000                     //if (this.toolbars[i].footer) {
24001                     //    tbh += this.toolbars[i].footer.el.getHeight();
24002                     //}
24003                 }
24004               
24005                 
24006                 
24007                 
24008                 
24009                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24010                 ah -= 5; // knock a few pixes off for look..
24011                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24012                 var eh = ah;
24013             }
24014         }
24015         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24016         this.editorcore.onResize(ew,eh);
24017         
24018     },
24019
24020     /**
24021      * Toggles the editor between standard and source edit mode.
24022      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24023      */
24024     toggleSourceEdit : function(sourceEditMode)
24025     {
24026         this.editorcore.toggleSourceEdit(sourceEditMode);
24027         
24028         if(this.editorcore.sourceEditMode){
24029             Roo.log('editor - showing textarea');
24030             
24031 //            Roo.log('in');
24032 //            Roo.log(this.syncValue());
24033             this.syncValue();
24034             this.inputEl().removeClass(['hide', 'x-hidden']);
24035             this.inputEl().dom.removeAttribute('tabIndex');
24036             this.inputEl().focus();
24037         }else{
24038             Roo.log('editor - hiding textarea');
24039 //            Roo.log('out')
24040 //            Roo.log(this.pushValue()); 
24041             this.pushValue();
24042             
24043             this.inputEl().addClass(['hide', 'x-hidden']);
24044             this.inputEl().dom.setAttribute('tabIndex', -1);
24045             //this.deferFocus();
24046         }
24047          
24048         if(this.resizable){
24049             this.setSize(this.wrap.getSize());
24050         }
24051         
24052         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24053     },
24054  
24055     // private (for BoxComponent)
24056     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24057
24058     // private (for BoxComponent)
24059     getResizeEl : function(){
24060         return this.wrap;
24061     },
24062
24063     // private (for BoxComponent)
24064     getPositionEl : function(){
24065         return this.wrap;
24066     },
24067
24068     // private
24069     initEvents : function(){
24070         this.originalValue = this.getValue();
24071     },
24072
24073 //    /**
24074 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24075 //     * @method
24076 //     */
24077 //    markInvalid : Roo.emptyFn,
24078 //    /**
24079 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24080 //     * @method
24081 //     */
24082 //    clearInvalid : Roo.emptyFn,
24083
24084     setValue : function(v){
24085         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24086         this.editorcore.pushValue();
24087     },
24088
24089      
24090     // private
24091     deferFocus : function(){
24092         this.focus.defer(10, this);
24093     },
24094
24095     // doc'ed in Field
24096     focus : function(){
24097         this.editorcore.focus();
24098         
24099     },
24100       
24101
24102     // private
24103     onDestroy : function(){
24104         
24105         
24106         
24107         if(this.rendered){
24108             
24109             for (var i =0; i < this.toolbars.length;i++) {
24110                 // fixme - ask toolbars for heights?
24111                 this.toolbars[i].onDestroy();
24112             }
24113             
24114             this.wrap.dom.innerHTML = '';
24115             this.wrap.remove();
24116         }
24117     },
24118
24119     // private
24120     onFirstFocus : function(){
24121         //Roo.log("onFirstFocus");
24122         this.editorcore.onFirstFocus();
24123          for (var i =0; i < this.toolbars.length;i++) {
24124             this.toolbars[i].onFirstFocus();
24125         }
24126         
24127     },
24128     
24129     // private
24130     syncValue : function()
24131     {   
24132         this.editorcore.syncValue();
24133     },
24134     
24135     pushValue : function()
24136     {   
24137         this.editorcore.pushValue();
24138     }
24139      
24140     
24141     // hide stuff that is not compatible
24142     /**
24143      * @event blur
24144      * @hide
24145      */
24146     /**
24147      * @event change
24148      * @hide
24149      */
24150     /**
24151      * @event focus
24152      * @hide
24153      */
24154     /**
24155      * @event specialkey
24156      * @hide
24157      */
24158     /**
24159      * @cfg {String} fieldClass @hide
24160      */
24161     /**
24162      * @cfg {String} focusClass @hide
24163      */
24164     /**
24165      * @cfg {String} autoCreate @hide
24166      */
24167     /**
24168      * @cfg {String} inputType @hide
24169      */
24170      
24171     /**
24172      * @cfg {String} invalidText @hide
24173      */
24174     /**
24175      * @cfg {String} msgFx @hide
24176      */
24177     /**
24178      * @cfg {String} validateOnBlur @hide
24179      */
24180 });
24181  
24182     
24183    
24184    
24185    
24186       
24187 Roo.namespace('Roo.bootstrap.htmleditor');
24188 /**
24189  * @class Roo.bootstrap.HtmlEditorToolbar1
24190  * Basic Toolbar
24191  * 
24192  * @example
24193  * Usage:
24194  *
24195  new Roo.bootstrap.HtmlEditor({
24196     ....
24197     toolbars : [
24198         new Roo.bootstrap.HtmlEditorToolbar1({
24199             disable : { fonts: 1 , format: 1, ..., ... , ...],
24200             btns : [ .... ]
24201         })
24202     }
24203      
24204  * 
24205  * @cfg {Object} disable List of elements to disable..
24206  * @cfg {Array} btns List of additional buttons.
24207  * 
24208  * 
24209  * NEEDS Extra CSS? 
24210  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24211  */
24212  
24213 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24214 {
24215     
24216     Roo.apply(this, config);
24217     
24218     // default disabled, based on 'good practice'..
24219     this.disable = this.disable || {};
24220     Roo.applyIf(this.disable, {
24221         fontSize : true,
24222         colors : true,
24223         specialElements : true
24224     });
24225     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24226     
24227     this.editor = config.editor;
24228     this.editorcore = config.editor.editorcore;
24229     
24230     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24231     
24232     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24233     // dont call parent... till later.
24234 }
24235 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24236      
24237     bar : true,
24238     
24239     editor : false,
24240     editorcore : false,
24241     
24242     
24243     formats : [
24244         "p" ,  
24245         "h1","h2","h3","h4","h5","h6", 
24246         "pre", "code", 
24247         "abbr", "acronym", "address", "cite", "samp", "var",
24248         'div','span'
24249     ],
24250     
24251     onRender : function(ct, position)
24252     {
24253        // Roo.log("Call onRender: " + this.xtype);
24254         
24255        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24256        Roo.log(this.el);
24257        this.el.dom.style.marginBottom = '0';
24258        var _this = this;
24259        var editorcore = this.editorcore;
24260        var editor= this.editor;
24261        
24262        var children = [];
24263        var btn = function(id,cmd , toggle, handler, html){
24264        
24265             var  event = toggle ? 'toggle' : 'click';
24266        
24267             var a = {
24268                 size : 'sm',
24269                 xtype: 'Button',
24270                 xns: Roo.bootstrap,
24271                 //glyphicon : id,
24272                 fa: id,
24273                 cmd : id || cmd,
24274                 enableToggle:toggle !== false,
24275                 html : html || '',
24276                 pressed : toggle ? false : null,
24277                 listeners : {}
24278             };
24279             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24280                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24281             };
24282             children.push(a);
24283             return a;
24284        }
24285        
24286     //    var cb_box = function...
24287         
24288         var style = {
24289                 xtype: 'Button',
24290                 size : 'sm',
24291                 xns: Roo.bootstrap,
24292                 fa : 'font',
24293                 //html : 'submit'
24294                 menu : {
24295                     xtype: 'Menu',
24296                     xns: Roo.bootstrap,
24297                     items:  []
24298                 }
24299         };
24300         Roo.each(this.formats, function(f) {
24301             style.menu.items.push({
24302                 xtype :'MenuItem',
24303                 xns: Roo.bootstrap,
24304                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24305                 tagname : f,
24306                 listeners : {
24307                     click : function()
24308                     {
24309                         editorcore.insertTag(this.tagname);
24310                         editor.focus();
24311                     }
24312                 }
24313                 
24314             });
24315         });
24316         children.push(style);   
24317         
24318         btn('bold',false,true);
24319         btn('italic',false,true);
24320         btn('align-left', 'justifyleft',true);
24321         btn('align-center', 'justifycenter',true);
24322         btn('align-right' , 'justifyright',true);
24323         btn('link', false, false, function(btn) {
24324             //Roo.log("create link?");
24325             var url = prompt(this.createLinkText, this.defaultLinkValue);
24326             if(url && url != 'http:/'+'/'){
24327                 this.editorcore.relayCmd('createlink', url);
24328             }
24329         }),
24330         btn('list','insertunorderedlist',true);
24331         btn('pencil', false,true, function(btn){
24332                 Roo.log(this);
24333                 this.toggleSourceEdit(btn.pressed);
24334         });
24335         
24336         if (this.editor.btns.length > 0) {
24337             for (var i = 0; i<this.editor.btns.length; i++) {
24338                 children.push(this.editor.btns[i]);
24339             }
24340         }
24341         
24342         /*
24343         var cog = {
24344                 xtype: 'Button',
24345                 size : 'sm',
24346                 xns: Roo.bootstrap,
24347                 glyphicon : 'cog',
24348                 //html : 'submit'
24349                 menu : {
24350                     xtype: 'Menu',
24351                     xns: Roo.bootstrap,
24352                     items:  []
24353                 }
24354         };
24355         
24356         cog.menu.items.push({
24357             xtype :'MenuItem',
24358             xns: Roo.bootstrap,
24359             html : Clean styles,
24360             tagname : f,
24361             listeners : {
24362                 click : function()
24363                 {
24364                     editorcore.insertTag(this.tagname);
24365                     editor.focus();
24366                 }
24367             }
24368             
24369         });
24370        */
24371         
24372          
24373        this.xtype = 'NavSimplebar';
24374         
24375         for(var i=0;i< children.length;i++) {
24376             
24377             this.buttons.add(this.addxtypeChild(children[i]));
24378             
24379         }
24380         
24381         editor.on('editorevent', this.updateToolbar, this);
24382     },
24383     onBtnClick : function(id)
24384     {
24385        this.editorcore.relayCmd(id);
24386        this.editorcore.focus();
24387     },
24388     
24389     /**
24390      * Protected method that will not generally be called directly. It triggers
24391      * a toolbar update by reading the markup state of the current selection in the editor.
24392      */
24393     updateToolbar: function(){
24394
24395         if(!this.editorcore.activated){
24396             this.editor.onFirstFocus(); // is this neeed?
24397             return;
24398         }
24399
24400         var btns = this.buttons; 
24401         var doc = this.editorcore.doc;
24402         btns.get('bold').setActive(doc.queryCommandState('bold'));
24403         btns.get('italic').setActive(doc.queryCommandState('italic'));
24404         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24405         
24406         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24407         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24408         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24409         
24410         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24411         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24412          /*
24413         
24414         var ans = this.editorcore.getAllAncestors();
24415         if (this.formatCombo) {
24416             
24417             
24418             var store = this.formatCombo.store;
24419             this.formatCombo.setValue("");
24420             for (var i =0; i < ans.length;i++) {
24421                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24422                     // select it..
24423                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24424                     break;
24425                 }
24426             }
24427         }
24428         
24429         
24430         
24431         // hides menus... - so this cant be on a menu...
24432         Roo.bootstrap.MenuMgr.hideAll();
24433         */
24434         Roo.bootstrap.MenuMgr.hideAll();
24435         //this.editorsyncValue();
24436     },
24437     onFirstFocus: function() {
24438         this.buttons.each(function(item){
24439            item.enable();
24440         });
24441     },
24442     toggleSourceEdit : function(sourceEditMode){
24443         
24444           
24445         if(sourceEditMode){
24446             Roo.log("disabling buttons");
24447            this.buttons.each( function(item){
24448                 if(item.cmd != 'pencil'){
24449                     item.disable();
24450                 }
24451             });
24452           
24453         }else{
24454             Roo.log("enabling buttons");
24455             if(this.editorcore.initialized){
24456                 this.buttons.each( function(item){
24457                     item.enable();
24458                 });
24459             }
24460             
24461         }
24462         Roo.log("calling toggole on editor");
24463         // tell the editor that it's been pressed..
24464         this.editor.toggleSourceEdit(sourceEditMode);
24465        
24466     }
24467 });
24468
24469
24470
24471
24472
24473 /**
24474  * @class Roo.bootstrap.Table.AbstractSelectionModel
24475  * @extends Roo.util.Observable
24476  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24477  * implemented by descendant classes.  This class should not be directly instantiated.
24478  * @constructor
24479  */
24480 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24481     this.locked = false;
24482     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24483 };
24484
24485
24486 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24487     /** @ignore Called by the grid automatically. Do not call directly. */
24488     init : function(grid){
24489         this.grid = grid;
24490         this.initEvents();
24491     },
24492
24493     /**
24494      * Locks the selections.
24495      */
24496     lock : function(){
24497         this.locked = true;
24498     },
24499
24500     /**
24501      * Unlocks the selections.
24502      */
24503     unlock : function(){
24504         this.locked = false;
24505     },
24506
24507     /**
24508      * Returns true if the selections are locked.
24509      * @return {Boolean}
24510      */
24511     isLocked : function(){
24512         return this.locked;
24513     }
24514 });
24515 /**
24516  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24517  * @class Roo.bootstrap.Table.RowSelectionModel
24518  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24519  * It supports multiple selections and keyboard selection/navigation. 
24520  * @constructor
24521  * @param {Object} config
24522  */
24523
24524 Roo.bootstrap.Table.RowSelectionModel = function(config){
24525     Roo.apply(this, config);
24526     this.selections = new Roo.util.MixedCollection(false, function(o){
24527         return o.id;
24528     });
24529
24530     this.last = false;
24531     this.lastActive = false;
24532
24533     this.addEvents({
24534         /**
24535              * @event selectionchange
24536              * Fires when the selection changes
24537              * @param {SelectionModel} this
24538              */
24539             "selectionchange" : true,
24540         /**
24541              * @event afterselectionchange
24542              * Fires after the selection changes (eg. by key press or clicking)
24543              * @param {SelectionModel} this
24544              */
24545             "afterselectionchange" : true,
24546         /**
24547              * @event beforerowselect
24548              * Fires when a row is selected being selected, return false to cancel.
24549              * @param {SelectionModel} this
24550              * @param {Number} rowIndex The selected index
24551              * @param {Boolean} keepExisting False if other selections will be cleared
24552              */
24553             "beforerowselect" : true,
24554         /**
24555              * @event rowselect
24556              * Fires when a row is selected.
24557              * @param {SelectionModel} this
24558              * @param {Number} rowIndex The selected index
24559              * @param {Roo.data.Record} r The record
24560              */
24561             "rowselect" : true,
24562         /**
24563              * @event rowdeselect
24564              * Fires when a row is deselected.
24565              * @param {SelectionModel} this
24566              * @param {Number} rowIndex The selected index
24567              */
24568         "rowdeselect" : true
24569     });
24570     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24571     this.locked = false;
24572  };
24573
24574 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24575     /**
24576      * @cfg {Boolean} singleSelect
24577      * True to allow selection of only one row at a time (defaults to false)
24578      */
24579     singleSelect : false,
24580
24581     // private
24582     initEvents : function()
24583     {
24584
24585         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24586         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24587         //}else{ // allow click to work like normal
24588          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24589         //}
24590         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24591         this.grid.on("rowclick", this.handleMouseDown, this);
24592         
24593         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24594             "up" : function(e){
24595                 if(!e.shiftKey){
24596                     this.selectPrevious(e.shiftKey);
24597                 }else if(this.last !== false && this.lastActive !== false){
24598                     var last = this.last;
24599                     this.selectRange(this.last,  this.lastActive-1);
24600                     this.grid.getView().focusRow(this.lastActive);
24601                     if(last !== false){
24602                         this.last = last;
24603                     }
24604                 }else{
24605                     this.selectFirstRow();
24606                 }
24607                 this.fireEvent("afterselectionchange", this);
24608             },
24609             "down" : function(e){
24610                 if(!e.shiftKey){
24611                     this.selectNext(e.shiftKey);
24612                 }else if(this.last !== false && this.lastActive !== false){
24613                     var last = this.last;
24614                     this.selectRange(this.last,  this.lastActive+1);
24615                     this.grid.getView().focusRow(this.lastActive);
24616                     if(last !== false){
24617                         this.last = last;
24618                     }
24619                 }else{
24620                     this.selectFirstRow();
24621                 }
24622                 this.fireEvent("afterselectionchange", this);
24623             },
24624             scope: this
24625         });
24626         this.grid.store.on('load', function(){
24627             this.selections.clear();
24628         },this);
24629         /*
24630         var view = this.grid.view;
24631         view.on("refresh", this.onRefresh, this);
24632         view.on("rowupdated", this.onRowUpdated, this);
24633         view.on("rowremoved", this.onRemove, this);
24634         */
24635     },
24636
24637     // private
24638     onRefresh : function()
24639     {
24640         var ds = this.grid.store, i, v = this.grid.view;
24641         var s = this.selections;
24642         s.each(function(r){
24643             if((i = ds.indexOfId(r.id)) != -1){
24644                 v.onRowSelect(i);
24645             }else{
24646                 s.remove(r);
24647             }
24648         });
24649     },
24650
24651     // private
24652     onRemove : function(v, index, r){
24653         this.selections.remove(r);
24654     },
24655
24656     // private
24657     onRowUpdated : function(v, index, r){
24658         if(this.isSelected(r)){
24659             v.onRowSelect(index);
24660         }
24661     },
24662
24663     /**
24664      * Select records.
24665      * @param {Array} records The records to select
24666      * @param {Boolean} keepExisting (optional) True to keep existing selections
24667      */
24668     selectRecords : function(records, keepExisting)
24669     {
24670         if(!keepExisting){
24671             this.clearSelections();
24672         }
24673             var ds = this.grid.store;
24674         for(var i = 0, len = records.length; i < len; i++){
24675             this.selectRow(ds.indexOf(records[i]), true);
24676         }
24677     },
24678
24679     /**
24680      * Gets the number of selected rows.
24681      * @return {Number}
24682      */
24683     getCount : function(){
24684         return this.selections.length;
24685     },
24686
24687     /**
24688      * Selects the first row in the grid.
24689      */
24690     selectFirstRow : function(){
24691         this.selectRow(0);
24692     },
24693
24694     /**
24695      * Select the last row.
24696      * @param {Boolean} keepExisting (optional) True to keep existing selections
24697      */
24698     selectLastRow : function(keepExisting){
24699         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24700         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24701     },
24702
24703     /**
24704      * Selects the row immediately following the last selected row.
24705      * @param {Boolean} keepExisting (optional) True to keep existing selections
24706      */
24707     selectNext : function(keepExisting)
24708     {
24709             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24710             this.selectRow(this.last+1, keepExisting);
24711             this.grid.getView().focusRow(this.last);
24712         }
24713     },
24714
24715     /**
24716      * Selects the row that precedes the last selected row.
24717      * @param {Boolean} keepExisting (optional) True to keep existing selections
24718      */
24719     selectPrevious : function(keepExisting){
24720         if(this.last){
24721             this.selectRow(this.last-1, keepExisting);
24722             this.grid.getView().focusRow(this.last);
24723         }
24724     },
24725
24726     /**
24727      * Returns the selected records
24728      * @return {Array} Array of selected records
24729      */
24730     getSelections : function(){
24731         return [].concat(this.selections.items);
24732     },
24733
24734     /**
24735      * Returns the first selected record.
24736      * @return {Record}
24737      */
24738     getSelected : function(){
24739         return this.selections.itemAt(0);
24740     },
24741
24742
24743     /**
24744      * Clears all selections.
24745      */
24746     clearSelections : function(fast)
24747     {
24748         if(this.locked) {
24749             return;
24750         }
24751         if(fast !== true){
24752                 var ds = this.grid.store;
24753             var s = this.selections;
24754             s.each(function(r){
24755                 this.deselectRow(ds.indexOfId(r.id));
24756             }, this);
24757             s.clear();
24758         }else{
24759             this.selections.clear();
24760         }
24761         this.last = false;
24762     },
24763
24764
24765     /**
24766      * Selects all rows.
24767      */
24768     selectAll : function(){
24769         if(this.locked) {
24770             return;
24771         }
24772         this.selections.clear();
24773         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24774             this.selectRow(i, true);
24775         }
24776     },
24777
24778     /**
24779      * Returns True if there is a selection.
24780      * @return {Boolean}
24781      */
24782     hasSelection : function(){
24783         return this.selections.length > 0;
24784     },
24785
24786     /**
24787      * Returns True if the specified row is selected.
24788      * @param {Number/Record} record The record or index of the record to check
24789      * @return {Boolean}
24790      */
24791     isSelected : function(index){
24792             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24793         return (r && this.selections.key(r.id) ? true : false);
24794     },
24795
24796     /**
24797      * Returns True if the specified record id is selected.
24798      * @param {String} id The id of record to check
24799      * @return {Boolean}
24800      */
24801     isIdSelected : function(id){
24802         return (this.selections.key(id) ? true : false);
24803     },
24804
24805
24806     // private
24807     handleMouseDBClick : function(e, t){
24808         
24809     },
24810     // private
24811     handleMouseDown : function(e, t)
24812     {
24813             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24814         if(this.isLocked() || rowIndex < 0 ){
24815             return;
24816         };
24817         if(e.shiftKey && this.last !== false){
24818             var last = this.last;
24819             this.selectRange(last, rowIndex, e.ctrlKey);
24820             this.last = last; // reset the last
24821             t.focus();
24822     
24823         }else{
24824             var isSelected = this.isSelected(rowIndex);
24825             //Roo.log("select row:" + rowIndex);
24826             if(isSelected){
24827                 this.deselectRow(rowIndex);
24828             } else {
24829                         this.selectRow(rowIndex, true);
24830             }
24831     
24832             /*
24833                 if(e.button !== 0 && isSelected){
24834                 alert('rowIndex 2: ' + rowIndex);
24835                     view.focusRow(rowIndex);
24836                 }else if(e.ctrlKey && isSelected){
24837                     this.deselectRow(rowIndex);
24838                 }else if(!isSelected){
24839                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24840                     view.focusRow(rowIndex);
24841                 }
24842             */
24843         }
24844         this.fireEvent("afterselectionchange", this);
24845     },
24846     // private
24847     handleDragableRowClick :  function(grid, rowIndex, e) 
24848     {
24849         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24850             this.selectRow(rowIndex, false);
24851             grid.view.focusRow(rowIndex);
24852              this.fireEvent("afterselectionchange", this);
24853         }
24854     },
24855     
24856     /**
24857      * Selects multiple rows.
24858      * @param {Array} rows Array of the indexes of the row to select
24859      * @param {Boolean} keepExisting (optional) True to keep existing selections
24860      */
24861     selectRows : function(rows, keepExisting){
24862         if(!keepExisting){
24863             this.clearSelections();
24864         }
24865         for(var i = 0, len = rows.length; i < len; i++){
24866             this.selectRow(rows[i], true);
24867         }
24868     },
24869
24870     /**
24871      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24872      * @param {Number} startRow The index of the first row in the range
24873      * @param {Number} endRow The index of the last row in the range
24874      * @param {Boolean} keepExisting (optional) True to retain existing selections
24875      */
24876     selectRange : function(startRow, endRow, keepExisting){
24877         if(this.locked) {
24878             return;
24879         }
24880         if(!keepExisting){
24881             this.clearSelections();
24882         }
24883         if(startRow <= endRow){
24884             for(var i = startRow; i <= endRow; i++){
24885                 this.selectRow(i, true);
24886             }
24887         }else{
24888             for(var i = startRow; i >= endRow; i--){
24889                 this.selectRow(i, true);
24890             }
24891         }
24892     },
24893
24894     /**
24895      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24896      * @param {Number} startRow The index of the first row in the range
24897      * @param {Number} endRow The index of the last row in the range
24898      */
24899     deselectRange : function(startRow, endRow, preventViewNotify){
24900         if(this.locked) {
24901             return;
24902         }
24903         for(var i = startRow; i <= endRow; i++){
24904             this.deselectRow(i, preventViewNotify);
24905         }
24906     },
24907
24908     /**
24909      * Selects a row.
24910      * @param {Number} row The index of the row to select
24911      * @param {Boolean} keepExisting (optional) True to keep existing selections
24912      */
24913     selectRow : function(index, keepExisting, preventViewNotify)
24914     {
24915             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24916             return;
24917         }
24918         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24919             if(!keepExisting || this.singleSelect){
24920                 this.clearSelections();
24921             }
24922             
24923             var r = this.grid.store.getAt(index);
24924             //console.log('selectRow - record id :' + r.id);
24925             
24926             this.selections.add(r);
24927             this.last = this.lastActive = index;
24928             if(!preventViewNotify){
24929                 var proxy = new Roo.Element(
24930                                 this.grid.getRowDom(index)
24931                 );
24932                 proxy.addClass('bg-info info');
24933             }
24934             this.fireEvent("rowselect", this, index, r);
24935             this.fireEvent("selectionchange", this);
24936         }
24937     },
24938
24939     /**
24940      * Deselects a row.
24941      * @param {Number} row The index of the row to deselect
24942      */
24943     deselectRow : function(index, preventViewNotify)
24944     {
24945         if(this.locked) {
24946             return;
24947         }
24948         if(this.last == index){
24949             this.last = false;
24950         }
24951         if(this.lastActive == index){
24952             this.lastActive = false;
24953         }
24954         
24955         var r = this.grid.store.getAt(index);
24956         if (!r) {
24957             return;
24958         }
24959         
24960         this.selections.remove(r);
24961         //.console.log('deselectRow - record id :' + r.id);
24962         if(!preventViewNotify){
24963         
24964             var proxy = new Roo.Element(
24965                 this.grid.getRowDom(index)
24966             );
24967             proxy.removeClass('bg-info info');
24968         }
24969         this.fireEvent("rowdeselect", this, index);
24970         this.fireEvent("selectionchange", this);
24971     },
24972
24973     // private
24974     restoreLast : function(){
24975         if(this._last){
24976             this.last = this._last;
24977         }
24978     },
24979
24980     // private
24981     acceptsNav : function(row, col, cm){
24982         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24983     },
24984
24985     // private
24986     onEditorKey : function(field, e){
24987         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24988         if(k == e.TAB){
24989             e.stopEvent();
24990             ed.completeEdit();
24991             if(e.shiftKey){
24992                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24993             }else{
24994                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24995             }
24996         }else if(k == e.ENTER && !e.ctrlKey){
24997             e.stopEvent();
24998             ed.completeEdit();
24999             if(e.shiftKey){
25000                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25001             }else{
25002                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25003             }
25004         }else if(k == e.ESC){
25005             ed.cancelEdit();
25006         }
25007         if(newCell){
25008             g.startEditing(newCell[0], newCell[1]);
25009         }
25010     }
25011 });
25012 /*
25013  * Based on:
25014  * Ext JS Library 1.1.1
25015  * Copyright(c) 2006-2007, Ext JS, LLC.
25016  *
25017  * Originally Released Under LGPL - original licence link has changed is not relivant.
25018  *
25019  * Fork - LGPL
25020  * <script type="text/javascript">
25021  */
25022  
25023 /**
25024  * @class Roo.bootstrap.PagingToolbar
25025  * @extends Roo.bootstrap.NavSimplebar
25026  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25027  * @constructor
25028  * Create a new PagingToolbar
25029  * @param {Object} config The config object
25030  * @param {Roo.data.Store} store
25031  */
25032 Roo.bootstrap.PagingToolbar = function(config)
25033 {
25034     // old args format still supported... - xtype is prefered..
25035         // created from xtype...
25036     
25037     this.ds = config.dataSource;
25038     
25039     if (config.store && !this.ds) {
25040         this.store= Roo.factory(config.store, Roo.data);
25041         this.ds = this.store;
25042         this.ds.xmodule = this.xmodule || false;
25043     }
25044     
25045     this.toolbarItems = [];
25046     if (config.items) {
25047         this.toolbarItems = config.items;
25048     }
25049     
25050     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25051     
25052     this.cursor = 0;
25053     
25054     if (this.ds) { 
25055         this.bind(this.ds);
25056     }
25057     
25058     if (Roo.bootstrap.version == 4) {
25059         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25060     } else {
25061         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25062     }
25063     
25064 };
25065
25066 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25067     /**
25068      * @cfg {Roo.data.Store} dataSource
25069      * The underlying data store providing the paged data
25070      */
25071     /**
25072      * @cfg {String/HTMLElement/Element} container
25073      * container The id or element that will contain the toolbar
25074      */
25075     /**
25076      * @cfg {Boolean} displayInfo
25077      * True to display the displayMsg (defaults to false)
25078      */
25079     /**
25080      * @cfg {Number} pageSize
25081      * The number of records to display per page (defaults to 20)
25082      */
25083     pageSize: 20,
25084     /**
25085      * @cfg {String} displayMsg
25086      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25087      */
25088     displayMsg : 'Displaying {0} - {1} of {2}',
25089     /**
25090      * @cfg {String} emptyMsg
25091      * The message to display when no records are found (defaults to "No data to display")
25092      */
25093     emptyMsg : 'No data to display',
25094     /**
25095      * Customizable piece of the default paging text (defaults to "Page")
25096      * @type String
25097      */
25098     beforePageText : "Page",
25099     /**
25100      * Customizable piece of the default paging text (defaults to "of %0")
25101      * @type String
25102      */
25103     afterPageText : "of {0}",
25104     /**
25105      * Customizable piece of the default paging text (defaults to "First Page")
25106      * @type String
25107      */
25108     firstText : "First Page",
25109     /**
25110      * Customizable piece of the default paging text (defaults to "Previous Page")
25111      * @type String
25112      */
25113     prevText : "Previous Page",
25114     /**
25115      * Customizable piece of the default paging text (defaults to "Next Page")
25116      * @type String
25117      */
25118     nextText : "Next Page",
25119     /**
25120      * Customizable piece of the default paging text (defaults to "Last Page")
25121      * @type String
25122      */
25123     lastText : "Last Page",
25124     /**
25125      * Customizable piece of the default paging text (defaults to "Refresh")
25126      * @type String
25127      */
25128     refreshText : "Refresh",
25129
25130     buttons : false,
25131     // private
25132     onRender : function(ct, position) 
25133     {
25134         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25135         this.navgroup.parentId = this.id;
25136         this.navgroup.onRender(this.el, null);
25137         // add the buttons to the navgroup
25138         
25139         if(this.displayInfo){
25140             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25141             this.displayEl = this.el.select('.x-paging-info', true).first();
25142 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25143 //            this.displayEl = navel.el.select('span',true).first();
25144         }
25145         
25146         var _this = this;
25147         
25148         if(this.buttons){
25149             Roo.each(_this.buttons, function(e){ // this might need to use render????
25150                Roo.factory(e).render(_this.el);
25151             });
25152         }
25153             
25154         Roo.each(_this.toolbarItems, function(e) {
25155             _this.navgroup.addItem(e);
25156         });
25157         
25158         
25159         this.first = this.navgroup.addItem({
25160             tooltip: this.firstText,
25161             cls: "prev btn-outline-secondary",
25162             html : ' <i class="fa fa-step-backward"></i>',
25163             disabled: true,
25164             preventDefault: true,
25165             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25166         });
25167         
25168         this.prev =  this.navgroup.addItem({
25169             tooltip: this.prevText,
25170             cls: "prev btn-outline-secondary",
25171             html : ' <i class="fa fa-backward"></i>',
25172             disabled: true,
25173             preventDefault: true,
25174             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25175         });
25176     //this.addSeparator();
25177         
25178         
25179         var field = this.navgroup.addItem( {
25180             tagtype : 'span',
25181             cls : 'x-paging-position  btn-outline-secondary',
25182              disabled: true,
25183             html : this.beforePageText  +
25184                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25185                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25186          } ); //?? escaped?
25187         
25188         this.field = field.el.select('input', true).first();
25189         this.field.on("keydown", this.onPagingKeydown, this);
25190         this.field.on("focus", function(){this.dom.select();});
25191     
25192     
25193         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25194         //this.field.setHeight(18);
25195         //this.addSeparator();
25196         this.next = this.navgroup.addItem({
25197             tooltip: this.nextText,
25198             cls: "next btn-outline-secondary",
25199             html : ' <i class="fa fa-forward"></i>',
25200             disabled: true,
25201             preventDefault: true,
25202             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25203         });
25204         this.last = this.navgroup.addItem({
25205             tooltip: this.lastText,
25206             html : ' <i class="fa fa-step-forward"></i>',
25207             cls: "next btn-outline-secondary",
25208             disabled: true,
25209             preventDefault: true,
25210             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25211         });
25212     //this.addSeparator();
25213         this.loading = this.navgroup.addItem({
25214             tooltip: this.refreshText,
25215             cls: "btn-outline-secondary",
25216             html : ' <i class="fa fa-refresh"></i>',
25217             preventDefault: true,
25218             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25219         });
25220         
25221     },
25222
25223     // private
25224     updateInfo : function(){
25225         if(this.displayEl){
25226             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25227             var msg = count == 0 ?
25228                 this.emptyMsg :
25229                 String.format(
25230                     this.displayMsg,
25231                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25232                 );
25233             this.displayEl.update(msg);
25234         }
25235     },
25236
25237     // private
25238     onLoad : function(ds, r, o)
25239     {
25240         this.cursor = o.params.start ? o.params.start : 0;
25241         
25242         var d = this.getPageData(),
25243             ap = d.activePage,
25244             ps = d.pages;
25245         
25246         
25247         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25248         this.field.dom.value = ap;
25249         this.first.setDisabled(ap == 1);
25250         this.prev.setDisabled(ap == 1);
25251         this.next.setDisabled(ap == ps);
25252         this.last.setDisabled(ap == ps);
25253         this.loading.enable();
25254         this.updateInfo();
25255     },
25256
25257     // private
25258     getPageData : function(){
25259         var total = this.ds.getTotalCount();
25260         return {
25261             total : total,
25262             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25263             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25264         };
25265     },
25266
25267     // private
25268     onLoadError : function(){
25269         this.loading.enable();
25270     },
25271
25272     // private
25273     onPagingKeydown : function(e){
25274         var k = e.getKey();
25275         var d = this.getPageData();
25276         if(k == e.RETURN){
25277             var v = this.field.dom.value, pageNum;
25278             if(!v || isNaN(pageNum = parseInt(v, 10))){
25279                 this.field.dom.value = d.activePage;
25280                 return;
25281             }
25282             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25283             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25284             e.stopEvent();
25285         }
25286         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))
25287         {
25288           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25289           this.field.dom.value = pageNum;
25290           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25291           e.stopEvent();
25292         }
25293         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25294         {
25295           var v = this.field.dom.value, pageNum; 
25296           var increment = (e.shiftKey) ? 10 : 1;
25297           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25298                 increment *= -1;
25299           }
25300           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25301             this.field.dom.value = d.activePage;
25302             return;
25303           }
25304           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25305           {
25306             this.field.dom.value = parseInt(v, 10) + increment;
25307             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25308             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25309           }
25310           e.stopEvent();
25311         }
25312     },
25313
25314     // private
25315     beforeLoad : function(){
25316         if(this.loading){
25317             this.loading.disable();
25318         }
25319     },
25320
25321     // private
25322     onClick : function(which){
25323         
25324         var ds = this.ds;
25325         if (!ds) {
25326             return;
25327         }
25328         
25329         switch(which){
25330             case "first":
25331                 ds.load({params:{start: 0, limit: this.pageSize}});
25332             break;
25333             case "prev":
25334                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25335             break;
25336             case "next":
25337                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25338             break;
25339             case "last":
25340                 var total = ds.getTotalCount();
25341                 var extra = total % this.pageSize;
25342                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25343                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25344             break;
25345             case "refresh":
25346                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25347             break;
25348         }
25349     },
25350
25351     /**
25352      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25353      * @param {Roo.data.Store} store The data store to unbind
25354      */
25355     unbind : function(ds){
25356         ds.un("beforeload", this.beforeLoad, this);
25357         ds.un("load", this.onLoad, this);
25358         ds.un("loadexception", this.onLoadError, this);
25359         ds.un("remove", this.updateInfo, this);
25360         ds.un("add", this.updateInfo, this);
25361         this.ds = undefined;
25362     },
25363
25364     /**
25365      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25366      * @param {Roo.data.Store} store The data store to bind
25367      */
25368     bind : function(ds){
25369         ds.on("beforeload", this.beforeLoad, this);
25370         ds.on("load", this.onLoad, this);
25371         ds.on("loadexception", this.onLoadError, this);
25372         ds.on("remove", this.updateInfo, this);
25373         ds.on("add", this.updateInfo, this);
25374         this.ds = ds;
25375     }
25376 });/*
25377  * - LGPL
25378  *
25379  * element
25380  * 
25381  */
25382
25383 /**
25384  * @class Roo.bootstrap.MessageBar
25385  * @extends Roo.bootstrap.Component
25386  * Bootstrap MessageBar class
25387  * @cfg {String} html contents of the MessageBar
25388  * @cfg {String} weight (info | success | warning | danger) default info
25389  * @cfg {String} beforeClass insert the bar before the given class
25390  * @cfg {Boolean} closable (true | false) default false
25391  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25392  * 
25393  * @constructor
25394  * Create a new Element
25395  * @param {Object} config The config object
25396  */
25397
25398 Roo.bootstrap.MessageBar = function(config){
25399     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25400 };
25401
25402 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25403     
25404     html: '',
25405     weight: 'info',
25406     closable: false,
25407     fixed: false,
25408     beforeClass: 'bootstrap-sticky-wrap',
25409     
25410     getAutoCreate : function(){
25411         
25412         var cfg = {
25413             tag: 'div',
25414             cls: 'alert alert-dismissable alert-' + this.weight,
25415             cn: [
25416                 {
25417                     tag: 'span',
25418                     cls: 'message',
25419                     html: this.html || ''
25420                 }
25421             ]
25422         };
25423         
25424         if(this.fixed){
25425             cfg.cls += ' alert-messages-fixed';
25426         }
25427         
25428         if(this.closable){
25429             cfg.cn.push({
25430                 tag: 'button',
25431                 cls: 'close',
25432                 html: 'x'
25433             });
25434         }
25435         
25436         return cfg;
25437     },
25438     
25439     onRender : function(ct, position)
25440     {
25441         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25442         
25443         if(!this.el){
25444             var cfg = Roo.apply({},  this.getAutoCreate());
25445             cfg.id = Roo.id();
25446             
25447             if (this.cls) {
25448                 cfg.cls += ' ' + this.cls;
25449             }
25450             if (this.style) {
25451                 cfg.style = this.style;
25452             }
25453             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25454             
25455             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25456         }
25457         
25458         this.el.select('>button.close').on('click', this.hide, this);
25459         
25460     },
25461     
25462     show : function()
25463     {
25464         if (!this.rendered) {
25465             this.render();
25466         }
25467         
25468         this.el.show();
25469         
25470         this.fireEvent('show', this);
25471         
25472     },
25473     
25474     hide : function()
25475     {
25476         if (!this.rendered) {
25477             this.render();
25478         }
25479         
25480         this.el.hide();
25481         
25482         this.fireEvent('hide', this);
25483     },
25484     
25485     update : function()
25486     {
25487 //        var e = this.el.dom.firstChild;
25488 //        
25489 //        if(this.closable){
25490 //            e = e.nextSibling;
25491 //        }
25492 //        
25493 //        e.data = this.html || '';
25494
25495         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25496     }
25497    
25498 });
25499
25500  
25501
25502      /*
25503  * - LGPL
25504  *
25505  * Graph
25506  * 
25507  */
25508
25509
25510 /**
25511  * @class Roo.bootstrap.Graph
25512  * @extends Roo.bootstrap.Component
25513  * Bootstrap Graph class
25514 > Prameters
25515  -sm {number} sm 4
25516  -md {number} md 5
25517  @cfg {String} graphtype  bar | vbar | pie
25518  @cfg {number} g_x coodinator | centre x (pie)
25519  @cfg {number} g_y coodinator | centre y (pie)
25520  @cfg {number} g_r radius (pie)
25521  @cfg {number} g_height height of the chart (respected by all elements in the set)
25522  @cfg {number} g_width width of the chart (respected by all elements in the set)
25523  @cfg {Object} title The title of the chart
25524     
25525  -{Array}  values
25526  -opts (object) options for the chart 
25527      o {
25528      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25529      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25530      o vgutter (number)
25531      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.
25532      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25533      o to
25534      o stretch (boolean)
25535      o }
25536  -opts (object) options for the pie
25537      o{
25538      o cut
25539      o startAngle (number)
25540      o endAngle (number)
25541      } 
25542  *
25543  * @constructor
25544  * Create a new Input
25545  * @param {Object} config The config object
25546  */
25547
25548 Roo.bootstrap.Graph = function(config){
25549     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25550     
25551     this.addEvents({
25552         // img events
25553         /**
25554          * @event click
25555          * The img click event for the img.
25556          * @param {Roo.EventObject} e
25557          */
25558         "click" : true
25559     });
25560 };
25561
25562 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25563     
25564     sm: 4,
25565     md: 5,
25566     graphtype: 'bar',
25567     g_height: 250,
25568     g_width: 400,
25569     g_x: 50,
25570     g_y: 50,
25571     g_r: 30,
25572     opts:{
25573         //g_colors: this.colors,
25574         g_type: 'soft',
25575         g_gutter: '20%'
25576
25577     },
25578     title : false,
25579
25580     getAutoCreate : function(){
25581         
25582         var cfg = {
25583             tag: 'div',
25584             html : null
25585         };
25586         
25587         
25588         return  cfg;
25589     },
25590
25591     onRender : function(ct,position){
25592         
25593         
25594         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25595         
25596         if (typeof(Raphael) == 'undefined') {
25597             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25598             return;
25599         }
25600         
25601         this.raphael = Raphael(this.el.dom);
25602         
25603                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25604                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25605                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25606                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25607                 /*
25608                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25609                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25610                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25611                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25612                 
25613                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25614                 r.barchart(330, 10, 300, 220, data1);
25615                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25616                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25617                 */
25618                 
25619                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25620                 // r.barchart(30, 30, 560, 250,  xdata, {
25621                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25622                 //     axis : "0 0 1 1",
25623                 //     axisxlabels :  xdata
25624                 //     //yvalues : cols,
25625                    
25626                 // });
25627 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25628 //        
25629 //        this.load(null,xdata,{
25630 //                axis : "0 0 1 1",
25631 //                axisxlabels :  xdata
25632 //                });
25633
25634     },
25635
25636     load : function(graphtype,xdata,opts)
25637     {
25638         this.raphael.clear();
25639         if(!graphtype) {
25640             graphtype = this.graphtype;
25641         }
25642         if(!opts){
25643             opts = this.opts;
25644         }
25645         var r = this.raphael,
25646             fin = function () {
25647                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25648             },
25649             fout = function () {
25650                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25651             },
25652             pfin = function() {
25653                 this.sector.stop();
25654                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25655
25656                 if (this.label) {
25657                     this.label[0].stop();
25658                     this.label[0].attr({ r: 7.5 });
25659                     this.label[1].attr({ "font-weight": 800 });
25660                 }
25661             },
25662             pfout = function() {
25663                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25664
25665                 if (this.label) {
25666                     this.label[0].animate({ r: 5 }, 500, "bounce");
25667                     this.label[1].attr({ "font-weight": 400 });
25668                 }
25669             };
25670
25671         switch(graphtype){
25672             case 'bar':
25673                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25674                 break;
25675             case 'hbar':
25676                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25677                 break;
25678             case 'pie':
25679 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25680 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25681 //            
25682                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25683                 
25684                 break;
25685
25686         }
25687         
25688         if(this.title){
25689             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25690         }
25691         
25692     },
25693     
25694     setTitle: function(o)
25695     {
25696         this.title = o;
25697     },
25698     
25699     initEvents: function() {
25700         
25701         if(!this.href){
25702             this.el.on('click', this.onClick, this);
25703         }
25704     },
25705     
25706     onClick : function(e)
25707     {
25708         Roo.log('img onclick');
25709         this.fireEvent('click', this, e);
25710     }
25711    
25712 });
25713
25714  
25715 /*
25716  * - LGPL
25717  *
25718  * numberBox
25719  * 
25720  */
25721 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25722
25723 /**
25724  * @class Roo.bootstrap.dash.NumberBox
25725  * @extends Roo.bootstrap.Component
25726  * Bootstrap NumberBox class
25727  * @cfg {String} headline Box headline
25728  * @cfg {String} content Box content
25729  * @cfg {String} icon Box icon
25730  * @cfg {String} footer Footer text
25731  * @cfg {String} fhref Footer href
25732  * 
25733  * @constructor
25734  * Create a new NumberBox
25735  * @param {Object} config The config object
25736  */
25737
25738
25739 Roo.bootstrap.dash.NumberBox = function(config){
25740     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25741     
25742 };
25743
25744 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25745     
25746     headline : '',
25747     content : '',
25748     icon : '',
25749     footer : '',
25750     fhref : '',
25751     ficon : '',
25752     
25753     getAutoCreate : function(){
25754         
25755         var cfg = {
25756             tag : 'div',
25757             cls : 'small-box ',
25758             cn : [
25759                 {
25760                     tag : 'div',
25761                     cls : 'inner',
25762                     cn :[
25763                         {
25764                             tag : 'h3',
25765                             cls : 'roo-headline',
25766                             html : this.headline
25767                         },
25768                         {
25769                             tag : 'p',
25770                             cls : 'roo-content',
25771                             html : this.content
25772                         }
25773                     ]
25774                 }
25775             ]
25776         };
25777         
25778         if(this.icon){
25779             cfg.cn.push({
25780                 tag : 'div',
25781                 cls : 'icon',
25782                 cn :[
25783                     {
25784                         tag : 'i',
25785                         cls : 'ion ' + this.icon
25786                     }
25787                 ]
25788             });
25789         }
25790         
25791         if(this.footer){
25792             var footer = {
25793                 tag : 'a',
25794                 cls : 'small-box-footer',
25795                 href : this.fhref || '#',
25796                 html : this.footer
25797             };
25798             
25799             cfg.cn.push(footer);
25800             
25801         }
25802         
25803         return  cfg;
25804     },
25805
25806     onRender : function(ct,position){
25807         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25808
25809
25810        
25811                 
25812     },
25813
25814     setHeadline: function (value)
25815     {
25816         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25817     },
25818     
25819     setFooter: function (value, href)
25820     {
25821         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25822         
25823         if(href){
25824             this.el.select('a.small-box-footer',true).first().attr('href', href);
25825         }
25826         
25827     },
25828
25829     setContent: function (value)
25830     {
25831         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25832     },
25833
25834     initEvents: function() 
25835     {   
25836         
25837     }
25838     
25839 });
25840
25841  
25842 /*
25843  * - LGPL
25844  *
25845  * TabBox
25846  * 
25847  */
25848 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25849
25850 /**
25851  * @class Roo.bootstrap.dash.TabBox
25852  * @extends Roo.bootstrap.Component
25853  * Bootstrap TabBox class
25854  * @cfg {String} title Title of the TabBox
25855  * @cfg {String} icon Icon of the TabBox
25856  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25857  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25858  * 
25859  * @constructor
25860  * Create a new TabBox
25861  * @param {Object} config The config object
25862  */
25863
25864
25865 Roo.bootstrap.dash.TabBox = function(config){
25866     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25867     this.addEvents({
25868         // raw events
25869         /**
25870          * @event addpane
25871          * When a pane is added
25872          * @param {Roo.bootstrap.dash.TabPane} pane
25873          */
25874         "addpane" : true,
25875         /**
25876          * @event activatepane
25877          * When a pane is activated
25878          * @param {Roo.bootstrap.dash.TabPane} pane
25879          */
25880         "activatepane" : true
25881         
25882          
25883     });
25884     
25885     this.panes = [];
25886 };
25887
25888 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25889
25890     title : '',
25891     icon : false,
25892     showtabs : true,
25893     tabScrollable : false,
25894     
25895     getChildContainer : function()
25896     {
25897         return this.el.select('.tab-content', true).first();
25898     },
25899     
25900     getAutoCreate : function(){
25901         
25902         var header = {
25903             tag: 'li',
25904             cls: 'pull-left header',
25905             html: this.title,
25906             cn : []
25907         };
25908         
25909         if(this.icon){
25910             header.cn.push({
25911                 tag: 'i',
25912                 cls: 'fa ' + this.icon
25913             });
25914         }
25915         
25916         var h = {
25917             tag: 'ul',
25918             cls: 'nav nav-tabs pull-right',
25919             cn: [
25920                 header
25921             ]
25922         };
25923         
25924         if(this.tabScrollable){
25925             h = {
25926                 tag: 'div',
25927                 cls: 'tab-header',
25928                 cn: [
25929                     {
25930                         tag: 'ul',
25931                         cls: 'nav nav-tabs pull-right',
25932                         cn: [
25933                             header
25934                         ]
25935                     }
25936                 ]
25937             };
25938         }
25939         
25940         var cfg = {
25941             tag: 'div',
25942             cls: 'nav-tabs-custom',
25943             cn: [
25944                 h,
25945                 {
25946                     tag: 'div',
25947                     cls: 'tab-content no-padding',
25948                     cn: []
25949                 }
25950             ]
25951         };
25952
25953         return  cfg;
25954     },
25955     initEvents : function()
25956     {
25957         //Roo.log('add add pane handler');
25958         this.on('addpane', this.onAddPane, this);
25959     },
25960      /**
25961      * Updates the box title
25962      * @param {String} html to set the title to.
25963      */
25964     setTitle : function(value)
25965     {
25966         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25967     },
25968     onAddPane : function(pane)
25969     {
25970         this.panes.push(pane);
25971         //Roo.log('addpane');
25972         //Roo.log(pane);
25973         // tabs are rendere left to right..
25974         if(!this.showtabs){
25975             return;
25976         }
25977         
25978         var ctr = this.el.select('.nav-tabs', true).first();
25979          
25980          
25981         var existing = ctr.select('.nav-tab',true);
25982         var qty = existing.getCount();;
25983         
25984         
25985         var tab = ctr.createChild({
25986             tag : 'li',
25987             cls : 'nav-tab' + (qty ? '' : ' active'),
25988             cn : [
25989                 {
25990                     tag : 'a',
25991                     href:'#',
25992                     html : pane.title
25993                 }
25994             ]
25995         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25996         pane.tab = tab;
25997         
25998         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25999         if (!qty) {
26000             pane.el.addClass('active');
26001         }
26002         
26003                 
26004     },
26005     onTabClick : function(ev,un,ob,pane)
26006     {
26007         //Roo.log('tab - prev default');
26008         ev.preventDefault();
26009         
26010         
26011         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26012         pane.tab.addClass('active');
26013         //Roo.log(pane.title);
26014         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26015         // technically we should have a deactivate event.. but maybe add later.
26016         // and it should not de-activate the selected tab...
26017         this.fireEvent('activatepane', pane);
26018         pane.el.addClass('active');
26019         pane.fireEvent('activate');
26020         
26021         
26022     },
26023     
26024     getActivePane : function()
26025     {
26026         var r = false;
26027         Roo.each(this.panes, function(p) {
26028             if(p.el.hasClass('active')){
26029                 r = p;
26030                 return false;
26031             }
26032             
26033             return;
26034         });
26035         
26036         return r;
26037     }
26038     
26039     
26040 });
26041
26042  
26043 /*
26044  * - LGPL
26045  *
26046  * Tab pane
26047  * 
26048  */
26049 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26050 /**
26051  * @class Roo.bootstrap.TabPane
26052  * @extends Roo.bootstrap.Component
26053  * Bootstrap TabPane class
26054  * @cfg {Boolean} active (false | true) Default false
26055  * @cfg {String} title title of panel
26056
26057  * 
26058  * @constructor
26059  * Create a new TabPane
26060  * @param {Object} config The config object
26061  */
26062
26063 Roo.bootstrap.dash.TabPane = function(config){
26064     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26065     
26066     this.addEvents({
26067         // raw events
26068         /**
26069          * @event activate
26070          * When a pane is activated
26071          * @param {Roo.bootstrap.dash.TabPane} pane
26072          */
26073         "activate" : true
26074          
26075     });
26076 };
26077
26078 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26079     
26080     active : false,
26081     title : '',
26082     
26083     // the tabBox that this is attached to.
26084     tab : false,
26085      
26086     getAutoCreate : function() 
26087     {
26088         var cfg = {
26089             tag: 'div',
26090             cls: 'tab-pane'
26091         };
26092         
26093         if(this.active){
26094             cfg.cls += ' active';
26095         }
26096         
26097         return cfg;
26098     },
26099     initEvents  : function()
26100     {
26101         //Roo.log('trigger add pane handler');
26102         this.parent().fireEvent('addpane', this)
26103     },
26104     
26105      /**
26106      * Updates the tab title 
26107      * @param {String} html to set the title to.
26108      */
26109     setTitle: function(str)
26110     {
26111         if (!this.tab) {
26112             return;
26113         }
26114         this.title = str;
26115         this.tab.select('a', true).first().dom.innerHTML = str;
26116         
26117     }
26118     
26119     
26120     
26121 });
26122
26123  
26124
26125
26126  /*
26127  * - LGPL
26128  *
26129  * menu
26130  * 
26131  */
26132 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26133
26134 /**
26135  * @class Roo.bootstrap.menu.Menu
26136  * @extends Roo.bootstrap.Component
26137  * Bootstrap Menu class - container for Menu
26138  * @cfg {String} html Text of the menu
26139  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26140  * @cfg {String} icon Font awesome icon
26141  * @cfg {String} pos Menu align to (top | bottom) default bottom
26142  * 
26143  * 
26144  * @constructor
26145  * Create a new Menu
26146  * @param {Object} config The config object
26147  */
26148
26149
26150 Roo.bootstrap.menu.Menu = function(config){
26151     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26152     
26153     this.addEvents({
26154         /**
26155          * @event beforeshow
26156          * Fires before this menu is displayed
26157          * @param {Roo.bootstrap.menu.Menu} this
26158          */
26159         beforeshow : true,
26160         /**
26161          * @event beforehide
26162          * Fires before this menu is hidden
26163          * @param {Roo.bootstrap.menu.Menu} this
26164          */
26165         beforehide : true,
26166         /**
26167          * @event show
26168          * Fires after this menu is displayed
26169          * @param {Roo.bootstrap.menu.Menu} this
26170          */
26171         show : true,
26172         /**
26173          * @event hide
26174          * Fires after this menu is hidden
26175          * @param {Roo.bootstrap.menu.Menu} this
26176          */
26177         hide : true,
26178         /**
26179          * @event click
26180          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26181          * @param {Roo.bootstrap.menu.Menu} this
26182          * @param {Roo.EventObject} e
26183          */
26184         click : true
26185     });
26186     
26187 };
26188
26189 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26190     
26191     submenu : false,
26192     html : '',
26193     weight : 'default',
26194     icon : false,
26195     pos : 'bottom',
26196     
26197     
26198     getChildContainer : function() {
26199         if(this.isSubMenu){
26200             return this.el;
26201         }
26202         
26203         return this.el.select('ul.dropdown-menu', true).first();  
26204     },
26205     
26206     getAutoCreate : function()
26207     {
26208         var text = [
26209             {
26210                 tag : 'span',
26211                 cls : 'roo-menu-text',
26212                 html : this.html
26213             }
26214         ];
26215         
26216         if(this.icon){
26217             text.unshift({
26218                 tag : 'i',
26219                 cls : 'fa ' + this.icon
26220             })
26221         }
26222         
26223         
26224         var cfg = {
26225             tag : 'div',
26226             cls : 'btn-group',
26227             cn : [
26228                 {
26229                     tag : 'button',
26230                     cls : 'dropdown-button btn btn-' + this.weight,
26231                     cn : text
26232                 },
26233                 {
26234                     tag : 'button',
26235                     cls : 'dropdown-toggle btn btn-' + this.weight,
26236                     cn : [
26237                         {
26238                             tag : 'span',
26239                             cls : 'caret'
26240                         }
26241                     ]
26242                 },
26243                 {
26244                     tag : 'ul',
26245                     cls : 'dropdown-menu'
26246                 }
26247             ]
26248             
26249         };
26250         
26251         if(this.pos == 'top'){
26252             cfg.cls += ' dropup';
26253         }
26254         
26255         if(this.isSubMenu){
26256             cfg = {
26257                 tag : 'ul',
26258                 cls : 'dropdown-menu'
26259             }
26260         }
26261         
26262         return cfg;
26263     },
26264     
26265     onRender : function(ct, position)
26266     {
26267         this.isSubMenu = ct.hasClass('dropdown-submenu');
26268         
26269         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26270     },
26271     
26272     initEvents : function() 
26273     {
26274         if(this.isSubMenu){
26275             return;
26276         }
26277         
26278         this.hidden = true;
26279         
26280         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26281         this.triggerEl.on('click', this.onTriggerPress, this);
26282         
26283         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26284         this.buttonEl.on('click', this.onClick, this);
26285         
26286     },
26287     
26288     list : function()
26289     {
26290         if(this.isSubMenu){
26291             return this.el;
26292         }
26293         
26294         return this.el.select('ul.dropdown-menu', true).first();
26295     },
26296     
26297     onClick : function(e)
26298     {
26299         this.fireEvent("click", this, e);
26300     },
26301     
26302     onTriggerPress  : function(e)
26303     {   
26304         if (this.isVisible()) {
26305             this.hide();
26306         } else {
26307             this.show();
26308         }
26309     },
26310     
26311     isVisible : function(){
26312         return !this.hidden;
26313     },
26314     
26315     show : function()
26316     {
26317         this.fireEvent("beforeshow", this);
26318         
26319         this.hidden = false;
26320         this.el.addClass('open');
26321         
26322         Roo.get(document).on("mouseup", this.onMouseUp, this);
26323         
26324         this.fireEvent("show", this);
26325         
26326         
26327     },
26328     
26329     hide : function()
26330     {
26331         this.fireEvent("beforehide", this);
26332         
26333         this.hidden = true;
26334         this.el.removeClass('open');
26335         
26336         Roo.get(document).un("mouseup", this.onMouseUp);
26337         
26338         this.fireEvent("hide", this);
26339     },
26340     
26341     onMouseUp : function()
26342     {
26343         this.hide();
26344     }
26345     
26346 });
26347
26348  
26349  /*
26350  * - LGPL
26351  *
26352  * menu item
26353  * 
26354  */
26355 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26356
26357 /**
26358  * @class Roo.bootstrap.menu.Item
26359  * @extends Roo.bootstrap.Component
26360  * Bootstrap MenuItem class
26361  * @cfg {Boolean} submenu (true | false) default false
26362  * @cfg {String} html text of the item
26363  * @cfg {String} href the link
26364  * @cfg {Boolean} disable (true | false) default false
26365  * @cfg {Boolean} preventDefault (true | false) default true
26366  * @cfg {String} icon Font awesome icon
26367  * @cfg {String} pos Submenu align to (left | right) default right 
26368  * 
26369  * 
26370  * @constructor
26371  * Create a new Item
26372  * @param {Object} config The config object
26373  */
26374
26375
26376 Roo.bootstrap.menu.Item = function(config){
26377     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26378     this.addEvents({
26379         /**
26380          * @event mouseover
26381          * Fires when the mouse is hovering over this menu
26382          * @param {Roo.bootstrap.menu.Item} this
26383          * @param {Roo.EventObject} e
26384          */
26385         mouseover : true,
26386         /**
26387          * @event mouseout
26388          * Fires when the mouse exits this menu
26389          * @param {Roo.bootstrap.menu.Item} this
26390          * @param {Roo.EventObject} e
26391          */
26392         mouseout : true,
26393         // raw events
26394         /**
26395          * @event click
26396          * The raw click event for the entire grid.
26397          * @param {Roo.EventObject} e
26398          */
26399         click : true
26400     });
26401 };
26402
26403 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26404     
26405     submenu : false,
26406     href : '',
26407     html : '',
26408     preventDefault: true,
26409     disable : false,
26410     icon : false,
26411     pos : 'right',
26412     
26413     getAutoCreate : function()
26414     {
26415         var text = [
26416             {
26417                 tag : 'span',
26418                 cls : 'roo-menu-item-text',
26419                 html : this.html
26420             }
26421         ];
26422         
26423         if(this.icon){
26424             text.unshift({
26425                 tag : 'i',
26426                 cls : 'fa ' + this.icon
26427             })
26428         }
26429         
26430         var cfg = {
26431             tag : 'li',
26432             cn : [
26433                 {
26434                     tag : 'a',
26435                     href : this.href || '#',
26436                     cn : text
26437                 }
26438             ]
26439         };
26440         
26441         if(this.disable){
26442             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26443         }
26444         
26445         if(this.submenu){
26446             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26447             
26448             if(this.pos == 'left'){
26449                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26450             }
26451         }
26452         
26453         return cfg;
26454     },
26455     
26456     initEvents : function() 
26457     {
26458         this.el.on('mouseover', this.onMouseOver, this);
26459         this.el.on('mouseout', this.onMouseOut, this);
26460         
26461         this.el.select('a', true).first().on('click', this.onClick, this);
26462         
26463     },
26464     
26465     onClick : function(e)
26466     {
26467         if(this.preventDefault){
26468             e.preventDefault();
26469         }
26470         
26471         this.fireEvent("click", this, e);
26472     },
26473     
26474     onMouseOver : function(e)
26475     {
26476         if(this.submenu && this.pos == 'left'){
26477             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26478         }
26479         
26480         this.fireEvent("mouseover", this, e);
26481     },
26482     
26483     onMouseOut : function(e)
26484     {
26485         this.fireEvent("mouseout", this, e);
26486     }
26487 });
26488
26489  
26490
26491  /*
26492  * - LGPL
26493  *
26494  * menu separator
26495  * 
26496  */
26497 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26498
26499 /**
26500  * @class Roo.bootstrap.menu.Separator
26501  * @extends Roo.bootstrap.Component
26502  * Bootstrap Separator class
26503  * 
26504  * @constructor
26505  * Create a new Separator
26506  * @param {Object} config The config object
26507  */
26508
26509
26510 Roo.bootstrap.menu.Separator = function(config){
26511     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26512 };
26513
26514 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26515     
26516     getAutoCreate : function(){
26517         var cfg = {
26518             tag : 'li',
26519             cls: 'divider'
26520         };
26521         
26522         return cfg;
26523     }
26524    
26525 });
26526
26527  
26528
26529  /*
26530  * - LGPL
26531  *
26532  * Tooltip
26533  * 
26534  */
26535
26536 /**
26537  * @class Roo.bootstrap.Tooltip
26538  * Bootstrap Tooltip class
26539  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26540  * to determine which dom element triggers the tooltip.
26541  * 
26542  * It needs to add support for additional attributes like tooltip-position
26543  * 
26544  * @constructor
26545  * Create a new Toolti
26546  * @param {Object} config The config object
26547  */
26548
26549 Roo.bootstrap.Tooltip = function(config){
26550     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26551     
26552     this.alignment = Roo.bootstrap.Tooltip.alignment;
26553     
26554     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26555         this.alignment = config.alignment;
26556     }
26557     
26558 };
26559
26560 Roo.apply(Roo.bootstrap.Tooltip, {
26561     /**
26562      * @function init initialize tooltip monitoring.
26563      * @static
26564      */
26565     currentEl : false,
26566     currentTip : false,
26567     currentRegion : false,
26568     
26569     //  init : delay?
26570     
26571     init : function()
26572     {
26573         Roo.get(document).on('mouseover', this.enter ,this);
26574         Roo.get(document).on('mouseout', this.leave, this);
26575          
26576         
26577         this.currentTip = new Roo.bootstrap.Tooltip();
26578     },
26579     
26580     enter : function(ev)
26581     {
26582         var dom = ev.getTarget();
26583         
26584         //Roo.log(['enter',dom]);
26585         var el = Roo.fly(dom);
26586         if (this.currentEl) {
26587             //Roo.log(dom);
26588             //Roo.log(this.currentEl);
26589             //Roo.log(this.currentEl.contains(dom));
26590             if (this.currentEl == el) {
26591                 return;
26592             }
26593             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26594                 return;
26595             }
26596
26597         }
26598         
26599         if (this.currentTip.el) {
26600             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26601         }    
26602         //Roo.log(ev);
26603         
26604         if(!el || el.dom == document){
26605             return;
26606         }
26607         
26608         var bindEl = el;
26609         
26610         // you can not look for children, as if el is the body.. then everythign is the child..
26611         if (!el.attr('tooltip')) { //
26612             if (!el.select("[tooltip]").elements.length) {
26613                 return;
26614             }
26615             // is the mouse over this child...?
26616             bindEl = el.select("[tooltip]").first();
26617             var xy = ev.getXY();
26618             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26619                 //Roo.log("not in region.");
26620                 return;
26621             }
26622             //Roo.log("child element over..");
26623             
26624         }
26625         this.currentEl = bindEl;
26626         this.currentTip.bind(bindEl);
26627         this.currentRegion = Roo.lib.Region.getRegion(dom);
26628         this.currentTip.enter();
26629         
26630     },
26631     leave : function(ev)
26632     {
26633         var dom = ev.getTarget();
26634         //Roo.log(['leave',dom]);
26635         if (!this.currentEl) {
26636             return;
26637         }
26638         
26639         
26640         if (dom != this.currentEl.dom) {
26641             return;
26642         }
26643         var xy = ev.getXY();
26644         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26645             return;
26646         }
26647         // only activate leave if mouse cursor is outside... bounding box..
26648         
26649         
26650         
26651         
26652         if (this.currentTip) {
26653             this.currentTip.leave();
26654         }
26655         //Roo.log('clear currentEl');
26656         this.currentEl = false;
26657         
26658         
26659     },
26660     alignment : {
26661         'left' : ['r-l', [-2,0], 'right'],
26662         'right' : ['l-r', [2,0], 'left'],
26663         'bottom' : ['t-b', [0,2], 'top'],
26664         'top' : [ 'b-t', [0,-2], 'bottom']
26665     }
26666     
26667 });
26668
26669
26670 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26671     
26672     
26673     bindEl : false,
26674     
26675     delay : null, // can be { show : 300 , hide: 500}
26676     
26677     timeout : null,
26678     
26679     hoverState : null, //???
26680     
26681     placement : 'bottom', 
26682     
26683     alignment : false,
26684     
26685     getAutoCreate : function(){
26686     
26687         var cfg = {
26688            cls : 'tooltip',
26689            role : 'tooltip',
26690            cn : [
26691                 {
26692                     cls : 'tooltip-arrow'
26693                 },
26694                 {
26695                     cls : 'tooltip-inner'
26696                 }
26697            ]
26698         };
26699         
26700         return cfg;
26701     },
26702     bind : function(el)
26703     {
26704         this.bindEl = el;
26705     },
26706       
26707     
26708     enter : function () {
26709        
26710         if (this.timeout != null) {
26711             clearTimeout(this.timeout);
26712         }
26713         
26714         this.hoverState = 'in';
26715          //Roo.log("enter - show");
26716         if (!this.delay || !this.delay.show) {
26717             this.show();
26718             return;
26719         }
26720         var _t = this;
26721         this.timeout = setTimeout(function () {
26722             if (_t.hoverState == 'in') {
26723                 _t.show();
26724             }
26725         }, this.delay.show);
26726     },
26727     leave : function()
26728     {
26729         clearTimeout(this.timeout);
26730     
26731         this.hoverState = 'out';
26732          if (!this.delay || !this.delay.hide) {
26733             this.hide();
26734             return;
26735         }
26736        
26737         var _t = this;
26738         this.timeout = setTimeout(function () {
26739             //Roo.log("leave - timeout");
26740             
26741             if (_t.hoverState == 'out') {
26742                 _t.hide();
26743                 Roo.bootstrap.Tooltip.currentEl = false;
26744             }
26745         }, delay);
26746     },
26747     
26748     show : function (msg)
26749     {
26750         if (!this.el) {
26751             this.render(document.body);
26752         }
26753         // set content.
26754         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26755         
26756         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26757         
26758         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26759         
26760         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26761         
26762         var placement = typeof this.placement == 'function' ?
26763             this.placement.call(this, this.el, on_el) :
26764             this.placement;
26765             
26766         var autoToken = /\s?auto?\s?/i;
26767         var autoPlace = autoToken.test(placement);
26768         if (autoPlace) {
26769             placement = placement.replace(autoToken, '') || 'top';
26770         }
26771         
26772         //this.el.detach()
26773         //this.el.setXY([0,0]);
26774         this.el.show();
26775         //this.el.dom.style.display='block';
26776         
26777         //this.el.appendTo(on_el);
26778         
26779         var p = this.getPosition();
26780         var box = this.el.getBox();
26781         
26782         if (autoPlace) {
26783             // fixme..
26784         }
26785         
26786         var align = this.alignment[placement];
26787         
26788         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26789         
26790         if(placement == 'top' || placement == 'bottom'){
26791             if(xy[0] < 0){
26792                 placement = 'right';
26793             }
26794             
26795             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26796                 placement = 'left';
26797             }
26798             
26799             var scroll = Roo.select('body', true).first().getScroll();
26800             
26801             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26802                 placement = 'top';
26803             }
26804             
26805             align = this.alignment[placement];
26806         }
26807         
26808         this.el.alignTo(this.bindEl, align[0],align[1]);
26809         //var arrow = this.el.select('.arrow',true).first();
26810         //arrow.set(align[2], 
26811         
26812         this.el.addClass(placement);
26813         
26814         this.el.addClass('in fade');
26815         
26816         this.hoverState = null;
26817         
26818         if (this.el.hasClass('fade')) {
26819             // fade it?
26820         }
26821         
26822     },
26823     hide : function()
26824     {
26825          
26826         if (!this.el) {
26827             return;
26828         }
26829         //this.el.setXY([0,0]);
26830         this.el.removeClass('in');
26831         //this.el.hide();
26832         
26833     }
26834     
26835 });
26836  
26837
26838  /*
26839  * - LGPL
26840  *
26841  * Location Picker
26842  * 
26843  */
26844
26845 /**
26846  * @class Roo.bootstrap.LocationPicker
26847  * @extends Roo.bootstrap.Component
26848  * Bootstrap LocationPicker class
26849  * @cfg {Number} latitude Position when init default 0
26850  * @cfg {Number} longitude Position when init default 0
26851  * @cfg {Number} zoom default 15
26852  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26853  * @cfg {Boolean} mapTypeControl default false
26854  * @cfg {Boolean} disableDoubleClickZoom default false
26855  * @cfg {Boolean} scrollwheel default true
26856  * @cfg {Boolean} streetViewControl default false
26857  * @cfg {Number} radius default 0
26858  * @cfg {String} locationName
26859  * @cfg {Boolean} draggable default true
26860  * @cfg {Boolean} enableAutocomplete default false
26861  * @cfg {Boolean} enableReverseGeocode default true
26862  * @cfg {String} markerTitle
26863  * 
26864  * @constructor
26865  * Create a new LocationPicker
26866  * @param {Object} config The config object
26867  */
26868
26869
26870 Roo.bootstrap.LocationPicker = function(config){
26871     
26872     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26873     
26874     this.addEvents({
26875         /**
26876          * @event initial
26877          * Fires when the picker initialized.
26878          * @param {Roo.bootstrap.LocationPicker} this
26879          * @param {Google Location} location
26880          */
26881         initial : true,
26882         /**
26883          * @event positionchanged
26884          * Fires when the picker position changed.
26885          * @param {Roo.bootstrap.LocationPicker} this
26886          * @param {Google Location} location
26887          */
26888         positionchanged : true,
26889         /**
26890          * @event resize
26891          * Fires when the map resize.
26892          * @param {Roo.bootstrap.LocationPicker} this
26893          */
26894         resize : true,
26895         /**
26896          * @event show
26897          * Fires when the map show.
26898          * @param {Roo.bootstrap.LocationPicker} this
26899          */
26900         show : true,
26901         /**
26902          * @event hide
26903          * Fires when the map hide.
26904          * @param {Roo.bootstrap.LocationPicker} this
26905          */
26906         hide : true,
26907         /**
26908          * @event mapClick
26909          * Fires when click the map.
26910          * @param {Roo.bootstrap.LocationPicker} this
26911          * @param {Map event} e
26912          */
26913         mapClick : true,
26914         /**
26915          * @event mapRightClick
26916          * Fires when right click the map.
26917          * @param {Roo.bootstrap.LocationPicker} this
26918          * @param {Map event} e
26919          */
26920         mapRightClick : true,
26921         /**
26922          * @event markerClick
26923          * Fires when click the marker.
26924          * @param {Roo.bootstrap.LocationPicker} this
26925          * @param {Map event} e
26926          */
26927         markerClick : true,
26928         /**
26929          * @event markerRightClick
26930          * Fires when right click the marker.
26931          * @param {Roo.bootstrap.LocationPicker} this
26932          * @param {Map event} e
26933          */
26934         markerRightClick : true,
26935         /**
26936          * @event OverlayViewDraw
26937          * Fires when OverlayView Draw
26938          * @param {Roo.bootstrap.LocationPicker} this
26939          */
26940         OverlayViewDraw : true,
26941         /**
26942          * @event OverlayViewOnAdd
26943          * Fires when OverlayView Draw
26944          * @param {Roo.bootstrap.LocationPicker} this
26945          */
26946         OverlayViewOnAdd : true,
26947         /**
26948          * @event OverlayViewOnRemove
26949          * Fires when OverlayView Draw
26950          * @param {Roo.bootstrap.LocationPicker} this
26951          */
26952         OverlayViewOnRemove : true,
26953         /**
26954          * @event OverlayViewShow
26955          * Fires when OverlayView Draw
26956          * @param {Roo.bootstrap.LocationPicker} this
26957          * @param {Pixel} cpx
26958          */
26959         OverlayViewShow : true,
26960         /**
26961          * @event OverlayViewHide
26962          * Fires when OverlayView Draw
26963          * @param {Roo.bootstrap.LocationPicker} this
26964          */
26965         OverlayViewHide : true,
26966         /**
26967          * @event loadexception
26968          * Fires when load google lib failed.
26969          * @param {Roo.bootstrap.LocationPicker} this
26970          */
26971         loadexception : true
26972     });
26973         
26974 };
26975
26976 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26977     
26978     gMapContext: false,
26979     
26980     latitude: 0,
26981     longitude: 0,
26982     zoom: 15,
26983     mapTypeId: false,
26984     mapTypeControl: false,
26985     disableDoubleClickZoom: false,
26986     scrollwheel: true,
26987     streetViewControl: false,
26988     radius: 0,
26989     locationName: '',
26990     draggable: true,
26991     enableAutocomplete: false,
26992     enableReverseGeocode: true,
26993     markerTitle: '',
26994     
26995     getAutoCreate: function()
26996     {
26997
26998         var cfg = {
26999             tag: 'div',
27000             cls: 'roo-location-picker'
27001         };
27002         
27003         return cfg
27004     },
27005     
27006     initEvents: function(ct, position)
27007     {       
27008         if(!this.el.getWidth() || this.isApplied()){
27009             return;
27010         }
27011         
27012         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27013         
27014         this.initial();
27015     },
27016     
27017     initial: function()
27018     {
27019         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27020             this.fireEvent('loadexception', this);
27021             return;
27022         }
27023         
27024         if(!this.mapTypeId){
27025             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27026         }
27027         
27028         this.gMapContext = this.GMapContext();
27029         
27030         this.initOverlayView();
27031         
27032         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27033         
27034         var _this = this;
27035                 
27036         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27037             _this.setPosition(_this.gMapContext.marker.position);
27038         });
27039         
27040         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27041             _this.fireEvent('mapClick', this, event);
27042             
27043         });
27044
27045         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27046             _this.fireEvent('mapRightClick', this, event);
27047             
27048         });
27049         
27050         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27051             _this.fireEvent('markerClick', this, event);
27052             
27053         });
27054
27055         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27056             _this.fireEvent('markerRightClick', this, event);
27057             
27058         });
27059         
27060         this.setPosition(this.gMapContext.location);
27061         
27062         this.fireEvent('initial', this, this.gMapContext.location);
27063     },
27064     
27065     initOverlayView: function()
27066     {
27067         var _this = this;
27068         
27069         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27070             
27071             draw: function()
27072             {
27073                 _this.fireEvent('OverlayViewDraw', _this);
27074             },
27075             
27076             onAdd: function()
27077             {
27078                 _this.fireEvent('OverlayViewOnAdd', _this);
27079             },
27080             
27081             onRemove: function()
27082             {
27083                 _this.fireEvent('OverlayViewOnRemove', _this);
27084             },
27085             
27086             show: function(cpx)
27087             {
27088                 _this.fireEvent('OverlayViewShow', _this, cpx);
27089             },
27090             
27091             hide: function()
27092             {
27093                 _this.fireEvent('OverlayViewHide', _this);
27094             }
27095             
27096         });
27097     },
27098     
27099     fromLatLngToContainerPixel: function(event)
27100     {
27101         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27102     },
27103     
27104     isApplied: function() 
27105     {
27106         return this.getGmapContext() == false ? false : true;
27107     },
27108     
27109     getGmapContext: function() 
27110     {
27111         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27112     },
27113     
27114     GMapContext: function() 
27115     {
27116         var position = new google.maps.LatLng(this.latitude, this.longitude);
27117         
27118         var _map = new google.maps.Map(this.el.dom, {
27119             center: position,
27120             zoom: this.zoom,
27121             mapTypeId: this.mapTypeId,
27122             mapTypeControl: this.mapTypeControl,
27123             disableDoubleClickZoom: this.disableDoubleClickZoom,
27124             scrollwheel: this.scrollwheel,
27125             streetViewControl: this.streetViewControl,
27126             locationName: this.locationName,
27127             draggable: this.draggable,
27128             enableAutocomplete: this.enableAutocomplete,
27129             enableReverseGeocode: this.enableReverseGeocode
27130         });
27131         
27132         var _marker = new google.maps.Marker({
27133             position: position,
27134             map: _map,
27135             title: this.markerTitle,
27136             draggable: this.draggable
27137         });
27138         
27139         return {
27140             map: _map,
27141             marker: _marker,
27142             circle: null,
27143             location: position,
27144             radius: this.radius,
27145             locationName: this.locationName,
27146             addressComponents: {
27147                 formatted_address: null,
27148                 addressLine1: null,
27149                 addressLine2: null,
27150                 streetName: null,
27151                 streetNumber: null,
27152                 city: null,
27153                 district: null,
27154                 state: null,
27155                 stateOrProvince: null
27156             },
27157             settings: this,
27158             domContainer: this.el.dom,
27159             geodecoder: new google.maps.Geocoder()
27160         };
27161     },
27162     
27163     drawCircle: function(center, radius, options) 
27164     {
27165         if (this.gMapContext.circle != null) {
27166             this.gMapContext.circle.setMap(null);
27167         }
27168         if (radius > 0) {
27169             radius *= 1;
27170             options = Roo.apply({}, options, {
27171                 strokeColor: "#0000FF",
27172                 strokeOpacity: .35,
27173                 strokeWeight: 2,
27174                 fillColor: "#0000FF",
27175                 fillOpacity: .2
27176             });
27177             
27178             options.map = this.gMapContext.map;
27179             options.radius = radius;
27180             options.center = center;
27181             this.gMapContext.circle = new google.maps.Circle(options);
27182             return this.gMapContext.circle;
27183         }
27184         
27185         return null;
27186     },
27187     
27188     setPosition: function(location) 
27189     {
27190         this.gMapContext.location = location;
27191         this.gMapContext.marker.setPosition(location);
27192         this.gMapContext.map.panTo(location);
27193         this.drawCircle(location, this.gMapContext.radius, {});
27194         
27195         var _this = this;
27196         
27197         if (this.gMapContext.settings.enableReverseGeocode) {
27198             this.gMapContext.geodecoder.geocode({
27199                 latLng: this.gMapContext.location
27200             }, function(results, status) {
27201                 
27202                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27203                     _this.gMapContext.locationName = results[0].formatted_address;
27204                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27205                     
27206                     _this.fireEvent('positionchanged', this, location);
27207                 }
27208             });
27209             
27210             return;
27211         }
27212         
27213         this.fireEvent('positionchanged', this, location);
27214     },
27215     
27216     resize: function()
27217     {
27218         google.maps.event.trigger(this.gMapContext.map, "resize");
27219         
27220         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27221         
27222         this.fireEvent('resize', this);
27223     },
27224     
27225     setPositionByLatLng: function(latitude, longitude)
27226     {
27227         this.setPosition(new google.maps.LatLng(latitude, longitude));
27228     },
27229     
27230     getCurrentPosition: function() 
27231     {
27232         return {
27233             latitude: this.gMapContext.location.lat(),
27234             longitude: this.gMapContext.location.lng()
27235         };
27236     },
27237     
27238     getAddressName: function() 
27239     {
27240         return this.gMapContext.locationName;
27241     },
27242     
27243     getAddressComponents: function() 
27244     {
27245         return this.gMapContext.addressComponents;
27246     },
27247     
27248     address_component_from_google_geocode: function(address_components) 
27249     {
27250         var result = {};
27251         
27252         for (var i = 0; i < address_components.length; i++) {
27253             var component = address_components[i];
27254             if (component.types.indexOf("postal_code") >= 0) {
27255                 result.postalCode = component.short_name;
27256             } else if (component.types.indexOf("street_number") >= 0) {
27257                 result.streetNumber = component.short_name;
27258             } else if (component.types.indexOf("route") >= 0) {
27259                 result.streetName = component.short_name;
27260             } else if (component.types.indexOf("neighborhood") >= 0) {
27261                 result.city = component.short_name;
27262             } else if (component.types.indexOf("locality") >= 0) {
27263                 result.city = component.short_name;
27264             } else if (component.types.indexOf("sublocality") >= 0) {
27265                 result.district = component.short_name;
27266             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27267                 result.stateOrProvince = component.short_name;
27268             } else if (component.types.indexOf("country") >= 0) {
27269                 result.country = component.short_name;
27270             }
27271         }
27272         
27273         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27274         result.addressLine2 = "";
27275         return result;
27276     },
27277     
27278     setZoomLevel: function(zoom)
27279     {
27280         this.gMapContext.map.setZoom(zoom);
27281     },
27282     
27283     show: function()
27284     {
27285         if(!this.el){
27286             return;
27287         }
27288         
27289         this.el.show();
27290         
27291         this.resize();
27292         
27293         this.fireEvent('show', this);
27294     },
27295     
27296     hide: function()
27297     {
27298         if(!this.el){
27299             return;
27300         }
27301         
27302         this.el.hide();
27303         
27304         this.fireEvent('hide', this);
27305     }
27306     
27307 });
27308
27309 Roo.apply(Roo.bootstrap.LocationPicker, {
27310     
27311     OverlayView : function(map, options)
27312     {
27313         options = options || {};
27314         
27315         this.setMap(map);
27316     }
27317     
27318     
27319 });/**
27320  * @class Roo.bootstrap.Alert
27321  * @extends Roo.bootstrap.Component
27322  * Bootstrap Alert class - shows an alert area box
27323  * eg
27324  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27325   Enter a valid email address
27326 </div>
27327  * @licence LGPL
27328  * @cfg {String} title The title of alert
27329  * @cfg {String} html The content of alert
27330  * @cfg {String} weight (  success | info | warning | danger )
27331  * @cfg {String} faicon font-awesomeicon
27332  * 
27333  * @constructor
27334  * Create a new alert
27335  * @param {Object} config The config object
27336  */
27337
27338
27339 Roo.bootstrap.Alert = function(config){
27340     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27341     
27342 };
27343
27344 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27345     
27346     title: '',
27347     html: '',
27348     weight: false,
27349     faicon: false,
27350     
27351     getAutoCreate : function()
27352     {
27353         
27354         var cfg = {
27355             tag : 'div',
27356             cls : 'alert',
27357             cn : [
27358                 {
27359                     tag : 'i',
27360                     cls : 'roo-alert-icon'
27361                     
27362                 },
27363                 {
27364                     tag : 'b',
27365                     cls : 'roo-alert-title',
27366                     html : this.title
27367                 },
27368                 {
27369                     tag : 'span',
27370                     cls : 'roo-alert-text',
27371                     html : this.html
27372                 }
27373             ]
27374         };
27375         
27376         if(this.faicon){
27377             cfg.cn[0].cls += ' fa ' + this.faicon;
27378         }
27379         
27380         if(this.weight){
27381             cfg.cls += ' alert-' + this.weight;
27382         }
27383         
27384         return cfg;
27385     },
27386     
27387     initEvents: function() 
27388     {
27389         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27390     },
27391     
27392     setTitle : function(str)
27393     {
27394         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27395     },
27396     
27397     setText : function(str)
27398     {
27399         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27400     },
27401     
27402     setWeight : function(weight)
27403     {
27404         if(this.weight){
27405             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27406         }
27407         
27408         this.weight = weight;
27409         
27410         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27411     },
27412     
27413     setIcon : function(icon)
27414     {
27415         if(this.faicon){
27416             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27417         }
27418         
27419         this.faicon = icon;
27420         
27421         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27422     },
27423     
27424     hide: function() 
27425     {
27426         this.el.hide();   
27427     },
27428     
27429     show: function() 
27430     {  
27431         this.el.show();   
27432     }
27433     
27434 });
27435
27436  
27437 /*
27438 * Licence: LGPL
27439 */
27440
27441 /**
27442  * @class Roo.bootstrap.UploadCropbox
27443  * @extends Roo.bootstrap.Component
27444  * Bootstrap UploadCropbox class
27445  * @cfg {String} emptyText show when image has been loaded
27446  * @cfg {String} rotateNotify show when image too small to rotate
27447  * @cfg {Number} errorTimeout default 3000
27448  * @cfg {Number} minWidth default 300
27449  * @cfg {Number} minHeight default 300
27450  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27451  * @cfg {Boolean} isDocument (true|false) default false
27452  * @cfg {String} url action url
27453  * @cfg {String} paramName default 'imageUpload'
27454  * @cfg {String} method default POST
27455  * @cfg {Boolean} loadMask (true|false) default true
27456  * @cfg {Boolean} loadingText default 'Loading...'
27457  * 
27458  * @constructor
27459  * Create a new UploadCropbox
27460  * @param {Object} config The config object
27461  */
27462
27463 Roo.bootstrap.UploadCropbox = function(config){
27464     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27465     
27466     this.addEvents({
27467         /**
27468          * @event beforeselectfile
27469          * Fire before select file
27470          * @param {Roo.bootstrap.UploadCropbox} this
27471          */
27472         "beforeselectfile" : true,
27473         /**
27474          * @event initial
27475          * Fire after initEvent
27476          * @param {Roo.bootstrap.UploadCropbox} this
27477          */
27478         "initial" : true,
27479         /**
27480          * @event crop
27481          * Fire after initEvent
27482          * @param {Roo.bootstrap.UploadCropbox} this
27483          * @param {String} data
27484          */
27485         "crop" : true,
27486         /**
27487          * @event prepare
27488          * Fire when preparing the file data
27489          * @param {Roo.bootstrap.UploadCropbox} this
27490          * @param {Object} file
27491          */
27492         "prepare" : true,
27493         /**
27494          * @event exception
27495          * Fire when get exception
27496          * @param {Roo.bootstrap.UploadCropbox} this
27497          * @param {XMLHttpRequest} xhr
27498          */
27499         "exception" : true,
27500         /**
27501          * @event beforeloadcanvas
27502          * Fire before load the canvas
27503          * @param {Roo.bootstrap.UploadCropbox} this
27504          * @param {String} src
27505          */
27506         "beforeloadcanvas" : true,
27507         /**
27508          * @event trash
27509          * Fire when trash image
27510          * @param {Roo.bootstrap.UploadCropbox} this
27511          */
27512         "trash" : true,
27513         /**
27514          * @event download
27515          * Fire when download the image
27516          * @param {Roo.bootstrap.UploadCropbox} this
27517          */
27518         "download" : true,
27519         /**
27520          * @event footerbuttonclick
27521          * Fire when footerbuttonclick
27522          * @param {Roo.bootstrap.UploadCropbox} this
27523          * @param {String} type
27524          */
27525         "footerbuttonclick" : true,
27526         /**
27527          * @event resize
27528          * Fire when resize
27529          * @param {Roo.bootstrap.UploadCropbox} this
27530          */
27531         "resize" : true,
27532         /**
27533          * @event rotate
27534          * Fire when rotate the image
27535          * @param {Roo.bootstrap.UploadCropbox} this
27536          * @param {String} pos
27537          */
27538         "rotate" : true,
27539         /**
27540          * @event inspect
27541          * Fire when inspect the file
27542          * @param {Roo.bootstrap.UploadCropbox} this
27543          * @param {Object} file
27544          */
27545         "inspect" : true,
27546         /**
27547          * @event upload
27548          * Fire when xhr upload the file
27549          * @param {Roo.bootstrap.UploadCropbox} this
27550          * @param {Object} data
27551          */
27552         "upload" : true,
27553         /**
27554          * @event arrange
27555          * Fire when arrange the file data
27556          * @param {Roo.bootstrap.UploadCropbox} this
27557          * @param {Object} formData
27558          */
27559         "arrange" : true
27560     });
27561     
27562     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27563 };
27564
27565 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27566     
27567     emptyText : 'Click to upload image',
27568     rotateNotify : 'Image is too small to rotate',
27569     errorTimeout : 3000,
27570     scale : 0,
27571     baseScale : 1,
27572     rotate : 0,
27573     dragable : false,
27574     pinching : false,
27575     mouseX : 0,
27576     mouseY : 0,
27577     cropData : false,
27578     minWidth : 300,
27579     minHeight : 300,
27580     file : false,
27581     exif : {},
27582     baseRotate : 1,
27583     cropType : 'image/jpeg',
27584     buttons : false,
27585     canvasLoaded : false,
27586     isDocument : false,
27587     method : 'POST',
27588     paramName : 'imageUpload',
27589     loadMask : true,
27590     loadingText : 'Loading...',
27591     maskEl : false,
27592     
27593     getAutoCreate : function()
27594     {
27595         var cfg = {
27596             tag : 'div',
27597             cls : 'roo-upload-cropbox',
27598             cn : [
27599                 {
27600                     tag : 'input',
27601                     cls : 'roo-upload-cropbox-selector',
27602                     type : 'file'
27603                 },
27604                 {
27605                     tag : 'div',
27606                     cls : 'roo-upload-cropbox-body',
27607                     style : 'cursor:pointer',
27608                     cn : [
27609                         {
27610                             tag : 'div',
27611                             cls : 'roo-upload-cropbox-preview'
27612                         },
27613                         {
27614                             tag : 'div',
27615                             cls : 'roo-upload-cropbox-thumb'
27616                         },
27617                         {
27618                             tag : 'div',
27619                             cls : 'roo-upload-cropbox-empty-notify',
27620                             html : this.emptyText
27621                         },
27622                         {
27623                             tag : 'div',
27624                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27625                             html : this.rotateNotify
27626                         }
27627                     ]
27628                 },
27629                 {
27630                     tag : 'div',
27631                     cls : 'roo-upload-cropbox-footer',
27632                     cn : {
27633                         tag : 'div',
27634                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27635                         cn : []
27636                     }
27637                 }
27638             ]
27639         };
27640         
27641         return cfg;
27642     },
27643     
27644     onRender : function(ct, position)
27645     {
27646         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27647         
27648         if (this.buttons.length) {
27649             
27650             Roo.each(this.buttons, function(bb) {
27651                 
27652                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27653                 
27654                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27655                 
27656             }, this);
27657         }
27658         
27659         if(this.loadMask){
27660             this.maskEl = this.el;
27661         }
27662     },
27663     
27664     initEvents : function()
27665     {
27666         this.urlAPI = (window.createObjectURL && window) || 
27667                                 (window.URL && URL.revokeObjectURL && URL) || 
27668                                 (window.webkitURL && webkitURL);
27669                         
27670         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27671         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27672         
27673         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27674         this.selectorEl.hide();
27675         
27676         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27677         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27678         
27679         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27680         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27681         this.thumbEl.hide();
27682         
27683         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27684         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27685         
27686         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27687         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27688         this.errorEl.hide();
27689         
27690         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27691         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27692         this.footerEl.hide();
27693         
27694         this.setThumbBoxSize();
27695         
27696         this.bind();
27697         
27698         this.resize();
27699         
27700         this.fireEvent('initial', this);
27701     },
27702
27703     bind : function()
27704     {
27705         var _this = this;
27706         
27707         window.addEventListener("resize", function() { _this.resize(); } );
27708         
27709         this.bodyEl.on('click', this.beforeSelectFile, this);
27710         
27711         if(Roo.isTouch){
27712             this.bodyEl.on('touchstart', this.onTouchStart, this);
27713             this.bodyEl.on('touchmove', this.onTouchMove, this);
27714             this.bodyEl.on('touchend', this.onTouchEnd, this);
27715         }
27716         
27717         if(!Roo.isTouch){
27718             this.bodyEl.on('mousedown', this.onMouseDown, this);
27719             this.bodyEl.on('mousemove', this.onMouseMove, this);
27720             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27721             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27722             Roo.get(document).on('mouseup', this.onMouseUp, this);
27723         }
27724         
27725         this.selectorEl.on('change', this.onFileSelected, this);
27726     },
27727     
27728     reset : function()
27729     {    
27730         this.scale = 0;
27731         this.baseScale = 1;
27732         this.rotate = 0;
27733         this.baseRotate = 1;
27734         this.dragable = false;
27735         this.pinching = false;
27736         this.mouseX = 0;
27737         this.mouseY = 0;
27738         this.cropData = false;
27739         this.notifyEl.dom.innerHTML = this.emptyText;
27740         
27741         this.selectorEl.dom.value = '';
27742         
27743     },
27744     
27745     resize : function()
27746     {
27747         if(this.fireEvent('resize', this) != false){
27748             this.setThumbBoxPosition();
27749             this.setCanvasPosition();
27750         }
27751     },
27752     
27753     onFooterButtonClick : function(e, el, o, type)
27754     {
27755         switch (type) {
27756             case 'rotate-left' :
27757                 this.onRotateLeft(e);
27758                 break;
27759             case 'rotate-right' :
27760                 this.onRotateRight(e);
27761                 break;
27762             case 'picture' :
27763                 this.beforeSelectFile(e);
27764                 break;
27765             case 'trash' :
27766                 this.trash(e);
27767                 break;
27768             case 'crop' :
27769                 this.crop(e);
27770                 break;
27771             case 'download' :
27772                 this.download(e);
27773                 break;
27774             default :
27775                 break;
27776         }
27777         
27778         this.fireEvent('footerbuttonclick', this, type);
27779     },
27780     
27781     beforeSelectFile : function(e)
27782     {
27783         e.preventDefault();
27784         
27785         if(this.fireEvent('beforeselectfile', this) != false){
27786             this.selectorEl.dom.click();
27787         }
27788     },
27789     
27790     onFileSelected : function(e)
27791     {
27792         e.preventDefault();
27793         
27794         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27795             return;
27796         }
27797         
27798         var file = this.selectorEl.dom.files[0];
27799         
27800         if(this.fireEvent('inspect', this, file) != false){
27801             this.prepare(file);
27802         }
27803         
27804     },
27805     
27806     trash : function(e)
27807     {
27808         this.fireEvent('trash', this);
27809     },
27810     
27811     download : function(e)
27812     {
27813         this.fireEvent('download', this);
27814     },
27815     
27816     loadCanvas : function(src)
27817     {   
27818         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27819             
27820             this.reset();
27821             
27822             this.imageEl = document.createElement('img');
27823             
27824             var _this = this;
27825             
27826             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27827             
27828             this.imageEl.src = src;
27829         }
27830     },
27831     
27832     onLoadCanvas : function()
27833     {   
27834         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27835         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27836         
27837         this.bodyEl.un('click', this.beforeSelectFile, this);
27838         
27839         this.notifyEl.hide();
27840         this.thumbEl.show();
27841         this.footerEl.show();
27842         
27843         this.baseRotateLevel();
27844         
27845         if(this.isDocument){
27846             this.setThumbBoxSize();
27847         }
27848         
27849         this.setThumbBoxPosition();
27850         
27851         this.baseScaleLevel();
27852         
27853         this.draw();
27854         
27855         this.resize();
27856         
27857         this.canvasLoaded = true;
27858         
27859         if(this.loadMask){
27860             this.maskEl.unmask();
27861         }
27862         
27863     },
27864     
27865     setCanvasPosition : function()
27866     {   
27867         if(!this.canvasEl){
27868             return;
27869         }
27870         
27871         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27872         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27873         
27874         this.previewEl.setLeft(pw);
27875         this.previewEl.setTop(ph);
27876         
27877     },
27878     
27879     onMouseDown : function(e)
27880     {   
27881         e.stopEvent();
27882         
27883         this.dragable = true;
27884         this.pinching = false;
27885         
27886         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27887             this.dragable = false;
27888             return;
27889         }
27890         
27891         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27892         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27893         
27894     },
27895     
27896     onMouseMove : function(e)
27897     {   
27898         e.stopEvent();
27899         
27900         if(!this.canvasLoaded){
27901             return;
27902         }
27903         
27904         if (!this.dragable){
27905             return;
27906         }
27907         
27908         var minX = Math.ceil(this.thumbEl.getLeft(true));
27909         var minY = Math.ceil(this.thumbEl.getTop(true));
27910         
27911         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27912         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27913         
27914         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27915         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27916         
27917         x = x - this.mouseX;
27918         y = y - this.mouseY;
27919         
27920         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27921         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27922         
27923         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27924         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27925         
27926         this.previewEl.setLeft(bgX);
27927         this.previewEl.setTop(bgY);
27928         
27929         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27930         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27931     },
27932     
27933     onMouseUp : function(e)
27934     {   
27935         e.stopEvent();
27936         
27937         this.dragable = false;
27938     },
27939     
27940     onMouseWheel : function(e)
27941     {   
27942         e.stopEvent();
27943         
27944         this.startScale = this.scale;
27945         
27946         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27947         
27948         if(!this.zoomable()){
27949             this.scale = this.startScale;
27950             return;
27951         }
27952         
27953         this.draw();
27954         
27955         return;
27956     },
27957     
27958     zoomable : function()
27959     {
27960         var minScale = this.thumbEl.getWidth() / this.minWidth;
27961         
27962         if(this.minWidth < this.minHeight){
27963             minScale = this.thumbEl.getHeight() / this.minHeight;
27964         }
27965         
27966         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27967         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27968         
27969         if(
27970                 this.isDocument &&
27971                 (this.rotate == 0 || this.rotate == 180) && 
27972                 (
27973                     width > this.imageEl.OriginWidth || 
27974                     height > this.imageEl.OriginHeight ||
27975                     (width < this.minWidth && height < this.minHeight)
27976                 )
27977         ){
27978             return false;
27979         }
27980         
27981         if(
27982                 this.isDocument &&
27983                 (this.rotate == 90 || this.rotate == 270) && 
27984                 (
27985                     width > this.imageEl.OriginWidth || 
27986                     height > this.imageEl.OriginHeight ||
27987                     (width < this.minHeight && height < this.minWidth)
27988                 )
27989         ){
27990             return false;
27991         }
27992         
27993         if(
27994                 !this.isDocument &&
27995                 (this.rotate == 0 || this.rotate == 180) && 
27996                 (
27997                     width < this.minWidth || 
27998                     width > this.imageEl.OriginWidth || 
27999                     height < this.minHeight || 
28000                     height > this.imageEl.OriginHeight
28001                 )
28002         ){
28003             return false;
28004         }
28005         
28006         if(
28007                 !this.isDocument &&
28008                 (this.rotate == 90 || this.rotate == 270) && 
28009                 (
28010                     width < this.minHeight || 
28011                     width > this.imageEl.OriginWidth || 
28012                     height < this.minWidth || 
28013                     height > this.imageEl.OriginHeight
28014                 )
28015         ){
28016             return false;
28017         }
28018         
28019         return true;
28020         
28021     },
28022     
28023     onRotateLeft : function(e)
28024     {   
28025         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28026             
28027             var minScale = this.thumbEl.getWidth() / this.minWidth;
28028             
28029             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28030             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28031             
28032             this.startScale = this.scale;
28033             
28034             while (this.getScaleLevel() < minScale){
28035             
28036                 this.scale = this.scale + 1;
28037                 
28038                 if(!this.zoomable()){
28039                     break;
28040                 }
28041                 
28042                 if(
28043                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28044                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28045                 ){
28046                     continue;
28047                 }
28048                 
28049                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28050
28051                 this.draw();
28052                 
28053                 return;
28054             }
28055             
28056             this.scale = this.startScale;
28057             
28058             this.onRotateFail();
28059             
28060             return false;
28061         }
28062         
28063         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28064
28065         if(this.isDocument){
28066             this.setThumbBoxSize();
28067             this.setThumbBoxPosition();
28068             this.setCanvasPosition();
28069         }
28070         
28071         this.draw();
28072         
28073         this.fireEvent('rotate', this, 'left');
28074         
28075     },
28076     
28077     onRotateRight : function(e)
28078     {
28079         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28080             
28081             var minScale = this.thumbEl.getWidth() / this.minWidth;
28082         
28083             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28084             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28085             
28086             this.startScale = this.scale;
28087             
28088             while (this.getScaleLevel() < minScale){
28089             
28090                 this.scale = this.scale + 1;
28091                 
28092                 if(!this.zoomable()){
28093                     break;
28094                 }
28095                 
28096                 if(
28097                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28098                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28099                 ){
28100                     continue;
28101                 }
28102                 
28103                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28104
28105                 this.draw();
28106                 
28107                 return;
28108             }
28109             
28110             this.scale = this.startScale;
28111             
28112             this.onRotateFail();
28113             
28114             return false;
28115         }
28116         
28117         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28118
28119         if(this.isDocument){
28120             this.setThumbBoxSize();
28121             this.setThumbBoxPosition();
28122             this.setCanvasPosition();
28123         }
28124         
28125         this.draw();
28126         
28127         this.fireEvent('rotate', this, 'right');
28128     },
28129     
28130     onRotateFail : function()
28131     {
28132         this.errorEl.show(true);
28133         
28134         var _this = this;
28135         
28136         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28137     },
28138     
28139     draw : function()
28140     {
28141         this.previewEl.dom.innerHTML = '';
28142         
28143         var canvasEl = document.createElement("canvas");
28144         
28145         var contextEl = canvasEl.getContext("2d");
28146         
28147         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28148         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28149         var center = this.imageEl.OriginWidth / 2;
28150         
28151         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28152             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28153             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28154             center = this.imageEl.OriginHeight / 2;
28155         }
28156         
28157         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28158         
28159         contextEl.translate(center, center);
28160         contextEl.rotate(this.rotate * Math.PI / 180);
28161
28162         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28163         
28164         this.canvasEl = document.createElement("canvas");
28165         
28166         this.contextEl = this.canvasEl.getContext("2d");
28167         
28168         switch (this.rotate) {
28169             case 0 :
28170                 
28171                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28172                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28173                 
28174                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28175                 
28176                 break;
28177             case 90 : 
28178                 
28179                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28180                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28181                 
28182                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28183                     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);
28184                     break;
28185                 }
28186                 
28187                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28188                 
28189                 break;
28190             case 180 :
28191                 
28192                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28193                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28194                 
28195                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28196                     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);
28197                     break;
28198                 }
28199                 
28200                 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);
28201                 
28202                 break;
28203             case 270 :
28204                 
28205                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28206                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28207         
28208                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28209                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28210                     break;
28211                 }
28212                 
28213                 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);
28214                 
28215                 break;
28216             default : 
28217                 break;
28218         }
28219         
28220         this.previewEl.appendChild(this.canvasEl);
28221         
28222         this.setCanvasPosition();
28223     },
28224     
28225     crop : function()
28226     {
28227         if(!this.canvasLoaded){
28228             return;
28229         }
28230         
28231         var imageCanvas = document.createElement("canvas");
28232         
28233         var imageContext = imageCanvas.getContext("2d");
28234         
28235         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28236         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28237         
28238         var center = imageCanvas.width / 2;
28239         
28240         imageContext.translate(center, center);
28241         
28242         imageContext.rotate(this.rotate * Math.PI / 180);
28243         
28244         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28245         
28246         var canvas = document.createElement("canvas");
28247         
28248         var context = canvas.getContext("2d");
28249                 
28250         canvas.width = this.minWidth;
28251         canvas.height = this.minHeight;
28252
28253         switch (this.rotate) {
28254             case 0 :
28255                 
28256                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28257                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28258                 
28259                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28260                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28261                 
28262                 var targetWidth = this.minWidth - 2 * x;
28263                 var targetHeight = this.minHeight - 2 * y;
28264                 
28265                 var scale = 1;
28266                 
28267                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28268                     scale = targetWidth / width;
28269                 }
28270                 
28271                 if(x > 0 && y == 0){
28272                     scale = targetHeight / height;
28273                 }
28274                 
28275                 if(x > 0 && y > 0){
28276                     scale = targetWidth / width;
28277                     
28278                     if(width < height){
28279                         scale = targetHeight / height;
28280                     }
28281                 }
28282                 
28283                 context.scale(scale, scale);
28284                 
28285                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28286                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28287
28288                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28289                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28290
28291                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28292                 
28293                 break;
28294             case 90 : 
28295                 
28296                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28297                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28298                 
28299                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28300                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28301                 
28302                 var targetWidth = this.minWidth - 2 * x;
28303                 var targetHeight = this.minHeight - 2 * y;
28304                 
28305                 var scale = 1;
28306                 
28307                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28308                     scale = targetWidth / width;
28309                 }
28310                 
28311                 if(x > 0 && y == 0){
28312                     scale = targetHeight / height;
28313                 }
28314                 
28315                 if(x > 0 && y > 0){
28316                     scale = targetWidth / width;
28317                     
28318                     if(width < height){
28319                         scale = targetHeight / height;
28320                     }
28321                 }
28322                 
28323                 context.scale(scale, scale);
28324                 
28325                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28326                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28327
28328                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28329                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28330                 
28331                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28332                 
28333                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28334                 
28335                 break;
28336             case 180 :
28337                 
28338                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28339                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28340                 
28341                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28342                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28343                 
28344                 var targetWidth = this.minWidth - 2 * x;
28345                 var targetHeight = this.minHeight - 2 * y;
28346                 
28347                 var scale = 1;
28348                 
28349                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28350                     scale = targetWidth / width;
28351                 }
28352                 
28353                 if(x > 0 && y == 0){
28354                     scale = targetHeight / height;
28355                 }
28356                 
28357                 if(x > 0 && y > 0){
28358                     scale = targetWidth / width;
28359                     
28360                     if(width < height){
28361                         scale = targetHeight / height;
28362                     }
28363                 }
28364                 
28365                 context.scale(scale, scale);
28366                 
28367                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28368                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28369
28370                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28371                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28372
28373                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28374                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28375                 
28376                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28377                 
28378                 break;
28379             case 270 :
28380                 
28381                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28382                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28383                 
28384                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28385                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28386                 
28387                 var targetWidth = this.minWidth - 2 * x;
28388                 var targetHeight = this.minHeight - 2 * y;
28389                 
28390                 var scale = 1;
28391                 
28392                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28393                     scale = targetWidth / width;
28394                 }
28395                 
28396                 if(x > 0 && y == 0){
28397                     scale = targetHeight / height;
28398                 }
28399                 
28400                 if(x > 0 && y > 0){
28401                     scale = targetWidth / width;
28402                     
28403                     if(width < height){
28404                         scale = targetHeight / height;
28405                     }
28406                 }
28407                 
28408                 context.scale(scale, scale);
28409                 
28410                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28411                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28412
28413                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28414                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28415                 
28416                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28417                 
28418                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28419                 
28420                 break;
28421             default : 
28422                 break;
28423         }
28424         
28425         this.cropData = canvas.toDataURL(this.cropType);
28426         
28427         if(this.fireEvent('crop', this, this.cropData) !== false){
28428             this.process(this.file, this.cropData);
28429         }
28430         
28431         return;
28432         
28433     },
28434     
28435     setThumbBoxSize : function()
28436     {
28437         var width, height;
28438         
28439         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28440             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28441             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28442             
28443             this.minWidth = width;
28444             this.minHeight = height;
28445             
28446             if(this.rotate == 90 || this.rotate == 270){
28447                 this.minWidth = height;
28448                 this.minHeight = width;
28449             }
28450         }
28451         
28452         height = 300;
28453         width = Math.ceil(this.minWidth * height / this.minHeight);
28454         
28455         if(this.minWidth > this.minHeight){
28456             width = 300;
28457             height = Math.ceil(this.minHeight * width / this.minWidth);
28458         }
28459         
28460         this.thumbEl.setStyle({
28461             width : width + 'px',
28462             height : height + 'px'
28463         });
28464
28465         return;
28466             
28467     },
28468     
28469     setThumbBoxPosition : function()
28470     {
28471         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28472         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28473         
28474         this.thumbEl.setLeft(x);
28475         this.thumbEl.setTop(y);
28476         
28477     },
28478     
28479     baseRotateLevel : function()
28480     {
28481         this.baseRotate = 1;
28482         
28483         if(
28484                 typeof(this.exif) != 'undefined' &&
28485                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28486                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28487         ){
28488             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28489         }
28490         
28491         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28492         
28493     },
28494     
28495     baseScaleLevel : function()
28496     {
28497         var width, height;
28498         
28499         if(this.isDocument){
28500             
28501             if(this.baseRotate == 6 || this.baseRotate == 8){
28502             
28503                 height = this.thumbEl.getHeight();
28504                 this.baseScale = height / this.imageEl.OriginWidth;
28505
28506                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28507                     width = this.thumbEl.getWidth();
28508                     this.baseScale = width / this.imageEl.OriginHeight;
28509                 }
28510
28511                 return;
28512             }
28513
28514             height = this.thumbEl.getHeight();
28515             this.baseScale = height / this.imageEl.OriginHeight;
28516
28517             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28518                 width = this.thumbEl.getWidth();
28519                 this.baseScale = width / this.imageEl.OriginWidth;
28520             }
28521
28522             return;
28523         }
28524         
28525         if(this.baseRotate == 6 || this.baseRotate == 8){
28526             
28527             width = this.thumbEl.getHeight();
28528             this.baseScale = width / this.imageEl.OriginHeight;
28529             
28530             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28531                 height = this.thumbEl.getWidth();
28532                 this.baseScale = height / this.imageEl.OriginHeight;
28533             }
28534             
28535             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28536                 height = this.thumbEl.getWidth();
28537                 this.baseScale = height / this.imageEl.OriginHeight;
28538                 
28539                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28540                     width = this.thumbEl.getHeight();
28541                     this.baseScale = width / this.imageEl.OriginWidth;
28542                 }
28543             }
28544             
28545             return;
28546         }
28547         
28548         width = this.thumbEl.getWidth();
28549         this.baseScale = width / this.imageEl.OriginWidth;
28550         
28551         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28552             height = this.thumbEl.getHeight();
28553             this.baseScale = height / this.imageEl.OriginHeight;
28554         }
28555         
28556         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28557             
28558             height = this.thumbEl.getHeight();
28559             this.baseScale = height / this.imageEl.OriginHeight;
28560             
28561             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28562                 width = this.thumbEl.getWidth();
28563                 this.baseScale = width / this.imageEl.OriginWidth;
28564             }
28565             
28566         }
28567         
28568         return;
28569     },
28570     
28571     getScaleLevel : function()
28572     {
28573         return this.baseScale * Math.pow(1.1, this.scale);
28574     },
28575     
28576     onTouchStart : function(e)
28577     {
28578         if(!this.canvasLoaded){
28579             this.beforeSelectFile(e);
28580             return;
28581         }
28582         
28583         var touches = e.browserEvent.touches;
28584         
28585         if(!touches){
28586             return;
28587         }
28588         
28589         if(touches.length == 1){
28590             this.onMouseDown(e);
28591             return;
28592         }
28593         
28594         if(touches.length != 2){
28595             return;
28596         }
28597         
28598         var coords = [];
28599         
28600         for(var i = 0, finger; finger = touches[i]; i++){
28601             coords.push(finger.pageX, finger.pageY);
28602         }
28603         
28604         var x = Math.pow(coords[0] - coords[2], 2);
28605         var y = Math.pow(coords[1] - coords[3], 2);
28606         
28607         this.startDistance = Math.sqrt(x + y);
28608         
28609         this.startScale = this.scale;
28610         
28611         this.pinching = true;
28612         this.dragable = false;
28613         
28614     },
28615     
28616     onTouchMove : function(e)
28617     {
28618         if(!this.pinching && !this.dragable){
28619             return;
28620         }
28621         
28622         var touches = e.browserEvent.touches;
28623         
28624         if(!touches){
28625             return;
28626         }
28627         
28628         if(this.dragable){
28629             this.onMouseMove(e);
28630             return;
28631         }
28632         
28633         var coords = [];
28634         
28635         for(var i = 0, finger; finger = touches[i]; i++){
28636             coords.push(finger.pageX, finger.pageY);
28637         }
28638         
28639         var x = Math.pow(coords[0] - coords[2], 2);
28640         var y = Math.pow(coords[1] - coords[3], 2);
28641         
28642         this.endDistance = Math.sqrt(x + y);
28643         
28644         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28645         
28646         if(!this.zoomable()){
28647             this.scale = this.startScale;
28648             return;
28649         }
28650         
28651         this.draw();
28652         
28653     },
28654     
28655     onTouchEnd : function(e)
28656     {
28657         this.pinching = false;
28658         this.dragable = false;
28659         
28660     },
28661     
28662     process : function(file, crop)
28663     {
28664         if(this.loadMask){
28665             this.maskEl.mask(this.loadingText);
28666         }
28667         
28668         this.xhr = new XMLHttpRequest();
28669         
28670         file.xhr = this.xhr;
28671
28672         this.xhr.open(this.method, this.url, true);
28673         
28674         var headers = {
28675             "Accept": "application/json",
28676             "Cache-Control": "no-cache",
28677             "X-Requested-With": "XMLHttpRequest"
28678         };
28679         
28680         for (var headerName in headers) {
28681             var headerValue = headers[headerName];
28682             if (headerValue) {
28683                 this.xhr.setRequestHeader(headerName, headerValue);
28684             }
28685         }
28686         
28687         var _this = this;
28688         
28689         this.xhr.onload = function()
28690         {
28691             _this.xhrOnLoad(_this.xhr);
28692         }
28693         
28694         this.xhr.onerror = function()
28695         {
28696             _this.xhrOnError(_this.xhr);
28697         }
28698         
28699         var formData = new FormData();
28700
28701         formData.append('returnHTML', 'NO');
28702         
28703         if(crop){
28704             formData.append('crop', crop);
28705         }
28706         
28707         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28708             formData.append(this.paramName, file, file.name);
28709         }
28710         
28711         if(typeof(file.filename) != 'undefined'){
28712             formData.append('filename', file.filename);
28713         }
28714         
28715         if(typeof(file.mimetype) != 'undefined'){
28716             formData.append('mimetype', file.mimetype);
28717         }
28718         
28719         if(this.fireEvent('arrange', this, formData) != false){
28720             this.xhr.send(formData);
28721         };
28722     },
28723     
28724     xhrOnLoad : function(xhr)
28725     {
28726         if(this.loadMask){
28727             this.maskEl.unmask();
28728         }
28729         
28730         if (xhr.readyState !== 4) {
28731             this.fireEvent('exception', this, xhr);
28732             return;
28733         }
28734
28735         var response = Roo.decode(xhr.responseText);
28736         
28737         if(!response.success){
28738             this.fireEvent('exception', this, xhr);
28739             return;
28740         }
28741         
28742         var response = Roo.decode(xhr.responseText);
28743         
28744         this.fireEvent('upload', this, response);
28745         
28746     },
28747     
28748     xhrOnError : function()
28749     {
28750         if(this.loadMask){
28751             this.maskEl.unmask();
28752         }
28753         
28754         Roo.log('xhr on error');
28755         
28756         var response = Roo.decode(xhr.responseText);
28757           
28758         Roo.log(response);
28759         
28760     },
28761     
28762     prepare : function(file)
28763     {   
28764         if(this.loadMask){
28765             this.maskEl.mask(this.loadingText);
28766         }
28767         
28768         this.file = false;
28769         this.exif = {};
28770         
28771         if(typeof(file) === 'string'){
28772             this.loadCanvas(file);
28773             return;
28774         }
28775         
28776         if(!file || !this.urlAPI){
28777             return;
28778         }
28779         
28780         this.file = file;
28781         this.cropType = file.type;
28782         
28783         var _this = this;
28784         
28785         if(this.fireEvent('prepare', this, this.file) != false){
28786             
28787             var reader = new FileReader();
28788             
28789             reader.onload = function (e) {
28790                 if (e.target.error) {
28791                     Roo.log(e.target.error);
28792                     return;
28793                 }
28794                 
28795                 var buffer = e.target.result,
28796                     dataView = new DataView(buffer),
28797                     offset = 2,
28798                     maxOffset = dataView.byteLength - 4,
28799                     markerBytes,
28800                     markerLength;
28801                 
28802                 if (dataView.getUint16(0) === 0xffd8) {
28803                     while (offset < maxOffset) {
28804                         markerBytes = dataView.getUint16(offset);
28805                         
28806                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28807                             markerLength = dataView.getUint16(offset + 2) + 2;
28808                             if (offset + markerLength > dataView.byteLength) {
28809                                 Roo.log('Invalid meta data: Invalid segment size.');
28810                                 break;
28811                             }
28812                             
28813                             if(markerBytes == 0xffe1){
28814                                 _this.parseExifData(
28815                                     dataView,
28816                                     offset,
28817                                     markerLength
28818                                 );
28819                             }
28820                             
28821                             offset += markerLength;
28822                             
28823                             continue;
28824                         }
28825                         
28826                         break;
28827                     }
28828                     
28829                 }
28830                 
28831                 var url = _this.urlAPI.createObjectURL(_this.file);
28832                 
28833                 _this.loadCanvas(url);
28834                 
28835                 return;
28836             }
28837             
28838             reader.readAsArrayBuffer(this.file);
28839             
28840         }
28841         
28842     },
28843     
28844     parseExifData : function(dataView, offset, length)
28845     {
28846         var tiffOffset = offset + 10,
28847             littleEndian,
28848             dirOffset;
28849     
28850         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28851             // No Exif data, might be XMP data instead
28852             return;
28853         }
28854         
28855         // Check for the ASCII code for "Exif" (0x45786966):
28856         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28857             // No Exif data, might be XMP data instead
28858             return;
28859         }
28860         if (tiffOffset + 8 > dataView.byteLength) {
28861             Roo.log('Invalid Exif data: Invalid segment size.');
28862             return;
28863         }
28864         // Check for the two null bytes:
28865         if (dataView.getUint16(offset + 8) !== 0x0000) {
28866             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28867             return;
28868         }
28869         // Check the byte alignment:
28870         switch (dataView.getUint16(tiffOffset)) {
28871         case 0x4949:
28872             littleEndian = true;
28873             break;
28874         case 0x4D4D:
28875             littleEndian = false;
28876             break;
28877         default:
28878             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28879             return;
28880         }
28881         // Check for the TIFF tag marker (0x002A):
28882         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28883             Roo.log('Invalid Exif data: Missing TIFF marker.');
28884             return;
28885         }
28886         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28887         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28888         
28889         this.parseExifTags(
28890             dataView,
28891             tiffOffset,
28892             tiffOffset + dirOffset,
28893             littleEndian
28894         );
28895     },
28896     
28897     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28898     {
28899         var tagsNumber,
28900             dirEndOffset,
28901             i;
28902         if (dirOffset + 6 > dataView.byteLength) {
28903             Roo.log('Invalid Exif data: Invalid directory offset.');
28904             return;
28905         }
28906         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28907         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28908         if (dirEndOffset + 4 > dataView.byteLength) {
28909             Roo.log('Invalid Exif data: Invalid directory size.');
28910             return;
28911         }
28912         for (i = 0; i < tagsNumber; i += 1) {
28913             this.parseExifTag(
28914                 dataView,
28915                 tiffOffset,
28916                 dirOffset + 2 + 12 * i, // tag offset
28917                 littleEndian
28918             );
28919         }
28920         // Return the offset to the next directory:
28921         return dataView.getUint32(dirEndOffset, littleEndian);
28922     },
28923     
28924     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28925     {
28926         var tag = dataView.getUint16(offset, littleEndian);
28927         
28928         this.exif[tag] = this.getExifValue(
28929             dataView,
28930             tiffOffset,
28931             offset,
28932             dataView.getUint16(offset + 2, littleEndian), // tag type
28933             dataView.getUint32(offset + 4, littleEndian), // tag length
28934             littleEndian
28935         );
28936     },
28937     
28938     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28939     {
28940         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28941             tagSize,
28942             dataOffset,
28943             values,
28944             i,
28945             str,
28946             c;
28947     
28948         if (!tagType) {
28949             Roo.log('Invalid Exif data: Invalid tag type.');
28950             return;
28951         }
28952         
28953         tagSize = tagType.size * length;
28954         // Determine if the value is contained in the dataOffset bytes,
28955         // or if the value at the dataOffset is a pointer to the actual data:
28956         dataOffset = tagSize > 4 ?
28957                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28958         if (dataOffset + tagSize > dataView.byteLength) {
28959             Roo.log('Invalid Exif data: Invalid data offset.');
28960             return;
28961         }
28962         if (length === 1) {
28963             return tagType.getValue(dataView, dataOffset, littleEndian);
28964         }
28965         values = [];
28966         for (i = 0; i < length; i += 1) {
28967             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28968         }
28969         
28970         if (tagType.ascii) {
28971             str = '';
28972             // Concatenate the chars:
28973             for (i = 0; i < values.length; i += 1) {
28974                 c = values[i];
28975                 // Ignore the terminating NULL byte(s):
28976                 if (c === '\u0000') {
28977                     break;
28978                 }
28979                 str += c;
28980             }
28981             return str;
28982         }
28983         return values;
28984     }
28985     
28986 });
28987
28988 Roo.apply(Roo.bootstrap.UploadCropbox, {
28989     tags : {
28990         'Orientation': 0x0112
28991     },
28992     
28993     Orientation: {
28994             1: 0, //'top-left',
28995 //            2: 'top-right',
28996             3: 180, //'bottom-right',
28997 //            4: 'bottom-left',
28998 //            5: 'left-top',
28999             6: 90, //'right-top',
29000 //            7: 'right-bottom',
29001             8: 270 //'left-bottom'
29002     },
29003     
29004     exifTagTypes : {
29005         // byte, 8-bit unsigned int:
29006         1: {
29007             getValue: function (dataView, dataOffset) {
29008                 return dataView.getUint8(dataOffset);
29009             },
29010             size: 1
29011         },
29012         // ascii, 8-bit byte:
29013         2: {
29014             getValue: function (dataView, dataOffset) {
29015                 return String.fromCharCode(dataView.getUint8(dataOffset));
29016             },
29017             size: 1,
29018             ascii: true
29019         },
29020         // short, 16 bit int:
29021         3: {
29022             getValue: function (dataView, dataOffset, littleEndian) {
29023                 return dataView.getUint16(dataOffset, littleEndian);
29024             },
29025             size: 2
29026         },
29027         // long, 32 bit int:
29028         4: {
29029             getValue: function (dataView, dataOffset, littleEndian) {
29030                 return dataView.getUint32(dataOffset, littleEndian);
29031             },
29032             size: 4
29033         },
29034         // rational = two long values, first is numerator, second is denominator:
29035         5: {
29036             getValue: function (dataView, dataOffset, littleEndian) {
29037                 return dataView.getUint32(dataOffset, littleEndian) /
29038                     dataView.getUint32(dataOffset + 4, littleEndian);
29039             },
29040             size: 8
29041         },
29042         // slong, 32 bit signed int:
29043         9: {
29044             getValue: function (dataView, dataOffset, littleEndian) {
29045                 return dataView.getInt32(dataOffset, littleEndian);
29046             },
29047             size: 4
29048         },
29049         // srational, two slongs, first is numerator, second is denominator:
29050         10: {
29051             getValue: function (dataView, dataOffset, littleEndian) {
29052                 return dataView.getInt32(dataOffset, littleEndian) /
29053                     dataView.getInt32(dataOffset + 4, littleEndian);
29054             },
29055             size: 8
29056         }
29057     },
29058     
29059     footer : {
29060         STANDARD : [
29061             {
29062                 tag : 'div',
29063                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29064                 action : 'rotate-left',
29065                 cn : [
29066                     {
29067                         tag : 'button',
29068                         cls : 'btn btn-default',
29069                         html : '<i class="fa fa-undo"></i>'
29070                     }
29071                 ]
29072             },
29073             {
29074                 tag : 'div',
29075                 cls : 'btn-group roo-upload-cropbox-picture',
29076                 action : 'picture',
29077                 cn : [
29078                     {
29079                         tag : 'button',
29080                         cls : 'btn btn-default',
29081                         html : '<i class="fa fa-picture-o"></i>'
29082                     }
29083                 ]
29084             },
29085             {
29086                 tag : 'div',
29087                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29088                 action : 'rotate-right',
29089                 cn : [
29090                     {
29091                         tag : 'button',
29092                         cls : 'btn btn-default',
29093                         html : '<i class="fa fa-repeat"></i>'
29094                     }
29095                 ]
29096             }
29097         ],
29098         DOCUMENT : [
29099             {
29100                 tag : 'div',
29101                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29102                 action : 'rotate-left',
29103                 cn : [
29104                     {
29105                         tag : 'button',
29106                         cls : 'btn btn-default',
29107                         html : '<i class="fa fa-undo"></i>'
29108                     }
29109                 ]
29110             },
29111             {
29112                 tag : 'div',
29113                 cls : 'btn-group roo-upload-cropbox-download',
29114                 action : 'download',
29115                 cn : [
29116                     {
29117                         tag : 'button',
29118                         cls : 'btn btn-default',
29119                         html : '<i class="fa fa-download"></i>'
29120                     }
29121                 ]
29122             },
29123             {
29124                 tag : 'div',
29125                 cls : 'btn-group roo-upload-cropbox-crop',
29126                 action : 'crop',
29127                 cn : [
29128                     {
29129                         tag : 'button',
29130                         cls : 'btn btn-default',
29131                         html : '<i class="fa fa-crop"></i>'
29132                     }
29133                 ]
29134             },
29135             {
29136                 tag : 'div',
29137                 cls : 'btn-group roo-upload-cropbox-trash',
29138                 action : 'trash',
29139                 cn : [
29140                     {
29141                         tag : 'button',
29142                         cls : 'btn btn-default',
29143                         html : '<i class="fa fa-trash"></i>'
29144                     }
29145                 ]
29146             },
29147             {
29148                 tag : 'div',
29149                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29150                 action : 'rotate-right',
29151                 cn : [
29152                     {
29153                         tag : 'button',
29154                         cls : 'btn btn-default',
29155                         html : '<i class="fa fa-repeat"></i>'
29156                     }
29157                 ]
29158             }
29159         ],
29160         ROTATOR : [
29161             {
29162                 tag : 'div',
29163                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29164                 action : 'rotate-left',
29165                 cn : [
29166                     {
29167                         tag : 'button',
29168                         cls : 'btn btn-default',
29169                         html : '<i class="fa fa-undo"></i>'
29170                     }
29171                 ]
29172             },
29173             {
29174                 tag : 'div',
29175                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29176                 action : 'rotate-right',
29177                 cn : [
29178                     {
29179                         tag : 'button',
29180                         cls : 'btn btn-default',
29181                         html : '<i class="fa fa-repeat"></i>'
29182                     }
29183                 ]
29184             }
29185         ]
29186     }
29187 });
29188
29189 /*
29190 * Licence: LGPL
29191 */
29192
29193 /**
29194  * @class Roo.bootstrap.DocumentManager
29195  * @extends Roo.bootstrap.Component
29196  * Bootstrap DocumentManager class
29197  * @cfg {String} paramName default 'imageUpload'
29198  * @cfg {String} toolTipName default 'filename'
29199  * @cfg {String} method default POST
29200  * @cfg {String} url action url
29201  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29202  * @cfg {Boolean} multiple multiple upload default true
29203  * @cfg {Number} thumbSize default 300
29204  * @cfg {String} fieldLabel
29205  * @cfg {Number} labelWidth default 4
29206  * @cfg {String} labelAlign (left|top) default left
29207  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29208 * @cfg {Number} labellg set the width of label (1-12)
29209  * @cfg {Number} labelmd set the width of label (1-12)
29210  * @cfg {Number} labelsm set the width of label (1-12)
29211  * @cfg {Number} labelxs set the width of label (1-12)
29212  * 
29213  * @constructor
29214  * Create a new DocumentManager
29215  * @param {Object} config The config object
29216  */
29217
29218 Roo.bootstrap.DocumentManager = function(config){
29219     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29220     
29221     this.files = [];
29222     this.delegates = [];
29223     
29224     this.addEvents({
29225         /**
29226          * @event initial
29227          * Fire when initial the DocumentManager
29228          * @param {Roo.bootstrap.DocumentManager} this
29229          */
29230         "initial" : true,
29231         /**
29232          * @event inspect
29233          * inspect selected file
29234          * @param {Roo.bootstrap.DocumentManager} this
29235          * @param {File} file
29236          */
29237         "inspect" : true,
29238         /**
29239          * @event exception
29240          * Fire when xhr load exception
29241          * @param {Roo.bootstrap.DocumentManager} this
29242          * @param {XMLHttpRequest} xhr
29243          */
29244         "exception" : true,
29245         /**
29246          * @event afterupload
29247          * Fire when xhr load exception
29248          * @param {Roo.bootstrap.DocumentManager} this
29249          * @param {XMLHttpRequest} xhr
29250          */
29251         "afterupload" : true,
29252         /**
29253          * @event prepare
29254          * prepare the form data
29255          * @param {Roo.bootstrap.DocumentManager} this
29256          * @param {Object} formData
29257          */
29258         "prepare" : true,
29259         /**
29260          * @event remove
29261          * Fire when remove the file
29262          * @param {Roo.bootstrap.DocumentManager} this
29263          * @param {Object} file
29264          */
29265         "remove" : true,
29266         /**
29267          * @event refresh
29268          * Fire after refresh the file
29269          * @param {Roo.bootstrap.DocumentManager} this
29270          */
29271         "refresh" : true,
29272         /**
29273          * @event click
29274          * Fire after click the image
29275          * @param {Roo.bootstrap.DocumentManager} this
29276          * @param {Object} file
29277          */
29278         "click" : true,
29279         /**
29280          * @event edit
29281          * Fire when upload a image and editable set to true
29282          * @param {Roo.bootstrap.DocumentManager} this
29283          * @param {Object} file
29284          */
29285         "edit" : true,
29286         /**
29287          * @event beforeselectfile
29288          * Fire before select file
29289          * @param {Roo.bootstrap.DocumentManager} this
29290          */
29291         "beforeselectfile" : true,
29292         /**
29293          * @event process
29294          * Fire before process file
29295          * @param {Roo.bootstrap.DocumentManager} this
29296          * @param {Object} file
29297          */
29298         "process" : true,
29299         /**
29300          * @event previewrendered
29301          * Fire when preview rendered
29302          * @param {Roo.bootstrap.DocumentManager} this
29303          * @param {Object} file
29304          */
29305         "previewrendered" : true,
29306         /**
29307          */
29308         "previewResize" : true
29309         
29310     });
29311 };
29312
29313 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29314     
29315     boxes : 0,
29316     inputName : '',
29317     thumbSize : 300,
29318     multiple : true,
29319     files : false,
29320     method : 'POST',
29321     url : '',
29322     paramName : 'imageUpload',
29323     toolTipName : 'filename',
29324     fieldLabel : '',
29325     labelWidth : 4,
29326     labelAlign : 'left',
29327     editable : true,
29328     delegates : false,
29329     xhr : false, 
29330     
29331     labellg : 0,
29332     labelmd : 0,
29333     labelsm : 0,
29334     labelxs : 0,
29335     
29336     getAutoCreate : function()
29337     {   
29338         var managerWidget = {
29339             tag : 'div',
29340             cls : 'roo-document-manager',
29341             cn : [
29342                 {
29343                     tag : 'input',
29344                     cls : 'roo-document-manager-selector',
29345                     type : 'file'
29346                 },
29347                 {
29348                     tag : 'div',
29349                     cls : 'roo-document-manager-uploader',
29350                     cn : [
29351                         {
29352                             tag : 'div',
29353                             cls : 'roo-document-manager-upload-btn',
29354                             html : '<i class="fa fa-plus"></i>'
29355                         }
29356                     ]
29357                     
29358                 }
29359             ]
29360         };
29361         
29362         var content = [
29363             {
29364                 tag : 'div',
29365                 cls : 'column col-md-12',
29366                 cn : managerWidget
29367             }
29368         ];
29369         
29370         if(this.fieldLabel.length){
29371             
29372             content = [
29373                 {
29374                     tag : 'div',
29375                     cls : 'column col-md-12',
29376                     html : this.fieldLabel
29377                 },
29378                 {
29379                     tag : 'div',
29380                     cls : 'column col-md-12',
29381                     cn : managerWidget
29382                 }
29383             ];
29384
29385             if(this.labelAlign == 'left'){
29386                 content = [
29387                     {
29388                         tag : 'div',
29389                         cls : 'column',
29390                         html : this.fieldLabel
29391                     },
29392                     {
29393                         tag : 'div',
29394                         cls : 'column',
29395                         cn : managerWidget
29396                     }
29397                 ];
29398                 
29399                 if(this.labelWidth > 12){
29400                     content[0].style = "width: " + this.labelWidth + 'px';
29401                 }
29402
29403                 if(this.labelWidth < 13 && this.labelmd == 0){
29404                     this.labelmd = this.labelWidth;
29405                 }
29406
29407                 if(this.labellg > 0){
29408                     content[0].cls += ' col-lg-' + this.labellg;
29409                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29410                 }
29411
29412                 if(this.labelmd > 0){
29413                     content[0].cls += ' col-md-' + this.labelmd;
29414                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29415                 }
29416
29417                 if(this.labelsm > 0){
29418                     content[0].cls += ' col-sm-' + this.labelsm;
29419                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29420                 }
29421
29422                 if(this.labelxs > 0){
29423                     content[0].cls += ' col-xs-' + this.labelxs;
29424                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29425                 }
29426                 
29427             }
29428         }
29429         
29430         var cfg = {
29431             tag : 'div',
29432             cls : 'row clearfix',
29433             cn : content
29434         };
29435         
29436         return cfg;
29437         
29438     },
29439     
29440     initEvents : function()
29441     {
29442         this.managerEl = this.el.select('.roo-document-manager', true).first();
29443         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29444         
29445         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29446         this.selectorEl.hide();
29447         
29448         if(this.multiple){
29449             this.selectorEl.attr('multiple', 'multiple');
29450         }
29451         
29452         this.selectorEl.on('change', this.onFileSelected, this);
29453         
29454         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29455         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29456         
29457         this.uploader.on('click', this.onUploaderClick, this);
29458         
29459         this.renderProgressDialog();
29460         
29461         var _this = this;
29462         
29463         window.addEventListener("resize", function() { _this.refresh(); } );
29464         
29465         this.fireEvent('initial', this);
29466     },
29467     
29468     renderProgressDialog : function()
29469     {
29470         var _this = this;
29471         
29472         this.progressDialog = new Roo.bootstrap.Modal({
29473             cls : 'roo-document-manager-progress-dialog',
29474             allow_close : false,
29475             animate : false,
29476             title : '',
29477             buttons : [
29478                 {
29479                     name  :'cancel',
29480                     weight : 'danger',
29481                     html : 'Cancel'
29482                 }
29483             ], 
29484             listeners : { 
29485                 btnclick : function() {
29486                     _this.uploadCancel();
29487                     this.hide();
29488                 }
29489             }
29490         });
29491          
29492         this.progressDialog.render(Roo.get(document.body));
29493          
29494         this.progress = new Roo.bootstrap.Progress({
29495             cls : 'roo-document-manager-progress',
29496             active : true,
29497             striped : true
29498         });
29499         
29500         this.progress.render(this.progressDialog.getChildContainer());
29501         
29502         this.progressBar = new Roo.bootstrap.ProgressBar({
29503             cls : 'roo-document-manager-progress-bar',
29504             aria_valuenow : 0,
29505             aria_valuemin : 0,
29506             aria_valuemax : 12,
29507             panel : 'success'
29508         });
29509         
29510         this.progressBar.render(this.progress.getChildContainer());
29511     },
29512     
29513     onUploaderClick : function(e)
29514     {
29515         e.preventDefault();
29516      
29517         if(this.fireEvent('beforeselectfile', this) != false){
29518             this.selectorEl.dom.click();
29519         }
29520         
29521     },
29522     
29523     onFileSelected : function(e)
29524     {
29525         e.preventDefault();
29526         
29527         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29528             return;
29529         }
29530         
29531         Roo.each(this.selectorEl.dom.files, function(file){
29532             if(this.fireEvent('inspect', this, file) != false){
29533                 this.files.push(file);
29534             }
29535         }, this);
29536         
29537         this.queue();
29538         
29539     },
29540     
29541     queue : function()
29542     {
29543         this.selectorEl.dom.value = '';
29544         
29545         if(!this.files || !this.files.length){
29546             return;
29547         }
29548         
29549         if(this.boxes > 0 && this.files.length > this.boxes){
29550             this.files = this.files.slice(0, this.boxes);
29551         }
29552         
29553         this.uploader.show();
29554         
29555         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29556             this.uploader.hide();
29557         }
29558         
29559         var _this = this;
29560         
29561         var files = [];
29562         
29563         var docs = [];
29564         
29565         Roo.each(this.files, function(file){
29566             
29567             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29568                 var f = this.renderPreview(file);
29569                 files.push(f);
29570                 return;
29571             }
29572             
29573             if(file.type.indexOf('image') != -1){
29574                 this.delegates.push(
29575                     (function(){
29576                         _this.process(file);
29577                     }).createDelegate(this)
29578                 );
29579         
29580                 return;
29581             }
29582             
29583             docs.push(
29584                 (function(){
29585                     _this.process(file);
29586                 }).createDelegate(this)
29587             );
29588             
29589         }, this);
29590         
29591         this.files = files;
29592         
29593         this.delegates = this.delegates.concat(docs);
29594         
29595         if(!this.delegates.length){
29596             this.refresh();
29597             return;
29598         }
29599         
29600         this.progressBar.aria_valuemax = this.delegates.length;
29601         
29602         this.arrange();
29603         
29604         return;
29605     },
29606     
29607     arrange : function()
29608     {
29609         if(!this.delegates.length){
29610             this.progressDialog.hide();
29611             this.refresh();
29612             return;
29613         }
29614         
29615         var delegate = this.delegates.shift();
29616         
29617         this.progressDialog.show();
29618         
29619         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29620         
29621         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29622         
29623         delegate();
29624     },
29625     
29626     refresh : function()
29627     {
29628         this.uploader.show();
29629         
29630         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29631             this.uploader.hide();
29632         }
29633         
29634         Roo.isTouch ? this.closable(false) : this.closable(true);
29635         
29636         this.fireEvent('refresh', this);
29637     },
29638     
29639     onRemove : function(e, el, o)
29640     {
29641         e.preventDefault();
29642         
29643         this.fireEvent('remove', this, o);
29644         
29645     },
29646     
29647     remove : function(o)
29648     {
29649         var files = [];
29650         
29651         Roo.each(this.files, function(file){
29652             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29653                 files.push(file);
29654                 return;
29655             }
29656
29657             o.target.remove();
29658
29659         }, this);
29660         
29661         this.files = files;
29662         
29663         this.refresh();
29664     },
29665     
29666     clear : function()
29667     {
29668         Roo.each(this.files, function(file){
29669             if(!file.target){
29670                 return;
29671             }
29672             
29673             file.target.remove();
29674
29675         }, this);
29676         
29677         this.files = [];
29678         
29679         this.refresh();
29680     },
29681     
29682     onClick : function(e, el, o)
29683     {
29684         e.preventDefault();
29685         
29686         this.fireEvent('click', this, o);
29687         
29688     },
29689     
29690     closable : function(closable)
29691     {
29692         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29693             
29694             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29695             
29696             if(closable){
29697                 el.show();
29698                 return;
29699             }
29700             
29701             el.hide();
29702             
29703         }, this);
29704     },
29705     
29706     xhrOnLoad : function(xhr)
29707     {
29708         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29709             el.remove();
29710         }, this);
29711         
29712         if (xhr.readyState !== 4) {
29713             this.arrange();
29714             this.fireEvent('exception', this, xhr);
29715             return;
29716         }
29717
29718         var response = Roo.decode(xhr.responseText);
29719         
29720         if(!response.success){
29721             this.arrange();
29722             this.fireEvent('exception', this, xhr);
29723             return;
29724         }
29725         
29726         var file = this.renderPreview(response.data);
29727         
29728         this.files.push(file);
29729         
29730         this.arrange();
29731         
29732         this.fireEvent('afterupload', this, xhr);
29733         
29734     },
29735     
29736     xhrOnError : function(xhr)
29737     {
29738         Roo.log('xhr on error');
29739         
29740         var response = Roo.decode(xhr.responseText);
29741           
29742         Roo.log(response);
29743         
29744         this.arrange();
29745     },
29746     
29747     process : function(file)
29748     {
29749         if(this.fireEvent('process', this, file) !== false){
29750             if(this.editable && file.type.indexOf('image') != -1){
29751                 this.fireEvent('edit', this, file);
29752                 return;
29753             }
29754
29755             this.uploadStart(file, false);
29756
29757             return;
29758         }
29759         
29760     },
29761     
29762     uploadStart : function(file, crop)
29763     {
29764         this.xhr = new XMLHttpRequest();
29765         
29766         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29767             this.arrange();
29768             return;
29769         }
29770         
29771         file.xhr = this.xhr;
29772             
29773         this.managerEl.createChild({
29774             tag : 'div',
29775             cls : 'roo-document-manager-loading',
29776             cn : [
29777                 {
29778                     tag : 'div',
29779                     tooltip : file.name,
29780                     cls : 'roo-document-manager-thumb',
29781                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29782                 }
29783             ]
29784
29785         });
29786
29787         this.xhr.open(this.method, this.url, true);
29788         
29789         var headers = {
29790             "Accept": "application/json",
29791             "Cache-Control": "no-cache",
29792             "X-Requested-With": "XMLHttpRequest"
29793         };
29794         
29795         for (var headerName in headers) {
29796             var headerValue = headers[headerName];
29797             if (headerValue) {
29798                 this.xhr.setRequestHeader(headerName, headerValue);
29799             }
29800         }
29801         
29802         var _this = this;
29803         
29804         this.xhr.onload = function()
29805         {
29806             _this.xhrOnLoad(_this.xhr);
29807         }
29808         
29809         this.xhr.onerror = function()
29810         {
29811             _this.xhrOnError(_this.xhr);
29812         }
29813         
29814         var formData = new FormData();
29815
29816         formData.append('returnHTML', 'NO');
29817         
29818         if(crop){
29819             formData.append('crop', crop);
29820         }
29821         
29822         formData.append(this.paramName, file, file.name);
29823         
29824         var options = {
29825             file : file, 
29826             manually : false
29827         };
29828         
29829         if(this.fireEvent('prepare', this, formData, options) != false){
29830             
29831             if(options.manually){
29832                 return;
29833             }
29834             
29835             this.xhr.send(formData);
29836             return;
29837         };
29838         
29839         this.uploadCancel();
29840     },
29841     
29842     uploadCancel : function()
29843     {
29844         if (this.xhr) {
29845             this.xhr.abort();
29846         }
29847         
29848         this.delegates = [];
29849         
29850         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29851             el.remove();
29852         }, this);
29853         
29854         this.arrange();
29855     },
29856     
29857     renderPreview : function(file)
29858     {
29859         if(typeof(file.target) != 'undefined' && file.target){
29860             return file;
29861         }
29862         
29863         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29864         
29865         var previewEl = this.managerEl.createChild({
29866             tag : 'div',
29867             cls : 'roo-document-manager-preview',
29868             cn : [
29869                 {
29870                     tag : 'div',
29871                     tooltip : file[this.toolTipName],
29872                     cls : 'roo-document-manager-thumb',
29873                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29874                 },
29875                 {
29876                     tag : 'button',
29877                     cls : 'close',
29878                     html : '<i class="fa fa-times-circle"></i>'
29879                 }
29880             ]
29881         });
29882
29883         var close = previewEl.select('button.close', true).first();
29884
29885         close.on('click', this.onRemove, this, file);
29886
29887         file.target = previewEl;
29888
29889         var image = previewEl.select('img', true).first();
29890         
29891         var _this = this;
29892         
29893         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29894         
29895         image.on('click', this.onClick, this, file);
29896         
29897         this.fireEvent('previewrendered', this, file);
29898         
29899         return file;
29900         
29901     },
29902     
29903     onPreviewLoad : function(file, image)
29904     {
29905         if(typeof(file.target) == 'undefined' || !file.target){
29906             return;
29907         }
29908         
29909         var width = image.dom.naturalWidth || image.dom.width;
29910         var height = image.dom.naturalHeight || image.dom.height;
29911         
29912         if(!this.previewResize) {
29913             return;
29914         }
29915         
29916         if(width > height){
29917             file.target.addClass('wide');
29918             return;
29919         }
29920         
29921         file.target.addClass('tall');
29922         return;
29923         
29924     },
29925     
29926     uploadFromSource : function(file, crop)
29927     {
29928         this.xhr = new XMLHttpRequest();
29929         
29930         this.managerEl.createChild({
29931             tag : 'div',
29932             cls : 'roo-document-manager-loading',
29933             cn : [
29934                 {
29935                     tag : 'div',
29936                     tooltip : file.name,
29937                     cls : 'roo-document-manager-thumb',
29938                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29939                 }
29940             ]
29941
29942         });
29943
29944         this.xhr.open(this.method, this.url, true);
29945         
29946         var headers = {
29947             "Accept": "application/json",
29948             "Cache-Control": "no-cache",
29949             "X-Requested-With": "XMLHttpRequest"
29950         };
29951         
29952         for (var headerName in headers) {
29953             var headerValue = headers[headerName];
29954             if (headerValue) {
29955                 this.xhr.setRequestHeader(headerName, headerValue);
29956             }
29957         }
29958         
29959         var _this = this;
29960         
29961         this.xhr.onload = function()
29962         {
29963             _this.xhrOnLoad(_this.xhr);
29964         }
29965         
29966         this.xhr.onerror = function()
29967         {
29968             _this.xhrOnError(_this.xhr);
29969         }
29970         
29971         var formData = new FormData();
29972
29973         formData.append('returnHTML', 'NO');
29974         
29975         formData.append('crop', crop);
29976         
29977         if(typeof(file.filename) != 'undefined'){
29978             formData.append('filename', file.filename);
29979         }
29980         
29981         if(typeof(file.mimetype) != 'undefined'){
29982             formData.append('mimetype', file.mimetype);
29983         }
29984         
29985         Roo.log(formData);
29986         
29987         if(this.fireEvent('prepare', this, formData) != false){
29988             this.xhr.send(formData);
29989         };
29990     }
29991 });
29992
29993 /*
29994 * Licence: LGPL
29995 */
29996
29997 /**
29998  * @class Roo.bootstrap.DocumentViewer
29999  * @extends Roo.bootstrap.Component
30000  * Bootstrap DocumentViewer class
30001  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30002  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30003  * 
30004  * @constructor
30005  * Create a new DocumentViewer
30006  * @param {Object} config The config object
30007  */
30008
30009 Roo.bootstrap.DocumentViewer = function(config){
30010     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30011     
30012     this.addEvents({
30013         /**
30014          * @event initial
30015          * Fire after initEvent
30016          * @param {Roo.bootstrap.DocumentViewer} this
30017          */
30018         "initial" : true,
30019         /**
30020          * @event click
30021          * Fire after click
30022          * @param {Roo.bootstrap.DocumentViewer} this
30023          */
30024         "click" : true,
30025         /**
30026          * @event download
30027          * Fire after download button
30028          * @param {Roo.bootstrap.DocumentViewer} this
30029          */
30030         "download" : true,
30031         /**
30032          * @event trash
30033          * Fire after trash button
30034          * @param {Roo.bootstrap.DocumentViewer} this
30035          */
30036         "trash" : true
30037         
30038     });
30039 };
30040
30041 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30042     
30043     showDownload : true,
30044     
30045     showTrash : true,
30046     
30047     getAutoCreate : function()
30048     {
30049         var cfg = {
30050             tag : 'div',
30051             cls : 'roo-document-viewer',
30052             cn : [
30053                 {
30054                     tag : 'div',
30055                     cls : 'roo-document-viewer-body',
30056                     cn : [
30057                         {
30058                             tag : 'div',
30059                             cls : 'roo-document-viewer-thumb',
30060                             cn : [
30061                                 {
30062                                     tag : 'img',
30063                                     cls : 'roo-document-viewer-image'
30064                                 }
30065                             ]
30066                         }
30067                     ]
30068                 },
30069                 {
30070                     tag : 'div',
30071                     cls : 'roo-document-viewer-footer',
30072                     cn : {
30073                         tag : 'div',
30074                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30075                         cn : [
30076                             {
30077                                 tag : 'div',
30078                                 cls : 'btn-group roo-document-viewer-download',
30079                                 cn : [
30080                                     {
30081                                         tag : 'button',
30082                                         cls : 'btn btn-default',
30083                                         html : '<i class="fa fa-download"></i>'
30084                                     }
30085                                 ]
30086                             },
30087                             {
30088                                 tag : 'div',
30089                                 cls : 'btn-group roo-document-viewer-trash',
30090                                 cn : [
30091                                     {
30092                                         tag : 'button',
30093                                         cls : 'btn btn-default',
30094                                         html : '<i class="fa fa-trash"></i>'
30095                                     }
30096                                 ]
30097                             }
30098                         ]
30099                     }
30100                 }
30101             ]
30102         };
30103         
30104         return cfg;
30105     },
30106     
30107     initEvents : function()
30108     {
30109         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30110         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30111         
30112         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30113         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30114         
30115         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30116         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30117         
30118         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30119         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30120         
30121         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30122         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30123         
30124         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30125         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30126         
30127         this.bodyEl.on('click', this.onClick, this);
30128         this.downloadBtn.on('click', this.onDownload, this);
30129         this.trashBtn.on('click', this.onTrash, this);
30130         
30131         this.downloadBtn.hide();
30132         this.trashBtn.hide();
30133         
30134         if(this.showDownload){
30135             this.downloadBtn.show();
30136         }
30137         
30138         if(this.showTrash){
30139             this.trashBtn.show();
30140         }
30141         
30142         if(!this.showDownload && !this.showTrash) {
30143             this.footerEl.hide();
30144         }
30145         
30146     },
30147     
30148     initial : function()
30149     {
30150         this.fireEvent('initial', this);
30151         
30152     },
30153     
30154     onClick : function(e)
30155     {
30156         e.preventDefault();
30157         
30158         this.fireEvent('click', this);
30159     },
30160     
30161     onDownload : function(e)
30162     {
30163         e.preventDefault();
30164         
30165         this.fireEvent('download', this);
30166     },
30167     
30168     onTrash : function(e)
30169     {
30170         e.preventDefault();
30171         
30172         this.fireEvent('trash', this);
30173     }
30174     
30175 });
30176 /*
30177  * - LGPL
30178  *
30179  * nav progress bar
30180  * 
30181  */
30182
30183 /**
30184  * @class Roo.bootstrap.NavProgressBar
30185  * @extends Roo.bootstrap.Component
30186  * Bootstrap NavProgressBar class
30187  * 
30188  * @constructor
30189  * Create a new nav progress bar
30190  * @param {Object} config The config object
30191  */
30192
30193 Roo.bootstrap.NavProgressBar = function(config){
30194     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30195
30196     this.bullets = this.bullets || [];
30197    
30198 //    Roo.bootstrap.NavProgressBar.register(this);
30199      this.addEvents({
30200         /**
30201              * @event changed
30202              * Fires when the active item changes
30203              * @param {Roo.bootstrap.NavProgressBar} this
30204              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30205              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30206          */
30207         'changed': true
30208      });
30209     
30210 };
30211
30212 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30213     
30214     bullets : [],
30215     barItems : [],
30216     
30217     getAutoCreate : function()
30218     {
30219         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30220         
30221         cfg = {
30222             tag : 'div',
30223             cls : 'roo-navigation-bar-group',
30224             cn : [
30225                 {
30226                     tag : 'div',
30227                     cls : 'roo-navigation-top-bar'
30228                 },
30229                 {
30230                     tag : 'div',
30231                     cls : 'roo-navigation-bullets-bar',
30232                     cn : [
30233                         {
30234                             tag : 'ul',
30235                             cls : 'roo-navigation-bar'
30236                         }
30237                     ]
30238                 },
30239                 
30240                 {
30241                     tag : 'div',
30242                     cls : 'roo-navigation-bottom-bar'
30243                 }
30244             ]
30245             
30246         };
30247         
30248         return cfg;
30249         
30250     },
30251     
30252     initEvents: function() 
30253     {
30254         
30255     },
30256     
30257     onRender : function(ct, position) 
30258     {
30259         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30260         
30261         if(this.bullets.length){
30262             Roo.each(this.bullets, function(b){
30263                this.addItem(b);
30264             }, this);
30265         }
30266         
30267         this.format();
30268         
30269     },
30270     
30271     addItem : function(cfg)
30272     {
30273         var item = new Roo.bootstrap.NavProgressItem(cfg);
30274         
30275         item.parentId = this.id;
30276         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30277         
30278         if(cfg.html){
30279             var top = new Roo.bootstrap.Element({
30280                 tag : 'div',
30281                 cls : 'roo-navigation-bar-text'
30282             });
30283             
30284             var bottom = new Roo.bootstrap.Element({
30285                 tag : 'div',
30286                 cls : 'roo-navigation-bar-text'
30287             });
30288             
30289             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30290             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30291             
30292             var topText = new Roo.bootstrap.Element({
30293                 tag : 'span',
30294                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30295             });
30296             
30297             var bottomText = new Roo.bootstrap.Element({
30298                 tag : 'span',
30299                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30300             });
30301             
30302             topText.onRender(top.el, null);
30303             bottomText.onRender(bottom.el, null);
30304             
30305             item.topEl = top;
30306             item.bottomEl = bottom;
30307         }
30308         
30309         this.barItems.push(item);
30310         
30311         return item;
30312     },
30313     
30314     getActive : function()
30315     {
30316         var active = false;
30317         
30318         Roo.each(this.barItems, function(v){
30319             
30320             if (!v.isActive()) {
30321                 return;
30322             }
30323             
30324             active = v;
30325             return false;
30326             
30327         });
30328         
30329         return active;
30330     },
30331     
30332     setActiveItem : function(item)
30333     {
30334         var prev = false;
30335         
30336         Roo.each(this.barItems, function(v){
30337             if (v.rid == item.rid) {
30338                 return ;
30339             }
30340             
30341             if (v.isActive()) {
30342                 v.setActive(false);
30343                 prev = v;
30344             }
30345         });
30346
30347         item.setActive(true);
30348         
30349         this.fireEvent('changed', this, item, prev);
30350     },
30351     
30352     getBarItem: function(rid)
30353     {
30354         var ret = false;
30355         
30356         Roo.each(this.barItems, function(e) {
30357             if (e.rid != rid) {
30358                 return;
30359             }
30360             
30361             ret =  e;
30362             return false;
30363         });
30364         
30365         return ret;
30366     },
30367     
30368     indexOfItem : function(item)
30369     {
30370         var index = false;
30371         
30372         Roo.each(this.barItems, function(v, i){
30373             
30374             if (v.rid != item.rid) {
30375                 return;
30376             }
30377             
30378             index = i;
30379             return false
30380         });
30381         
30382         return index;
30383     },
30384     
30385     setActiveNext : function()
30386     {
30387         var i = this.indexOfItem(this.getActive());
30388         
30389         if (i > this.barItems.length) {
30390             return;
30391         }
30392         
30393         this.setActiveItem(this.barItems[i+1]);
30394     },
30395     
30396     setActivePrev : function()
30397     {
30398         var i = this.indexOfItem(this.getActive());
30399         
30400         if (i  < 1) {
30401             return;
30402         }
30403         
30404         this.setActiveItem(this.barItems[i-1]);
30405     },
30406     
30407     format : function()
30408     {
30409         if(!this.barItems.length){
30410             return;
30411         }
30412      
30413         var width = 100 / this.barItems.length;
30414         
30415         Roo.each(this.barItems, function(i){
30416             i.el.setStyle('width', width + '%');
30417             i.topEl.el.setStyle('width', width + '%');
30418             i.bottomEl.el.setStyle('width', width + '%');
30419         }, this);
30420         
30421     }
30422     
30423 });
30424 /*
30425  * - LGPL
30426  *
30427  * Nav Progress Item
30428  * 
30429  */
30430
30431 /**
30432  * @class Roo.bootstrap.NavProgressItem
30433  * @extends Roo.bootstrap.Component
30434  * Bootstrap NavProgressItem class
30435  * @cfg {String} rid the reference id
30436  * @cfg {Boolean} active (true|false) Is item active default false
30437  * @cfg {Boolean} disabled (true|false) Is item active default false
30438  * @cfg {String} html
30439  * @cfg {String} position (top|bottom) text position default bottom
30440  * @cfg {String} icon show icon instead of number
30441  * 
30442  * @constructor
30443  * Create a new NavProgressItem
30444  * @param {Object} config The config object
30445  */
30446 Roo.bootstrap.NavProgressItem = function(config){
30447     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30448     this.addEvents({
30449         // raw events
30450         /**
30451          * @event click
30452          * The raw click event for the entire grid.
30453          * @param {Roo.bootstrap.NavProgressItem} this
30454          * @param {Roo.EventObject} e
30455          */
30456         "click" : true
30457     });
30458    
30459 };
30460
30461 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30462     
30463     rid : '',
30464     active : false,
30465     disabled : false,
30466     html : '',
30467     position : 'bottom',
30468     icon : false,
30469     
30470     getAutoCreate : function()
30471     {
30472         var iconCls = 'roo-navigation-bar-item-icon';
30473         
30474         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30475         
30476         var cfg = {
30477             tag: 'li',
30478             cls: 'roo-navigation-bar-item',
30479             cn : [
30480                 {
30481                     tag : 'i',
30482                     cls : iconCls
30483                 }
30484             ]
30485         };
30486         
30487         if(this.active){
30488             cfg.cls += ' active';
30489         }
30490         if(this.disabled){
30491             cfg.cls += ' disabled';
30492         }
30493         
30494         return cfg;
30495     },
30496     
30497     disable : function()
30498     {
30499         this.setDisabled(true);
30500     },
30501     
30502     enable : function()
30503     {
30504         this.setDisabled(false);
30505     },
30506     
30507     initEvents: function() 
30508     {
30509         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30510         
30511         this.iconEl.on('click', this.onClick, this);
30512     },
30513     
30514     onClick : function(e)
30515     {
30516         e.preventDefault();
30517         
30518         if(this.disabled){
30519             return;
30520         }
30521         
30522         if(this.fireEvent('click', this, e) === false){
30523             return;
30524         };
30525         
30526         this.parent().setActiveItem(this);
30527     },
30528     
30529     isActive: function () 
30530     {
30531         return this.active;
30532     },
30533     
30534     setActive : function(state)
30535     {
30536         if(this.active == state){
30537             return;
30538         }
30539         
30540         this.active = state;
30541         
30542         if (state) {
30543             this.el.addClass('active');
30544             return;
30545         }
30546         
30547         this.el.removeClass('active');
30548         
30549         return;
30550     },
30551     
30552     setDisabled : function(state)
30553     {
30554         if(this.disabled == state){
30555             return;
30556         }
30557         
30558         this.disabled = state;
30559         
30560         if (state) {
30561             this.el.addClass('disabled');
30562             return;
30563         }
30564         
30565         this.el.removeClass('disabled');
30566     },
30567     
30568     tooltipEl : function()
30569     {
30570         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30571     }
30572 });
30573  
30574
30575  /*
30576  * - LGPL
30577  *
30578  * FieldLabel
30579  * 
30580  */
30581
30582 /**
30583  * @class Roo.bootstrap.FieldLabel
30584  * @extends Roo.bootstrap.Component
30585  * Bootstrap FieldLabel class
30586  * @cfg {String} html contents of the element
30587  * @cfg {String} tag tag of the element default label
30588  * @cfg {String} cls class of the element
30589  * @cfg {String} target label target 
30590  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30591  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30592  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30593  * @cfg {String} iconTooltip default "This field is required"
30594  * @cfg {String} indicatorpos (left|right) default left
30595  * 
30596  * @constructor
30597  * Create a new FieldLabel
30598  * @param {Object} config The config object
30599  */
30600
30601 Roo.bootstrap.FieldLabel = function(config){
30602     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30603     
30604     this.addEvents({
30605             /**
30606              * @event invalid
30607              * Fires after the field has been marked as invalid.
30608              * @param {Roo.form.FieldLabel} this
30609              * @param {String} msg The validation message
30610              */
30611             invalid : true,
30612             /**
30613              * @event valid
30614              * Fires after the field has been validated with no errors.
30615              * @param {Roo.form.FieldLabel} this
30616              */
30617             valid : true
30618         });
30619 };
30620
30621 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30622     
30623     tag: 'label',
30624     cls: '',
30625     html: '',
30626     target: '',
30627     allowBlank : true,
30628     invalidClass : 'has-warning',
30629     validClass : 'has-success',
30630     iconTooltip : 'This field is required',
30631     indicatorpos : 'left',
30632     
30633     getAutoCreate : function(){
30634         
30635         var cls = "";
30636         if (!this.allowBlank) {
30637             cls  = "visible";
30638         }
30639         
30640         var cfg = {
30641             tag : this.tag,
30642             cls : 'roo-bootstrap-field-label ' + this.cls,
30643             for : this.target,
30644             cn : [
30645                 {
30646                     tag : 'i',
30647                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30648                     tooltip : this.iconTooltip
30649                 },
30650                 {
30651                     tag : 'span',
30652                     html : this.html
30653                 }
30654             ] 
30655         };
30656         
30657         if(this.indicatorpos == 'right'){
30658             var cfg = {
30659                 tag : this.tag,
30660                 cls : 'roo-bootstrap-field-label ' + this.cls,
30661                 for : this.target,
30662                 cn : [
30663                     {
30664                         tag : 'span',
30665                         html : this.html
30666                     },
30667                     {
30668                         tag : 'i',
30669                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30670                         tooltip : this.iconTooltip
30671                     }
30672                 ] 
30673             };
30674         }
30675         
30676         return cfg;
30677     },
30678     
30679     initEvents: function() 
30680     {
30681         Roo.bootstrap.Element.superclass.initEvents.call(this);
30682         
30683         this.indicator = this.indicatorEl();
30684         
30685         if(this.indicator){
30686             this.indicator.removeClass('visible');
30687             this.indicator.addClass('invisible');
30688         }
30689         
30690         Roo.bootstrap.FieldLabel.register(this);
30691     },
30692     
30693     indicatorEl : function()
30694     {
30695         var indicator = this.el.select('i.roo-required-indicator',true).first();
30696         
30697         if(!indicator){
30698             return false;
30699         }
30700         
30701         return indicator;
30702         
30703     },
30704     
30705     /**
30706      * Mark this field as valid
30707      */
30708     markValid : function()
30709     {
30710         if(this.indicator){
30711             this.indicator.removeClass('visible');
30712             this.indicator.addClass('invisible');
30713         }
30714         if (Roo.bootstrap.version == 3) {
30715             this.el.removeClass(this.invalidClass);
30716             this.el.addClass(this.validClass);
30717         } else {
30718             this.el.removeClass('is-invalid');
30719             this.el.addClass('is-valid');
30720         }
30721         
30722         
30723         this.fireEvent('valid', this);
30724     },
30725     
30726     /**
30727      * Mark this field as invalid
30728      * @param {String} msg The validation message
30729      */
30730     markInvalid : function(msg)
30731     {
30732         if(this.indicator){
30733             this.indicator.removeClass('invisible');
30734             this.indicator.addClass('visible');
30735         }
30736           if (Roo.bootstrap.version == 3) {
30737             this.el.removeClass(this.validClass);
30738             this.el.addClass(this.invalidClass);
30739         } else {
30740             this.el.removeClass('is-valid');
30741             this.el.addClass('is-invalid');
30742         }
30743         
30744         
30745         this.fireEvent('invalid', this, msg);
30746     }
30747     
30748    
30749 });
30750
30751 Roo.apply(Roo.bootstrap.FieldLabel, {
30752     
30753     groups: {},
30754     
30755      /**
30756     * register a FieldLabel Group
30757     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30758     */
30759     register : function(label)
30760     {
30761         if(this.groups.hasOwnProperty(label.target)){
30762             return;
30763         }
30764      
30765         this.groups[label.target] = label;
30766         
30767     },
30768     /**
30769     * fetch a FieldLabel Group based on the target
30770     * @param {string} target
30771     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30772     */
30773     get: function(target) {
30774         if (typeof(this.groups[target]) == 'undefined') {
30775             return false;
30776         }
30777         
30778         return this.groups[target] ;
30779     }
30780 });
30781
30782  
30783
30784  /*
30785  * - LGPL
30786  *
30787  * page DateSplitField.
30788  * 
30789  */
30790
30791
30792 /**
30793  * @class Roo.bootstrap.DateSplitField
30794  * @extends Roo.bootstrap.Component
30795  * Bootstrap DateSplitField class
30796  * @cfg {string} fieldLabel - the label associated
30797  * @cfg {Number} labelWidth set the width of label (0-12)
30798  * @cfg {String} labelAlign (top|left)
30799  * @cfg {Boolean} dayAllowBlank (true|false) default false
30800  * @cfg {Boolean} monthAllowBlank (true|false) default false
30801  * @cfg {Boolean} yearAllowBlank (true|false) default false
30802  * @cfg {string} dayPlaceholder 
30803  * @cfg {string} monthPlaceholder
30804  * @cfg {string} yearPlaceholder
30805  * @cfg {string} dayFormat default 'd'
30806  * @cfg {string} monthFormat default 'm'
30807  * @cfg {string} yearFormat default 'Y'
30808  * @cfg {Number} labellg set the width of label (1-12)
30809  * @cfg {Number} labelmd set the width of label (1-12)
30810  * @cfg {Number} labelsm set the width of label (1-12)
30811  * @cfg {Number} labelxs set the width of label (1-12)
30812
30813  *     
30814  * @constructor
30815  * Create a new DateSplitField
30816  * @param {Object} config The config object
30817  */
30818
30819 Roo.bootstrap.DateSplitField = function(config){
30820     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30821     
30822     this.addEvents({
30823         // raw events
30824          /**
30825          * @event years
30826          * getting the data of years
30827          * @param {Roo.bootstrap.DateSplitField} this
30828          * @param {Object} years
30829          */
30830         "years" : true,
30831         /**
30832          * @event days
30833          * getting the data of days
30834          * @param {Roo.bootstrap.DateSplitField} this
30835          * @param {Object} days
30836          */
30837         "days" : true,
30838         /**
30839          * @event invalid
30840          * Fires after the field has been marked as invalid.
30841          * @param {Roo.form.Field} this
30842          * @param {String} msg The validation message
30843          */
30844         invalid : true,
30845        /**
30846          * @event valid
30847          * Fires after the field has been validated with no errors.
30848          * @param {Roo.form.Field} this
30849          */
30850         valid : true
30851     });
30852 };
30853
30854 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30855     
30856     fieldLabel : '',
30857     labelAlign : 'top',
30858     labelWidth : 3,
30859     dayAllowBlank : false,
30860     monthAllowBlank : false,
30861     yearAllowBlank : false,
30862     dayPlaceholder : '',
30863     monthPlaceholder : '',
30864     yearPlaceholder : '',
30865     dayFormat : 'd',
30866     monthFormat : 'm',
30867     yearFormat : 'Y',
30868     isFormField : true,
30869     labellg : 0,
30870     labelmd : 0,
30871     labelsm : 0,
30872     labelxs : 0,
30873     
30874     getAutoCreate : function()
30875     {
30876         var cfg = {
30877             tag : 'div',
30878             cls : 'row roo-date-split-field-group',
30879             cn : [
30880                 {
30881                     tag : 'input',
30882                     type : 'hidden',
30883                     cls : 'form-hidden-field roo-date-split-field-group-value',
30884                     name : this.name
30885                 }
30886             ]
30887         };
30888         
30889         var labelCls = 'col-md-12';
30890         var contentCls = 'col-md-4';
30891         
30892         if(this.fieldLabel){
30893             
30894             var label = {
30895                 tag : 'div',
30896                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30897                 cn : [
30898                     {
30899                         tag : 'label',
30900                         html : this.fieldLabel
30901                     }
30902                 ]
30903             };
30904             
30905             if(this.labelAlign == 'left'){
30906             
30907                 if(this.labelWidth > 12){
30908                     label.style = "width: " + this.labelWidth + 'px';
30909                 }
30910
30911                 if(this.labelWidth < 13 && this.labelmd == 0){
30912                     this.labelmd = this.labelWidth;
30913                 }
30914
30915                 if(this.labellg > 0){
30916                     labelCls = ' col-lg-' + this.labellg;
30917                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30918                 }
30919
30920                 if(this.labelmd > 0){
30921                     labelCls = ' col-md-' + this.labelmd;
30922                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30923                 }
30924
30925                 if(this.labelsm > 0){
30926                     labelCls = ' col-sm-' + this.labelsm;
30927                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30928                 }
30929
30930                 if(this.labelxs > 0){
30931                     labelCls = ' col-xs-' + this.labelxs;
30932                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30933                 }
30934             }
30935             
30936             label.cls += ' ' + labelCls;
30937             
30938             cfg.cn.push(label);
30939         }
30940         
30941         Roo.each(['day', 'month', 'year'], function(t){
30942             cfg.cn.push({
30943                 tag : 'div',
30944                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30945             });
30946         }, this);
30947         
30948         return cfg;
30949     },
30950     
30951     inputEl: function ()
30952     {
30953         return this.el.select('.roo-date-split-field-group-value', true).first();
30954     },
30955     
30956     onRender : function(ct, position) 
30957     {
30958         var _this = this;
30959         
30960         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30961         
30962         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30963         
30964         this.dayField = new Roo.bootstrap.ComboBox({
30965             allowBlank : this.dayAllowBlank,
30966             alwaysQuery : true,
30967             displayField : 'value',
30968             editable : false,
30969             fieldLabel : '',
30970             forceSelection : true,
30971             mode : 'local',
30972             placeholder : this.dayPlaceholder,
30973             selectOnFocus : true,
30974             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30975             triggerAction : 'all',
30976             typeAhead : true,
30977             valueField : 'value',
30978             store : new Roo.data.SimpleStore({
30979                 data : (function() {    
30980                     var days = [];
30981                     _this.fireEvent('days', _this, days);
30982                     return days;
30983                 })(),
30984                 fields : [ 'value' ]
30985             }),
30986             listeners : {
30987                 select : function (_self, record, index)
30988                 {
30989                     _this.setValue(_this.getValue());
30990                 }
30991             }
30992         });
30993
30994         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30995         
30996         this.monthField = new Roo.bootstrap.MonthField({
30997             after : '<i class=\"fa fa-calendar\"></i>',
30998             allowBlank : this.monthAllowBlank,
30999             placeholder : this.monthPlaceholder,
31000             readOnly : true,
31001             listeners : {
31002                 render : function (_self)
31003                 {
31004                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31005                         e.preventDefault();
31006                         _self.focus();
31007                     });
31008                 },
31009                 select : function (_self, oldvalue, newvalue)
31010                 {
31011                     _this.setValue(_this.getValue());
31012                 }
31013             }
31014         });
31015         
31016         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31017         
31018         this.yearField = new Roo.bootstrap.ComboBox({
31019             allowBlank : this.yearAllowBlank,
31020             alwaysQuery : true,
31021             displayField : 'value',
31022             editable : false,
31023             fieldLabel : '',
31024             forceSelection : true,
31025             mode : 'local',
31026             placeholder : this.yearPlaceholder,
31027             selectOnFocus : true,
31028             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31029             triggerAction : 'all',
31030             typeAhead : true,
31031             valueField : 'value',
31032             store : new Roo.data.SimpleStore({
31033                 data : (function() {
31034                     var years = [];
31035                     _this.fireEvent('years', _this, years);
31036                     return years;
31037                 })(),
31038                 fields : [ 'value' ]
31039             }),
31040             listeners : {
31041                 select : function (_self, record, index)
31042                 {
31043                     _this.setValue(_this.getValue());
31044                 }
31045             }
31046         });
31047
31048         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31049     },
31050     
31051     setValue : function(v, format)
31052     {
31053         this.inputEl.dom.value = v;
31054         
31055         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31056         
31057         var d = Date.parseDate(v, f);
31058         
31059         if(!d){
31060             this.validate();
31061             return;
31062         }
31063         
31064         this.setDay(d.format(this.dayFormat));
31065         this.setMonth(d.format(this.monthFormat));
31066         this.setYear(d.format(this.yearFormat));
31067         
31068         this.validate();
31069         
31070         return;
31071     },
31072     
31073     setDay : function(v)
31074     {
31075         this.dayField.setValue(v);
31076         this.inputEl.dom.value = this.getValue();
31077         this.validate();
31078         return;
31079     },
31080     
31081     setMonth : function(v)
31082     {
31083         this.monthField.setValue(v, true);
31084         this.inputEl.dom.value = this.getValue();
31085         this.validate();
31086         return;
31087     },
31088     
31089     setYear : function(v)
31090     {
31091         this.yearField.setValue(v);
31092         this.inputEl.dom.value = this.getValue();
31093         this.validate();
31094         return;
31095     },
31096     
31097     getDay : function()
31098     {
31099         return this.dayField.getValue();
31100     },
31101     
31102     getMonth : function()
31103     {
31104         return this.monthField.getValue();
31105     },
31106     
31107     getYear : function()
31108     {
31109         return this.yearField.getValue();
31110     },
31111     
31112     getValue : function()
31113     {
31114         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31115         
31116         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31117         
31118         return date;
31119     },
31120     
31121     reset : function()
31122     {
31123         this.setDay('');
31124         this.setMonth('');
31125         this.setYear('');
31126         this.inputEl.dom.value = '';
31127         this.validate();
31128         return;
31129     },
31130     
31131     validate : function()
31132     {
31133         var d = this.dayField.validate();
31134         var m = this.monthField.validate();
31135         var y = this.yearField.validate();
31136         
31137         var valid = true;
31138         
31139         if(
31140                 (!this.dayAllowBlank && !d) ||
31141                 (!this.monthAllowBlank && !m) ||
31142                 (!this.yearAllowBlank && !y)
31143         ){
31144             valid = false;
31145         }
31146         
31147         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31148             return valid;
31149         }
31150         
31151         if(valid){
31152             this.markValid();
31153             return valid;
31154         }
31155         
31156         this.markInvalid();
31157         
31158         return valid;
31159     },
31160     
31161     markValid : function()
31162     {
31163         
31164         var label = this.el.select('label', true).first();
31165         var icon = this.el.select('i.fa-star', true).first();
31166
31167         if(label && icon){
31168             icon.remove();
31169         }
31170         
31171         this.fireEvent('valid', this);
31172     },
31173     
31174      /**
31175      * Mark this field as invalid
31176      * @param {String} msg The validation message
31177      */
31178     markInvalid : function(msg)
31179     {
31180         
31181         var label = this.el.select('label', true).first();
31182         var icon = this.el.select('i.fa-star', true).first();
31183
31184         if(label && !icon){
31185             this.el.select('.roo-date-split-field-label', true).createChild({
31186                 tag : 'i',
31187                 cls : 'text-danger fa fa-lg fa-star',
31188                 tooltip : 'This field is required',
31189                 style : 'margin-right:5px;'
31190             }, label, true);
31191         }
31192         
31193         this.fireEvent('invalid', this, msg);
31194     },
31195     
31196     clearInvalid : function()
31197     {
31198         var label = this.el.select('label', true).first();
31199         var icon = this.el.select('i.fa-star', true).first();
31200
31201         if(label && icon){
31202             icon.remove();
31203         }
31204         
31205         this.fireEvent('valid', this);
31206     },
31207     
31208     getName: function()
31209     {
31210         return this.name;
31211     }
31212     
31213 });
31214
31215  /**
31216  *
31217  * This is based on 
31218  * http://masonry.desandro.com
31219  *
31220  * The idea is to render all the bricks based on vertical width...
31221  *
31222  * The original code extends 'outlayer' - we might need to use that....
31223  * 
31224  */
31225
31226
31227 /**
31228  * @class Roo.bootstrap.LayoutMasonry
31229  * @extends Roo.bootstrap.Component
31230  * Bootstrap Layout Masonry class
31231  * 
31232  * @constructor
31233  * Create a new Element
31234  * @param {Object} config The config object
31235  */
31236
31237 Roo.bootstrap.LayoutMasonry = function(config){
31238     
31239     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31240     
31241     this.bricks = [];
31242     
31243     Roo.bootstrap.LayoutMasonry.register(this);
31244     
31245     this.addEvents({
31246         // raw events
31247         /**
31248          * @event layout
31249          * Fire after layout the items
31250          * @param {Roo.bootstrap.LayoutMasonry} this
31251          * @param {Roo.EventObject} e
31252          */
31253         "layout" : true
31254     });
31255     
31256 };
31257
31258 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31259     
31260     /**
31261      * @cfg {Boolean} isLayoutInstant = no animation?
31262      */   
31263     isLayoutInstant : false, // needed?
31264    
31265     /**
31266      * @cfg {Number} boxWidth  width of the columns
31267      */   
31268     boxWidth : 450,
31269     
31270       /**
31271      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31272      */   
31273     boxHeight : 0,
31274     
31275     /**
31276      * @cfg {Number} padWidth padding below box..
31277      */   
31278     padWidth : 10, 
31279     
31280     /**
31281      * @cfg {Number} gutter gutter width..
31282      */   
31283     gutter : 10,
31284     
31285      /**
31286      * @cfg {Number} maxCols maximum number of columns
31287      */   
31288     
31289     maxCols: 0,
31290     
31291     /**
31292      * @cfg {Boolean} isAutoInitial defalut true
31293      */   
31294     isAutoInitial : true, 
31295     
31296     containerWidth: 0,
31297     
31298     /**
31299      * @cfg {Boolean} isHorizontal defalut false
31300      */   
31301     isHorizontal : false, 
31302
31303     currentSize : null,
31304     
31305     tag: 'div',
31306     
31307     cls: '',
31308     
31309     bricks: null, //CompositeElement
31310     
31311     cols : 1,
31312     
31313     _isLayoutInited : false,
31314     
31315 //    isAlternative : false, // only use for vertical layout...
31316     
31317     /**
31318      * @cfg {Number} alternativePadWidth padding below box..
31319      */   
31320     alternativePadWidth : 50,
31321     
31322     selectedBrick : [],
31323     
31324     getAutoCreate : function(){
31325         
31326         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31327         
31328         var cfg = {
31329             tag: this.tag,
31330             cls: 'blog-masonary-wrapper ' + this.cls,
31331             cn : {
31332                 cls : 'mas-boxes masonary'
31333             }
31334         };
31335         
31336         return cfg;
31337     },
31338     
31339     getChildContainer: function( )
31340     {
31341         if (this.boxesEl) {
31342             return this.boxesEl;
31343         }
31344         
31345         this.boxesEl = this.el.select('.mas-boxes').first();
31346         
31347         return this.boxesEl;
31348     },
31349     
31350     
31351     initEvents : function()
31352     {
31353         var _this = this;
31354         
31355         if(this.isAutoInitial){
31356             Roo.log('hook children rendered');
31357             this.on('childrenrendered', function() {
31358                 Roo.log('children rendered');
31359                 _this.initial();
31360             } ,this);
31361         }
31362     },
31363     
31364     initial : function()
31365     {
31366         this.selectedBrick = [];
31367         
31368         this.currentSize = this.el.getBox(true);
31369         
31370         Roo.EventManager.onWindowResize(this.resize, this); 
31371
31372         if(!this.isAutoInitial){
31373             this.layout();
31374             return;
31375         }
31376         
31377         this.layout();
31378         
31379         return;
31380         //this.layout.defer(500,this);
31381         
31382     },
31383     
31384     resize : function()
31385     {
31386         var cs = this.el.getBox(true);
31387         
31388         if (
31389                 this.currentSize.width == cs.width && 
31390                 this.currentSize.x == cs.x && 
31391                 this.currentSize.height == cs.height && 
31392                 this.currentSize.y == cs.y 
31393         ) {
31394             Roo.log("no change in with or X or Y");
31395             return;
31396         }
31397         
31398         this.currentSize = cs;
31399         
31400         this.layout();
31401         
31402     },
31403     
31404     layout : function()
31405     {   
31406         this._resetLayout();
31407         
31408         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31409         
31410         this.layoutItems( isInstant );
31411       
31412         this._isLayoutInited = true;
31413         
31414         this.fireEvent('layout', this);
31415         
31416     },
31417     
31418     _resetLayout : function()
31419     {
31420         if(this.isHorizontal){
31421             this.horizontalMeasureColumns();
31422             return;
31423         }
31424         
31425         this.verticalMeasureColumns();
31426         
31427     },
31428     
31429     verticalMeasureColumns : function()
31430     {
31431         this.getContainerWidth();
31432         
31433 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31434 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31435 //            return;
31436 //        }
31437         
31438         var boxWidth = this.boxWidth + this.padWidth;
31439         
31440         if(this.containerWidth < this.boxWidth){
31441             boxWidth = this.containerWidth
31442         }
31443         
31444         var containerWidth = this.containerWidth;
31445         
31446         var cols = Math.floor(containerWidth / boxWidth);
31447         
31448         this.cols = Math.max( cols, 1 );
31449         
31450         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31451         
31452         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31453         
31454         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31455         
31456         this.colWidth = boxWidth + avail - this.padWidth;
31457         
31458         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31459         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31460     },
31461     
31462     horizontalMeasureColumns : function()
31463     {
31464         this.getContainerWidth();
31465         
31466         var boxWidth = this.boxWidth;
31467         
31468         if(this.containerWidth < boxWidth){
31469             boxWidth = this.containerWidth;
31470         }
31471         
31472         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31473         
31474         this.el.setHeight(boxWidth);
31475         
31476     },
31477     
31478     getContainerWidth : function()
31479     {
31480         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31481     },
31482     
31483     layoutItems : function( isInstant )
31484     {
31485         Roo.log(this.bricks);
31486         
31487         var items = Roo.apply([], this.bricks);
31488         
31489         if(this.isHorizontal){
31490             this._horizontalLayoutItems( items , isInstant );
31491             return;
31492         }
31493         
31494 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31495 //            this._verticalAlternativeLayoutItems( items , isInstant );
31496 //            return;
31497 //        }
31498         
31499         this._verticalLayoutItems( items , isInstant );
31500         
31501     },
31502     
31503     _verticalLayoutItems : function ( items , isInstant)
31504     {
31505         if ( !items || !items.length ) {
31506             return;
31507         }
31508         
31509         var standard = [
31510             ['xs', 'xs', 'xs', 'tall'],
31511             ['xs', 'xs', 'tall'],
31512             ['xs', 'xs', 'sm'],
31513             ['xs', 'xs', 'xs'],
31514             ['xs', 'tall'],
31515             ['xs', 'sm'],
31516             ['xs', 'xs'],
31517             ['xs'],
31518             
31519             ['sm', 'xs', 'xs'],
31520             ['sm', 'xs'],
31521             ['sm'],
31522             
31523             ['tall', 'xs', 'xs', 'xs'],
31524             ['tall', 'xs', 'xs'],
31525             ['tall', 'xs'],
31526             ['tall']
31527             
31528         ];
31529         
31530         var queue = [];
31531         
31532         var boxes = [];
31533         
31534         var box = [];
31535         
31536         Roo.each(items, function(item, k){
31537             
31538             switch (item.size) {
31539                 // these layouts take up a full box,
31540                 case 'md' :
31541                 case 'md-left' :
31542                 case 'md-right' :
31543                 case 'wide' :
31544                     
31545                     if(box.length){
31546                         boxes.push(box);
31547                         box = [];
31548                     }
31549                     
31550                     boxes.push([item]);
31551                     
31552                     break;
31553                     
31554                 case 'xs' :
31555                 case 'sm' :
31556                 case 'tall' :
31557                     
31558                     box.push(item);
31559                     
31560                     break;
31561                 default :
31562                     break;
31563                     
31564             }
31565             
31566         }, this);
31567         
31568         if(box.length){
31569             boxes.push(box);
31570             box = [];
31571         }
31572         
31573         var filterPattern = function(box, length)
31574         {
31575             if(!box.length){
31576                 return;
31577             }
31578             
31579             var match = false;
31580             
31581             var pattern = box.slice(0, length);
31582             
31583             var format = [];
31584             
31585             Roo.each(pattern, function(i){
31586                 format.push(i.size);
31587             }, this);
31588             
31589             Roo.each(standard, function(s){
31590                 
31591                 if(String(s) != String(format)){
31592                     return;
31593                 }
31594                 
31595                 match = true;
31596                 return false;
31597                 
31598             }, this);
31599             
31600             if(!match && length == 1){
31601                 return;
31602             }
31603             
31604             if(!match){
31605                 filterPattern(box, length - 1);
31606                 return;
31607             }
31608                 
31609             queue.push(pattern);
31610
31611             box = box.slice(length, box.length);
31612
31613             filterPattern(box, 4);
31614
31615             return;
31616             
31617         }
31618         
31619         Roo.each(boxes, function(box, k){
31620             
31621             if(!box.length){
31622                 return;
31623             }
31624             
31625             if(box.length == 1){
31626                 queue.push(box);
31627                 return;
31628             }
31629             
31630             filterPattern(box, 4);
31631             
31632         }, this);
31633         
31634         this._processVerticalLayoutQueue( queue, isInstant );
31635         
31636     },
31637     
31638 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31639 //    {
31640 //        if ( !items || !items.length ) {
31641 //            return;
31642 //        }
31643 //
31644 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31645 //        
31646 //    },
31647     
31648     _horizontalLayoutItems : function ( items , isInstant)
31649     {
31650         if ( !items || !items.length || items.length < 3) {
31651             return;
31652         }
31653         
31654         items.reverse();
31655         
31656         var eItems = items.slice(0, 3);
31657         
31658         items = items.slice(3, items.length);
31659         
31660         var standard = [
31661             ['xs', 'xs', 'xs', 'wide'],
31662             ['xs', 'xs', 'wide'],
31663             ['xs', 'xs', 'sm'],
31664             ['xs', 'xs', 'xs'],
31665             ['xs', 'wide'],
31666             ['xs', 'sm'],
31667             ['xs', 'xs'],
31668             ['xs'],
31669             
31670             ['sm', 'xs', 'xs'],
31671             ['sm', 'xs'],
31672             ['sm'],
31673             
31674             ['wide', 'xs', 'xs', 'xs'],
31675             ['wide', 'xs', 'xs'],
31676             ['wide', 'xs'],
31677             ['wide'],
31678             
31679             ['wide-thin']
31680         ];
31681         
31682         var queue = [];
31683         
31684         var boxes = [];
31685         
31686         var box = [];
31687         
31688         Roo.each(items, function(item, k){
31689             
31690             switch (item.size) {
31691                 case 'md' :
31692                 case 'md-left' :
31693                 case 'md-right' :
31694                 case 'tall' :
31695                     
31696                     if(box.length){
31697                         boxes.push(box);
31698                         box = [];
31699                     }
31700                     
31701                     boxes.push([item]);
31702                     
31703                     break;
31704                     
31705                 case 'xs' :
31706                 case 'sm' :
31707                 case 'wide' :
31708                 case 'wide-thin' :
31709                     
31710                     box.push(item);
31711                     
31712                     break;
31713                 default :
31714                     break;
31715                     
31716             }
31717             
31718         }, this);
31719         
31720         if(box.length){
31721             boxes.push(box);
31722             box = [];
31723         }
31724         
31725         var filterPattern = function(box, length)
31726         {
31727             if(!box.length){
31728                 return;
31729             }
31730             
31731             var match = false;
31732             
31733             var pattern = box.slice(0, length);
31734             
31735             var format = [];
31736             
31737             Roo.each(pattern, function(i){
31738                 format.push(i.size);
31739             }, this);
31740             
31741             Roo.each(standard, function(s){
31742                 
31743                 if(String(s) != String(format)){
31744                     return;
31745                 }
31746                 
31747                 match = true;
31748                 return false;
31749                 
31750             }, this);
31751             
31752             if(!match && length == 1){
31753                 return;
31754             }
31755             
31756             if(!match){
31757                 filterPattern(box, length - 1);
31758                 return;
31759             }
31760                 
31761             queue.push(pattern);
31762
31763             box = box.slice(length, box.length);
31764
31765             filterPattern(box, 4);
31766
31767             return;
31768             
31769         }
31770         
31771         Roo.each(boxes, function(box, k){
31772             
31773             if(!box.length){
31774                 return;
31775             }
31776             
31777             if(box.length == 1){
31778                 queue.push(box);
31779                 return;
31780             }
31781             
31782             filterPattern(box, 4);
31783             
31784         }, this);
31785         
31786         
31787         var prune = [];
31788         
31789         var pos = this.el.getBox(true);
31790         
31791         var minX = pos.x;
31792         
31793         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31794         
31795         var hit_end = false;
31796         
31797         Roo.each(queue, function(box){
31798             
31799             if(hit_end){
31800                 
31801                 Roo.each(box, function(b){
31802                 
31803                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31804                     b.el.hide();
31805
31806                 }, this);
31807
31808                 return;
31809             }
31810             
31811             var mx = 0;
31812             
31813             Roo.each(box, function(b){
31814                 
31815                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31816                 b.el.show();
31817
31818                 mx = Math.max(mx, b.x);
31819                 
31820             }, this);
31821             
31822             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31823             
31824             if(maxX < minX){
31825                 
31826                 Roo.each(box, function(b){
31827                 
31828                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31829                     b.el.hide();
31830                     
31831                 }, this);
31832                 
31833                 hit_end = true;
31834                 
31835                 return;
31836             }
31837             
31838             prune.push(box);
31839             
31840         }, this);
31841         
31842         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31843     },
31844     
31845     /** Sets position of item in DOM
31846     * @param {Element} item
31847     * @param {Number} x - horizontal position
31848     * @param {Number} y - vertical position
31849     * @param {Boolean} isInstant - disables transitions
31850     */
31851     _processVerticalLayoutQueue : function( queue, isInstant )
31852     {
31853         var pos = this.el.getBox(true);
31854         var x = pos.x;
31855         var y = pos.y;
31856         var maxY = [];
31857         
31858         for (var i = 0; i < this.cols; i++){
31859             maxY[i] = pos.y;
31860         }
31861         
31862         Roo.each(queue, function(box, k){
31863             
31864             var col = k % this.cols;
31865             
31866             Roo.each(box, function(b,kk){
31867                 
31868                 b.el.position('absolute');
31869                 
31870                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31871                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31872                 
31873                 if(b.size == 'md-left' || b.size == 'md-right'){
31874                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31875                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31876                 }
31877                 
31878                 b.el.setWidth(width);
31879                 b.el.setHeight(height);
31880                 // iframe?
31881                 b.el.select('iframe',true).setSize(width,height);
31882                 
31883             }, this);
31884             
31885             for (var i = 0; i < this.cols; i++){
31886                 
31887                 if(maxY[i] < maxY[col]){
31888                     col = i;
31889                     continue;
31890                 }
31891                 
31892                 col = Math.min(col, i);
31893                 
31894             }
31895             
31896             x = pos.x + col * (this.colWidth + this.padWidth);
31897             
31898             y = maxY[col];
31899             
31900             var positions = [];
31901             
31902             switch (box.length){
31903                 case 1 :
31904                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31905                     break;
31906                 case 2 :
31907                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31908                     break;
31909                 case 3 :
31910                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31911                     break;
31912                 case 4 :
31913                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31914                     break;
31915                 default :
31916                     break;
31917             }
31918             
31919             Roo.each(box, function(b,kk){
31920                 
31921                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31922                 
31923                 var sz = b.el.getSize();
31924                 
31925                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31926                 
31927             }, this);
31928             
31929         }, this);
31930         
31931         var mY = 0;
31932         
31933         for (var i = 0; i < this.cols; i++){
31934             mY = Math.max(mY, maxY[i]);
31935         }
31936         
31937         this.el.setHeight(mY - pos.y);
31938         
31939     },
31940     
31941 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31942 //    {
31943 //        var pos = this.el.getBox(true);
31944 //        var x = pos.x;
31945 //        var y = pos.y;
31946 //        var maxX = pos.right;
31947 //        
31948 //        var maxHeight = 0;
31949 //        
31950 //        Roo.each(items, function(item, k){
31951 //            
31952 //            var c = k % 2;
31953 //            
31954 //            item.el.position('absolute');
31955 //                
31956 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31957 //
31958 //            item.el.setWidth(width);
31959 //
31960 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31961 //
31962 //            item.el.setHeight(height);
31963 //            
31964 //            if(c == 0){
31965 //                item.el.setXY([x, y], isInstant ? false : true);
31966 //            } else {
31967 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31968 //            }
31969 //            
31970 //            y = y + height + this.alternativePadWidth;
31971 //            
31972 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31973 //            
31974 //        }, this);
31975 //        
31976 //        this.el.setHeight(maxHeight);
31977 //        
31978 //    },
31979     
31980     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31981     {
31982         var pos = this.el.getBox(true);
31983         
31984         var minX = pos.x;
31985         var minY = pos.y;
31986         
31987         var maxX = pos.right;
31988         
31989         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31990         
31991         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31992         
31993         Roo.each(queue, function(box, k){
31994             
31995             Roo.each(box, function(b, kk){
31996                 
31997                 b.el.position('absolute');
31998                 
31999                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32000                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32001                 
32002                 if(b.size == 'md-left' || b.size == 'md-right'){
32003                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32004                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32005                 }
32006                 
32007                 b.el.setWidth(width);
32008                 b.el.setHeight(height);
32009                 
32010             }, this);
32011             
32012             if(!box.length){
32013                 return;
32014             }
32015             
32016             var positions = [];
32017             
32018             switch (box.length){
32019                 case 1 :
32020                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32021                     break;
32022                 case 2 :
32023                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32024                     break;
32025                 case 3 :
32026                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32027                     break;
32028                 case 4 :
32029                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32030                     break;
32031                 default :
32032                     break;
32033             }
32034             
32035             Roo.each(box, function(b,kk){
32036                 
32037                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32038                 
32039                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32040                 
32041             }, this);
32042             
32043         }, this);
32044         
32045     },
32046     
32047     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32048     {
32049         Roo.each(eItems, function(b,k){
32050             
32051             b.size = (k == 0) ? 'sm' : 'xs';
32052             b.x = (k == 0) ? 2 : 1;
32053             b.y = (k == 0) ? 2 : 1;
32054             
32055             b.el.position('absolute');
32056             
32057             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32058                 
32059             b.el.setWidth(width);
32060             
32061             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32062             
32063             b.el.setHeight(height);
32064             
32065         }, this);
32066
32067         var positions = [];
32068         
32069         positions.push({
32070             x : maxX - this.unitWidth * 2 - this.gutter,
32071             y : minY
32072         });
32073         
32074         positions.push({
32075             x : maxX - this.unitWidth,
32076             y : minY + (this.unitWidth + this.gutter) * 2
32077         });
32078         
32079         positions.push({
32080             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32081             y : minY
32082         });
32083         
32084         Roo.each(eItems, function(b,k){
32085             
32086             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32087
32088         }, this);
32089         
32090     },
32091     
32092     getVerticalOneBoxColPositions : function(x, y, box)
32093     {
32094         var pos = [];
32095         
32096         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32097         
32098         if(box[0].size == 'md-left'){
32099             rand = 0;
32100         }
32101         
32102         if(box[0].size == 'md-right'){
32103             rand = 1;
32104         }
32105         
32106         pos.push({
32107             x : x + (this.unitWidth + this.gutter) * rand,
32108             y : y
32109         });
32110         
32111         return pos;
32112     },
32113     
32114     getVerticalTwoBoxColPositions : function(x, y, box)
32115     {
32116         var pos = [];
32117         
32118         if(box[0].size == 'xs'){
32119             
32120             pos.push({
32121                 x : x,
32122                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32123             });
32124
32125             pos.push({
32126                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32127                 y : y
32128             });
32129             
32130             return pos;
32131             
32132         }
32133         
32134         pos.push({
32135             x : x,
32136             y : y
32137         });
32138
32139         pos.push({
32140             x : x + (this.unitWidth + this.gutter) * 2,
32141             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32142         });
32143         
32144         return pos;
32145         
32146     },
32147     
32148     getVerticalThreeBoxColPositions : function(x, y, box)
32149     {
32150         var pos = [];
32151         
32152         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32153             
32154             pos.push({
32155                 x : x,
32156                 y : y
32157             });
32158
32159             pos.push({
32160                 x : x + (this.unitWidth + this.gutter) * 1,
32161                 y : y
32162             });
32163             
32164             pos.push({
32165                 x : x + (this.unitWidth + this.gutter) * 2,
32166                 y : y
32167             });
32168             
32169             return pos;
32170             
32171         }
32172         
32173         if(box[0].size == 'xs' && box[1].size == 'xs'){
32174             
32175             pos.push({
32176                 x : x,
32177                 y : y
32178             });
32179
32180             pos.push({
32181                 x : x,
32182                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32183             });
32184             
32185             pos.push({
32186                 x : x + (this.unitWidth + this.gutter) * 1,
32187                 y : y
32188             });
32189             
32190             return pos;
32191             
32192         }
32193         
32194         pos.push({
32195             x : x,
32196             y : y
32197         });
32198
32199         pos.push({
32200             x : x + (this.unitWidth + this.gutter) * 2,
32201             y : y
32202         });
32203
32204         pos.push({
32205             x : x + (this.unitWidth + this.gutter) * 2,
32206             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32207         });
32208             
32209         return pos;
32210         
32211     },
32212     
32213     getVerticalFourBoxColPositions : function(x, y, box)
32214     {
32215         var pos = [];
32216         
32217         if(box[0].size == 'xs'){
32218             
32219             pos.push({
32220                 x : x,
32221                 y : y
32222             });
32223
32224             pos.push({
32225                 x : x,
32226                 y : y + (this.unitHeight + this.gutter) * 1
32227             });
32228             
32229             pos.push({
32230                 x : x,
32231                 y : y + (this.unitHeight + this.gutter) * 2
32232             });
32233             
32234             pos.push({
32235                 x : x + (this.unitWidth + this.gutter) * 1,
32236                 y : y
32237             });
32238             
32239             return pos;
32240             
32241         }
32242         
32243         pos.push({
32244             x : x,
32245             y : y
32246         });
32247
32248         pos.push({
32249             x : x + (this.unitWidth + this.gutter) * 2,
32250             y : y
32251         });
32252
32253         pos.push({
32254             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32255             y : y + (this.unitHeight + this.gutter) * 1
32256         });
32257
32258         pos.push({
32259             x : x + (this.unitWidth + this.gutter) * 2,
32260             y : y + (this.unitWidth + this.gutter) * 2
32261         });
32262
32263         return pos;
32264         
32265     },
32266     
32267     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32268     {
32269         var pos = [];
32270         
32271         if(box[0].size == 'md-left'){
32272             pos.push({
32273                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32274                 y : minY
32275             });
32276             
32277             return pos;
32278         }
32279         
32280         if(box[0].size == 'md-right'){
32281             pos.push({
32282                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32283                 y : minY + (this.unitWidth + this.gutter) * 1
32284             });
32285             
32286             return pos;
32287         }
32288         
32289         var rand = Math.floor(Math.random() * (4 - box[0].y));
32290         
32291         pos.push({
32292             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32293             y : minY + (this.unitWidth + this.gutter) * rand
32294         });
32295         
32296         return pos;
32297         
32298     },
32299     
32300     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32301     {
32302         var pos = [];
32303         
32304         if(box[0].size == 'xs'){
32305             
32306             pos.push({
32307                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32308                 y : minY
32309             });
32310
32311             pos.push({
32312                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32313                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32314             });
32315             
32316             return pos;
32317             
32318         }
32319         
32320         pos.push({
32321             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32322             y : minY
32323         });
32324
32325         pos.push({
32326             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32327             y : minY + (this.unitWidth + this.gutter) * 2
32328         });
32329         
32330         return pos;
32331         
32332     },
32333     
32334     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32335     {
32336         var pos = [];
32337         
32338         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32339             
32340             pos.push({
32341                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32342                 y : minY
32343             });
32344
32345             pos.push({
32346                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32347                 y : minY + (this.unitWidth + this.gutter) * 1
32348             });
32349             
32350             pos.push({
32351                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32352                 y : minY + (this.unitWidth + this.gutter) * 2
32353             });
32354             
32355             return pos;
32356             
32357         }
32358         
32359         if(box[0].size == 'xs' && box[1].size == 'xs'){
32360             
32361             pos.push({
32362                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32363                 y : minY
32364             });
32365
32366             pos.push({
32367                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32368                 y : minY
32369             });
32370             
32371             pos.push({
32372                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32373                 y : minY + (this.unitWidth + this.gutter) * 1
32374             });
32375             
32376             return pos;
32377             
32378         }
32379         
32380         pos.push({
32381             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32382             y : minY
32383         });
32384
32385         pos.push({
32386             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32387             y : minY + (this.unitWidth + this.gutter) * 2
32388         });
32389
32390         pos.push({
32391             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32392             y : minY + (this.unitWidth + this.gutter) * 2
32393         });
32394             
32395         return pos;
32396         
32397     },
32398     
32399     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32400     {
32401         var pos = [];
32402         
32403         if(box[0].size == 'xs'){
32404             
32405             pos.push({
32406                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32407                 y : minY
32408             });
32409
32410             pos.push({
32411                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32412                 y : minY
32413             });
32414             
32415             pos.push({
32416                 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),
32417                 y : minY
32418             });
32419             
32420             pos.push({
32421                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32422                 y : minY + (this.unitWidth + this.gutter) * 1
32423             });
32424             
32425             return pos;
32426             
32427         }
32428         
32429         pos.push({
32430             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32431             y : minY
32432         });
32433         
32434         pos.push({
32435             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32436             y : minY + (this.unitWidth + this.gutter) * 2
32437         });
32438         
32439         pos.push({
32440             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32441             y : minY + (this.unitWidth + this.gutter) * 2
32442         });
32443         
32444         pos.push({
32445             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),
32446             y : minY + (this.unitWidth + this.gutter) * 2
32447         });
32448
32449         return pos;
32450         
32451     },
32452     
32453     /**
32454     * remove a Masonry Brick
32455     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32456     */
32457     removeBrick : function(brick_id)
32458     {
32459         if (!brick_id) {
32460             return;
32461         }
32462         
32463         for (var i = 0; i<this.bricks.length; i++) {
32464             if (this.bricks[i].id == brick_id) {
32465                 this.bricks.splice(i,1);
32466                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32467                 this.initial();
32468             }
32469         }
32470     },
32471     
32472     /**
32473     * adds a Masonry Brick
32474     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32475     */
32476     addBrick : function(cfg)
32477     {
32478         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32479         //this.register(cn);
32480         cn.parentId = this.id;
32481         cn.render(this.el);
32482         return cn;
32483     },
32484     
32485     /**
32486     * register a Masonry Brick
32487     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32488     */
32489     
32490     register : function(brick)
32491     {
32492         this.bricks.push(brick);
32493         brick.masonryId = this.id;
32494     },
32495     
32496     /**
32497     * clear all the Masonry Brick
32498     */
32499     clearAll : function()
32500     {
32501         this.bricks = [];
32502         //this.getChildContainer().dom.innerHTML = "";
32503         this.el.dom.innerHTML = '';
32504     },
32505     
32506     getSelected : function()
32507     {
32508         if (!this.selectedBrick) {
32509             return false;
32510         }
32511         
32512         return this.selectedBrick;
32513     }
32514 });
32515
32516 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32517     
32518     groups: {},
32519      /**
32520     * register a Masonry Layout
32521     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32522     */
32523     
32524     register : function(layout)
32525     {
32526         this.groups[layout.id] = layout;
32527     },
32528     /**
32529     * fetch a  Masonry Layout based on the masonry layout ID
32530     * @param {string} the masonry layout to add
32531     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32532     */
32533     
32534     get: function(layout_id) {
32535         if (typeof(this.groups[layout_id]) == 'undefined') {
32536             return false;
32537         }
32538         return this.groups[layout_id] ;
32539     }
32540     
32541     
32542     
32543 });
32544
32545  
32546
32547  /**
32548  *
32549  * This is based on 
32550  * http://masonry.desandro.com
32551  *
32552  * The idea is to render all the bricks based on vertical width...
32553  *
32554  * The original code extends 'outlayer' - we might need to use that....
32555  * 
32556  */
32557
32558
32559 /**
32560  * @class Roo.bootstrap.LayoutMasonryAuto
32561  * @extends Roo.bootstrap.Component
32562  * Bootstrap Layout Masonry class
32563  * 
32564  * @constructor
32565  * Create a new Element
32566  * @param {Object} config The config object
32567  */
32568
32569 Roo.bootstrap.LayoutMasonryAuto = function(config){
32570     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32571 };
32572
32573 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32574     
32575       /**
32576      * @cfg {Boolean} isFitWidth  - resize the width..
32577      */   
32578     isFitWidth : false,  // options..
32579     /**
32580      * @cfg {Boolean} isOriginLeft = left align?
32581      */   
32582     isOriginLeft : true,
32583     /**
32584      * @cfg {Boolean} isOriginTop = top align?
32585      */   
32586     isOriginTop : false,
32587     /**
32588      * @cfg {Boolean} isLayoutInstant = no animation?
32589      */   
32590     isLayoutInstant : false, // needed?
32591     /**
32592      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32593      */   
32594     isResizingContainer : true,
32595     /**
32596      * @cfg {Number} columnWidth  width of the columns 
32597      */   
32598     
32599     columnWidth : 0,
32600     
32601     /**
32602      * @cfg {Number} maxCols maximum number of columns
32603      */   
32604     
32605     maxCols: 0,
32606     /**
32607      * @cfg {Number} padHeight padding below box..
32608      */   
32609     
32610     padHeight : 10, 
32611     
32612     /**
32613      * @cfg {Boolean} isAutoInitial defalut true
32614      */   
32615     
32616     isAutoInitial : true, 
32617     
32618     // private?
32619     gutter : 0,
32620     
32621     containerWidth: 0,
32622     initialColumnWidth : 0,
32623     currentSize : null,
32624     
32625     colYs : null, // array.
32626     maxY : 0,
32627     padWidth: 10,
32628     
32629     
32630     tag: 'div',
32631     cls: '',
32632     bricks: null, //CompositeElement
32633     cols : 0, // array?
32634     // element : null, // wrapped now this.el
32635     _isLayoutInited : null, 
32636     
32637     
32638     getAutoCreate : function(){
32639         
32640         var cfg = {
32641             tag: this.tag,
32642             cls: 'blog-masonary-wrapper ' + this.cls,
32643             cn : {
32644                 cls : 'mas-boxes masonary'
32645             }
32646         };
32647         
32648         return cfg;
32649     },
32650     
32651     getChildContainer: function( )
32652     {
32653         if (this.boxesEl) {
32654             return this.boxesEl;
32655         }
32656         
32657         this.boxesEl = this.el.select('.mas-boxes').first();
32658         
32659         return this.boxesEl;
32660     },
32661     
32662     
32663     initEvents : function()
32664     {
32665         var _this = this;
32666         
32667         if(this.isAutoInitial){
32668             Roo.log('hook children rendered');
32669             this.on('childrenrendered', function() {
32670                 Roo.log('children rendered');
32671                 _this.initial();
32672             } ,this);
32673         }
32674         
32675     },
32676     
32677     initial : function()
32678     {
32679         this.reloadItems();
32680
32681         this.currentSize = this.el.getBox(true);
32682
32683         /// was window resize... - let's see if this works..
32684         Roo.EventManager.onWindowResize(this.resize, this); 
32685
32686         if(!this.isAutoInitial){
32687             this.layout();
32688             return;
32689         }
32690         
32691         this.layout.defer(500,this);
32692     },
32693     
32694     reloadItems: function()
32695     {
32696         this.bricks = this.el.select('.masonry-brick', true);
32697         
32698         this.bricks.each(function(b) {
32699             //Roo.log(b.getSize());
32700             if (!b.attr('originalwidth')) {
32701                 b.attr('originalwidth',  b.getSize().width);
32702             }
32703             
32704         });
32705         
32706         Roo.log(this.bricks.elements.length);
32707     },
32708     
32709     resize : function()
32710     {
32711         Roo.log('resize');
32712         var cs = this.el.getBox(true);
32713         
32714         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32715             Roo.log("no change in with or X");
32716             return;
32717         }
32718         this.currentSize = cs;
32719         this.layout();
32720     },
32721     
32722     layout : function()
32723     {
32724          Roo.log('layout');
32725         this._resetLayout();
32726         //this._manageStamps();
32727       
32728         // don't animate first layout
32729         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32730         this.layoutItems( isInstant );
32731       
32732         // flag for initalized
32733         this._isLayoutInited = true;
32734     },
32735     
32736     layoutItems : function( isInstant )
32737     {
32738         //var items = this._getItemsForLayout( this.items );
32739         // original code supports filtering layout items.. we just ignore it..
32740         
32741         this._layoutItems( this.bricks , isInstant );
32742       
32743         this._postLayout();
32744     },
32745     _layoutItems : function ( items , isInstant)
32746     {
32747        //this.fireEvent( 'layout', this, items );
32748     
32749
32750         if ( !items || !items.elements.length ) {
32751           // no items, emit event with empty array
32752             return;
32753         }
32754
32755         var queue = [];
32756         items.each(function(item) {
32757             Roo.log("layout item");
32758             Roo.log(item);
32759             // get x/y object from method
32760             var position = this._getItemLayoutPosition( item );
32761             // enqueue
32762             position.item = item;
32763             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32764             queue.push( position );
32765         }, this);
32766       
32767         this._processLayoutQueue( queue );
32768     },
32769     /** Sets position of item in DOM
32770     * @param {Element} item
32771     * @param {Number} x - horizontal position
32772     * @param {Number} y - vertical position
32773     * @param {Boolean} isInstant - disables transitions
32774     */
32775     _processLayoutQueue : function( queue )
32776     {
32777         for ( var i=0, len = queue.length; i < len; i++ ) {
32778             var obj = queue[i];
32779             obj.item.position('absolute');
32780             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32781         }
32782     },
32783       
32784     
32785     /**
32786     * Any logic you want to do after each layout,
32787     * i.e. size the container
32788     */
32789     _postLayout : function()
32790     {
32791         this.resizeContainer();
32792     },
32793     
32794     resizeContainer : function()
32795     {
32796         if ( !this.isResizingContainer ) {
32797             return;
32798         }
32799         var size = this._getContainerSize();
32800         if ( size ) {
32801             this.el.setSize(size.width,size.height);
32802             this.boxesEl.setSize(size.width,size.height);
32803         }
32804     },
32805     
32806     
32807     
32808     _resetLayout : function()
32809     {
32810         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32811         this.colWidth = this.el.getWidth();
32812         //this.gutter = this.el.getWidth(); 
32813         
32814         this.measureColumns();
32815
32816         // reset column Y
32817         var i = this.cols;
32818         this.colYs = [];
32819         while (i--) {
32820             this.colYs.push( 0 );
32821         }
32822     
32823         this.maxY = 0;
32824     },
32825
32826     measureColumns : function()
32827     {
32828         this.getContainerWidth();
32829       // if columnWidth is 0, default to outerWidth of first item
32830         if ( !this.columnWidth ) {
32831             var firstItem = this.bricks.first();
32832             Roo.log(firstItem);
32833             this.columnWidth  = this.containerWidth;
32834             if (firstItem && firstItem.attr('originalwidth') ) {
32835                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32836             }
32837             // columnWidth fall back to item of first element
32838             Roo.log("set column width?");
32839                         this.initialColumnWidth = this.columnWidth  ;
32840
32841             // if first elem has no width, default to size of container
32842             
32843         }
32844         
32845         
32846         if (this.initialColumnWidth) {
32847             this.columnWidth = this.initialColumnWidth;
32848         }
32849         
32850         
32851             
32852         // column width is fixed at the top - however if container width get's smaller we should
32853         // reduce it...
32854         
32855         // this bit calcs how man columns..
32856             
32857         var columnWidth = this.columnWidth += this.gutter;
32858       
32859         // calculate columns
32860         var containerWidth = this.containerWidth + this.gutter;
32861         
32862         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32863         // fix rounding errors, typically with gutters
32864         var excess = columnWidth - containerWidth % columnWidth;
32865         
32866         
32867         // if overshoot is less than a pixel, round up, otherwise floor it
32868         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32869         cols = Math[ mathMethod ]( cols );
32870         this.cols = Math.max( cols, 1 );
32871         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32872         
32873          // padding positioning..
32874         var totalColWidth = this.cols * this.columnWidth;
32875         var padavail = this.containerWidth - totalColWidth;
32876         // so for 2 columns - we need 3 'pads'
32877         
32878         var padNeeded = (1+this.cols) * this.padWidth;
32879         
32880         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32881         
32882         this.columnWidth += padExtra
32883         //this.padWidth = Math.floor(padavail /  ( this.cols));
32884         
32885         // adjust colum width so that padding is fixed??
32886         
32887         // we have 3 columns ... total = width * 3
32888         // we have X left over... that should be used by 
32889         
32890         //if (this.expandC) {
32891             
32892         //}
32893         
32894         
32895         
32896     },
32897     
32898     getContainerWidth : function()
32899     {
32900        /* // container is parent if fit width
32901         var container = this.isFitWidth ? this.element.parentNode : this.element;
32902         // check that this.size and size are there
32903         // IE8 triggers resize on body size change, so they might not be
32904         
32905         var size = getSize( container );  //FIXME
32906         this.containerWidth = size && size.innerWidth; //FIXME
32907         */
32908          
32909         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32910         
32911     },
32912     
32913     _getItemLayoutPosition : function( item )  // what is item?
32914     {
32915         // we resize the item to our columnWidth..
32916       
32917         item.setWidth(this.columnWidth);
32918         item.autoBoxAdjust  = false;
32919         
32920         var sz = item.getSize();
32921  
32922         // how many columns does this brick span
32923         var remainder = this.containerWidth % this.columnWidth;
32924         
32925         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32926         // round if off by 1 pixel, otherwise use ceil
32927         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32928         colSpan = Math.min( colSpan, this.cols );
32929         
32930         // normally this should be '1' as we dont' currently allow multi width columns..
32931         
32932         var colGroup = this._getColGroup( colSpan );
32933         // get the minimum Y value from the columns
32934         var minimumY = Math.min.apply( Math, colGroup );
32935         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32936         
32937         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32938          
32939         // position the brick
32940         var position = {
32941             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32942             y: this.currentSize.y + minimumY + this.padHeight
32943         };
32944         
32945         Roo.log(position);
32946         // apply setHeight to necessary columns
32947         var setHeight = minimumY + sz.height + this.padHeight;
32948         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32949         
32950         var setSpan = this.cols + 1 - colGroup.length;
32951         for ( var i = 0; i < setSpan; i++ ) {
32952           this.colYs[ shortColIndex + i ] = setHeight ;
32953         }
32954       
32955         return position;
32956     },
32957     
32958     /**
32959      * @param {Number} colSpan - number of columns the element spans
32960      * @returns {Array} colGroup
32961      */
32962     _getColGroup : function( colSpan )
32963     {
32964         if ( colSpan < 2 ) {
32965           // if brick spans only one column, use all the column Ys
32966           return this.colYs;
32967         }
32968       
32969         var colGroup = [];
32970         // how many different places could this brick fit horizontally
32971         var groupCount = this.cols + 1 - colSpan;
32972         // for each group potential horizontal position
32973         for ( var i = 0; i < groupCount; i++ ) {
32974           // make an array of colY values for that one group
32975           var groupColYs = this.colYs.slice( i, i + colSpan );
32976           // and get the max value of the array
32977           colGroup[i] = Math.max.apply( Math, groupColYs );
32978         }
32979         return colGroup;
32980     },
32981     /*
32982     _manageStamp : function( stamp )
32983     {
32984         var stampSize =  stamp.getSize();
32985         var offset = stamp.getBox();
32986         // get the columns that this stamp affects
32987         var firstX = this.isOriginLeft ? offset.x : offset.right;
32988         var lastX = firstX + stampSize.width;
32989         var firstCol = Math.floor( firstX / this.columnWidth );
32990         firstCol = Math.max( 0, firstCol );
32991         
32992         var lastCol = Math.floor( lastX / this.columnWidth );
32993         // lastCol should not go over if multiple of columnWidth #425
32994         lastCol -= lastX % this.columnWidth ? 0 : 1;
32995         lastCol = Math.min( this.cols - 1, lastCol );
32996         
32997         // set colYs to bottom of the stamp
32998         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32999             stampSize.height;
33000             
33001         for ( var i = firstCol; i <= lastCol; i++ ) {
33002           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33003         }
33004     },
33005     */
33006     
33007     _getContainerSize : function()
33008     {
33009         this.maxY = Math.max.apply( Math, this.colYs );
33010         var size = {
33011             height: this.maxY
33012         };
33013       
33014         if ( this.isFitWidth ) {
33015             size.width = this._getContainerFitWidth();
33016         }
33017       
33018         return size;
33019     },
33020     
33021     _getContainerFitWidth : function()
33022     {
33023         var unusedCols = 0;
33024         // count unused columns
33025         var i = this.cols;
33026         while ( --i ) {
33027           if ( this.colYs[i] !== 0 ) {
33028             break;
33029           }
33030           unusedCols++;
33031         }
33032         // fit container to columns that have been used
33033         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33034     },
33035     
33036     needsResizeLayout : function()
33037     {
33038         var previousWidth = this.containerWidth;
33039         this.getContainerWidth();
33040         return previousWidth !== this.containerWidth;
33041     }
33042  
33043 });
33044
33045  
33046
33047  /*
33048  * - LGPL
33049  *
33050  * element
33051  * 
33052  */
33053
33054 /**
33055  * @class Roo.bootstrap.MasonryBrick
33056  * @extends Roo.bootstrap.Component
33057  * Bootstrap MasonryBrick class
33058  * 
33059  * @constructor
33060  * Create a new MasonryBrick
33061  * @param {Object} config The config object
33062  */
33063
33064 Roo.bootstrap.MasonryBrick = function(config){
33065     
33066     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33067     
33068     Roo.bootstrap.MasonryBrick.register(this);
33069     
33070     this.addEvents({
33071         // raw events
33072         /**
33073          * @event click
33074          * When a MasonryBrick is clcik
33075          * @param {Roo.bootstrap.MasonryBrick} this
33076          * @param {Roo.EventObject} e
33077          */
33078         "click" : true
33079     });
33080 };
33081
33082 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33083     
33084     /**
33085      * @cfg {String} title
33086      */   
33087     title : '',
33088     /**
33089      * @cfg {String} html
33090      */   
33091     html : '',
33092     /**
33093      * @cfg {String} bgimage
33094      */   
33095     bgimage : '',
33096     /**
33097      * @cfg {String} videourl
33098      */   
33099     videourl : '',
33100     /**
33101      * @cfg {String} cls
33102      */   
33103     cls : '',
33104     /**
33105      * @cfg {String} href
33106      */   
33107     href : '',
33108     /**
33109      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33110      */   
33111     size : 'xs',
33112     
33113     /**
33114      * @cfg {String} placetitle (center|bottom)
33115      */   
33116     placetitle : '',
33117     
33118     /**
33119      * @cfg {Boolean} isFitContainer defalut true
33120      */   
33121     isFitContainer : true, 
33122     
33123     /**
33124      * @cfg {Boolean} preventDefault defalut false
33125      */   
33126     preventDefault : false, 
33127     
33128     /**
33129      * @cfg {Boolean} inverse defalut false
33130      */   
33131     maskInverse : false, 
33132     
33133     getAutoCreate : function()
33134     {
33135         if(!this.isFitContainer){
33136             return this.getSplitAutoCreate();
33137         }
33138         
33139         var cls = 'masonry-brick masonry-brick-full';
33140         
33141         if(this.href.length){
33142             cls += ' masonry-brick-link';
33143         }
33144         
33145         if(this.bgimage.length){
33146             cls += ' masonry-brick-image';
33147         }
33148         
33149         if(this.maskInverse){
33150             cls += ' mask-inverse';
33151         }
33152         
33153         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33154             cls += ' enable-mask';
33155         }
33156         
33157         if(this.size){
33158             cls += ' masonry-' + this.size + '-brick';
33159         }
33160         
33161         if(this.placetitle.length){
33162             
33163             switch (this.placetitle) {
33164                 case 'center' :
33165                     cls += ' masonry-center-title';
33166                     break;
33167                 case 'bottom' :
33168                     cls += ' masonry-bottom-title';
33169                     break;
33170                 default:
33171                     break;
33172             }
33173             
33174         } else {
33175             if(!this.html.length && !this.bgimage.length){
33176                 cls += ' masonry-center-title';
33177             }
33178
33179             if(!this.html.length && this.bgimage.length){
33180                 cls += ' masonry-bottom-title';
33181             }
33182         }
33183         
33184         if(this.cls){
33185             cls += ' ' + this.cls;
33186         }
33187         
33188         var cfg = {
33189             tag: (this.href.length) ? 'a' : 'div',
33190             cls: cls,
33191             cn: [
33192                 {
33193                     tag: 'div',
33194                     cls: 'masonry-brick-mask'
33195                 },
33196                 {
33197                     tag: 'div',
33198                     cls: 'masonry-brick-paragraph',
33199                     cn: []
33200                 }
33201             ]
33202         };
33203         
33204         if(this.href.length){
33205             cfg.href = this.href;
33206         }
33207         
33208         var cn = cfg.cn[1].cn;
33209         
33210         if(this.title.length){
33211             cn.push({
33212                 tag: 'h4',
33213                 cls: 'masonry-brick-title',
33214                 html: this.title
33215             });
33216         }
33217         
33218         if(this.html.length){
33219             cn.push({
33220                 tag: 'p',
33221                 cls: 'masonry-brick-text',
33222                 html: this.html
33223             });
33224         }
33225         
33226         if (!this.title.length && !this.html.length) {
33227             cfg.cn[1].cls += ' hide';
33228         }
33229         
33230         if(this.bgimage.length){
33231             cfg.cn.push({
33232                 tag: 'img',
33233                 cls: 'masonry-brick-image-view',
33234                 src: this.bgimage
33235             });
33236         }
33237         
33238         if(this.videourl.length){
33239             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33240             // youtube support only?
33241             cfg.cn.push({
33242                 tag: 'iframe',
33243                 cls: 'masonry-brick-image-view',
33244                 src: vurl,
33245                 frameborder : 0,
33246                 allowfullscreen : true
33247             });
33248         }
33249         
33250         return cfg;
33251         
33252     },
33253     
33254     getSplitAutoCreate : function()
33255     {
33256         var cls = 'masonry-brick masonry-brick-split';
33257         
33258         if(this.href.length){
33259             cls += ' masonry-brick-link';
33260         }
33261         
33262         if(this.bgimage.length){
33263             cls += ' masonry-brick-image';
33264         }
33265         
33266         if(this.size){
33267             cls += ' masonry-' + this.size + '-brick';
33268         }
33269         
33270         switch (this.placetitle) {
33271             case 'center' :
33272                 cls += ' masonry-center-title';
33273                 break;
33274             case 'bottom' :
33275                 cls += ' masonry-bottom-title';
33276                 break;
33277             default:
33278                 if(!this.bgimage.length){
33279                     cls += ' masonry-center-title';
33280                 }
33281
33282                 if(this.bgimage.length){
33283                     cls += ' masonry-bottom-title';
33284                 }
33285                 break;
33286         }
33287         
33288         if(this.cls){
33289             cls += ' ' + this.cls;
33290         }
33291         
33292         var cfg = {
33293             tag: (this.href.length) ? 'a' : 'div',
33294             cls: cls,
33295             cn: [
33296                 {
33297                     tag: 'div',
33298                     cls: 'masonry-brick-split-head',
33299                     cn: [
33300                         {
33301                             tag: 'div',
33302                             cls: 'masonry-brick-paragraph',
33303                             cn: []
33304                         }
33305                     ]
33306                 },
33307                 {
33308                     tag: 'div',
33309                     cls: 'masonry-brick-split-body',
33310                     cn: []
33311                 }
33312             ]
33313         };
33314         
33315         if(this.href.length){
33316             cfg.href = this.href;
33317         }
33318         
33319         if(this.title.length){
33320             cfg.cn[0].cn[0].cn.push({
33321                 tag: 'h4',
33322                 cls: 'masonry-brick-title',
33323                 html: this.title
33324             });
33325         }
33326         
33327         if(this.html.length){
33328             cfg.cn[1].cn.push({
33329                 tag: 'p',
33330                 cls: 'masonry-brick-text',
33331                 html: this.html
33332             });
33333         }
33334
33335         if(this.bgimage.length){
33336             cfg.cn[0].cn.push({
33337                 tag: 'img',
33338                 cls: 'masonry-brick-image-view',
33339                 src: this.bgimage
33340             });
33341         }
33342         
33343         if(this.videourl.length){
33344             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33345             // youtube support only?
33346             cfg.cn[0].cn.cn.push({
33347                 tag: 'iframe',
33348                 cls: 'masonry-brick-image-view',
33349                 src: vurl,
33350                 frameborder : 0,
33351                 allowfullscreen : true
33352             });
33353         }
33354         
33355         return cfg;
33356     },
33357     
33358     initEvents: function() 
33359     {
33360         switch (this.size) {
33361             case 'xs' :
33362                 this.x = 1;
33363                 this.y = 1;
33364                 break;
33365             case 'sm' :
33366                 this.x = 2;
33367                 this.y = 2;
33368                 break;
33369             case 'md' :
33370             case 'md-left' :
33371             case 'md-right' :
33372                 this.x = 3;
33373                 this.y = 3;
33374                 break;
33375             case 'tall' :
33376                 this.x = 2;
33377                 this.y = 3;
33378                 break;
33379             case 'wide' :
33380                 this.x = 3;
33381                 this.y = 2;
33382                 break;
33383             case 'wide-thin' :
33384                 this.x = 3;
33385                 this.y = 1;
33386                 break;
33387                         
33388             default :
33389                 break;
33390         }
33391         
33392         if(Roo.isTouch){
33393             this.el.on('touchstart', this.onTouchStart, this);
33394             this.el.on('touchmove', this.onTouchMove, this);
33395             this.el.on('touchend', this.onTouchEnd, this);
33396             this.el.on('contextmenu', this.onContextMenu, this);
33397         } else {
33398             this.el.on('mouseenter'  ,this.enter, this);
33399             this.el.on('mouseleave', this.leave, this);
33400             this.el.on('click', this.onClick, this);
33401         }
33402         
33403         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33404             this.parent().bricks.push(this);   
33405         }
33406         
33407     },
33408     
33409     onClick: function(e, el)
33410     {
33411         var time = this.endTimer - this.startTimer;
33412         // Roo.log(e.preventDefault());
33413         if(Roo.isTouch){
33414             if(time > 1000){
33415                 e.preventDefault();
33416                 return;
33417             }
33418         }
33419         
33420         if(!this.preventDefault){
33421             return;
33422         }
33423         
33424         e.preventDefault();
33425         
33426         if (this.activeClass != '') {
33427             this.selectBrick();
33428         }
33429         
33430         this.fireEvent('click', this, e);
33431     },
33432     
33433     enter: function(e, el)
33434     {
33435         e.preventDefault();
33436         
33437         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33438             return;
33439         }
33440         
33441         if(this.bgimage.length && this.html.length){
33442             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33443         }
33444     },
33445     
33446     leave: function(e, el)
33447     {
33448         e.preventDefault();
33449         
33450         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33451             return;
33452         }
33453         
33454         if(this.bgimage.length && this.html.length){
33455             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33456         }
33457     },
33458     
33459     onTouchStart: function(e, el)
33460     {
33461 //        e.preventDefault();
33462         
33463         this.touchmoved = false;
33464         
33465         if(!this.isFitContainer){
33466             return;
33467         }
33468         
33469         if(!this.bgimage.length || !this.html.length){
33470             return;
33471         }
33472         
33473         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33474         
33475         this.timer = new Date().getTime();
33476         
33477     },
33478     
33479     onTouchMove: function(e, el)
33480     {
33481         this.touchmoved = true;
33482     },
33483     
33484     onContextMenu : function(e,el)
33485     {
33486         e.preventDefault();
33487         e.stopPropagation();
33488         return false;
33489     },
33490     
33491     onTouchEnd: function(e, el)
33492     {
33493 //        e.preventDefault();
33494         
33495         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33496         
33497             this.leave(e,el);
33498             
33499             return;
33500         }
33501         
33502         if(!this.bgimage.length || !this.html.length){
33503             
33504             if(this.href.length){
33505                 window.location.href = this.href;
33506             }
33507             
33508             return;
33509         }
33510         
33511         if(!this.isFitContainer){
33512             return;
33513         }
33514         
33515         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33516         
33517         window.location.href = this.href;
33518     },
33519     
33520     //selection on single brick only
33521     selectBrick : function() {
33522         
33523         if (!this.parentId) {
33524             return;
33525         }
33526         
33527         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33528         var index = m.selectedBrick.indexOf(this.id);
33529         
33530         if ( index > -1) {
33531             m.selectedBrick.splice(index,1);
33532             this.el.removeClass(this.activeClass);
33533             return;
33534         }
33535         
33536         for(var i = 0; i < m.selectedBrick.length; i++) {
33537             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33538             b.el.removeClass(b.activeClass);
33539         }
33540         
33541         m.selectedBrick = [];
33542         
33543         m.selectedBrick.push(this.id);
33544         this.el.addClass(this.activeClass);
33545         return;
33546     },
33547     
33548     isSelected : function(){
33549         return this.el.hasClass(this.activeClass);
33550         
33551     }
33552 });
33553
33554 Roo.apply(Roo.bootstrap.MasonryBrick, {
33555     
33556     //groups: {},
33557     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33558      /**
33559     * register a Masonry Brick
33560     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33561     */
33562     
33563     register : function(brick)
33564     {
33565         //this.groups[brick.id] = brick;
33566         this.groups.add(brick.id, brick);
33567     },
33568     /**
33569     * fetch a  masonry brick based on the masonry brick ID
33570     * @param {string} the masonry brick to add
33571     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33572     */
33573     
33574     get: function(brick_id) 
33575     {
33576         // if (typeof(this.groups[brick_id]) == 'undefined') {
33577         //     return false;
33578         // }
33579         // return this.groups[brick_id] ;
33580         
33581         if(this.groups.key(brick_id)) {
33582             return this.groups.key(brick_id);
33583         }
33584         
33585         return false;
33586     }
33587     
33588     
33589     
33590 });
33591
33592  /*
33593  * - LGPL
33594  *
33595  * element
33596  * 
33597  */
33598
33599 /**
33600  * @class Roo.bootstrap.Brick
33601  * @extends Roo.bootstrap.Component
33602  * Bootstrap Brick class
33603  * 
33604  * @constructor
33605  * Create a new Brick
33606  * @param {Object} config The config object
33607  */
33608
33609 Roo.bootstrap.Brick = function(config){
33610     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33611     
33612     this.addEvents({
33613         // raw events
33614         /**
33615          * @event click
33616          * When a Brick is click
33617          * @param {Roo.bootstrap.Brick} this
33618          * @param {Roo.EventObject} e
33619          */
33620         "click" : true
33621     });
33622 };
33623
33624 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33625     
33626     /**
33627      * @cfg {String} title
33628      */   
33629     title : '',
33630     /**
33631      * @cfg {String} html
33632      */   
33633     html : '',
33634     /**
33635      * @cfg {String} bgimage
33636      */   
33637     bgimage : '',
33638     /**
33639      * @cfg {String} cls
33640      */   
33641     cls : '',
33642     /**
33643      * @cfg {String} href
33644      */   
33645     href : '',
33646     /**
33647      * @cfg {String} video
33648      */   
33649     video : '',
33650     /**
33651      * @cfg {Boolean} square
33652      */   
33653     square : true,
33654     
33655     getAutoCreate : function()
33656     {
33657         var cls = 'roo-brick';
33658         
33659         if(this.href.length){
33660             cls += ' roo-brick-link';
33661         }
33662         
33663         if(this.bgimage.length){
33664             cls += ' roo-brick-image';
33665         }
33666         
33667         if(!this.html.length && !this.bgimage.length){
33668             cls += ' roo-brick-center-title';
33669         }
33670         
33671         if(!this.html.length && this.bgimage.length){
33672             cls += ' roo-brick-bottom-title';
33673         }
33674         
33675         if(this.cls){
33676             cls += ' ' + this.cls;
33677         }
33678         
33679         var cfg = {
33680             tag: (this.href.length) ? 'a' : 'div',
33681             cls: cls,
33682             cn: [
33683                 {
33684                     tag: 'div',
33685                     cls: 'roo-brick-paragraph',
33686                     cn: []
33687                 }
33688             ]
33689         };
33690         
33691         if(this.href.length){
33692             cfg.href = this.href;
33693         }
33694         
33695         var cn = cfg.cn[0].cn;
33696         
33697         if(this.title.length){
33698             cn.push({
33699                 tag: 'h4',
33700                 cls: 'roo-brick-title',
33701                 html: this.title
33702             });
33703         }
33704         
33705         if(this.html.length){
33706             cn.push({
33707                 tag: 'p',
33708                 cls: 'roo-brick-text',
33709                 html: this.html
33710             });
33711         } else {
33712             cn.cls += ' hide';
33713         }
33714         
33715         if(this.bgimage.length){
33716             cfg.cn.push({
33717                 tag: 'img',
33718                 cls: 'roo-brick-image-view',
33719                 src: this.bgimage
33720             });
33721         }
33722         
33723         return cfg;
33724     },
33725     
33726     initEvents: function() 
33727     {
33728         if(this.title.length || this.html.length){
33729             this.el.on('mouseenter'  ,this.enter, this);
33730             this.el.on('mouseleave', this.leave, this);
33731         }
33732         
33733         Roo.EventManager.onWindowResize(this.resize, this); 
33734         
33735         if(this.bgimage.length){
33736             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33737             this.imageEl.on('load', this.onImageLoad, this);
33738             return;
33739         }
33740         
33741         this.resize();
33742     },
33743     
33744     onImageLoad : function()
33745     {
33746         this.resize();
33747     },
33748     
33749     resize : function()
33750     {
33751         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33752         
33753         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33754         
33755         if(this.bgimage.length){
33756             var image = this.el.select('.roo-brick-image-view', true).first();
33757             
33758             image.setWidth(paragraph.getWidth());
33759             
33760             if(this.square){
33761                 image.setHeight(paragraph.getWidth());
33762             }
33763             
33764             this.el.setHeight(image.getHeight());
33765             paragraph.setHeight(image.getHeight());
33766             
33767         }
33768         
33769     },
33770     
33771     enter: function(e, el)
33772     {
33773         e.preventDefault();
33774         
33775         if(this.bgimage.length){
33776             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33777             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33778         }
33779     },
33780     
33781     leave: function(e, el)
33782     {
33783         e.preventDefault();
33784         
33785         if(this.bgimage.length){
33786             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33787             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33788         }
33789     }
33790     
33791 });
33792
33793  
33794
33795  /*
33796  * - LGPL
33797  *
33798  * Number field 
33799  */
33800
33801 /**
33802  * @class Roo.bootstrap.NumberField
33803  * @extends Roo.bootstrap.Input
33804  * Bootstrap NumberField class
33805  * 
33806  * 
33807  * 
33808  * 
33809  * @constructor
33810  * Create a new NumberField
33811  * @param {Object} config The config object
33812  */
33813
33814 Roo.bootstrap.NumberField = function(config){
33815     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33816 };
33817
33818 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33819     
33820     /**
33821      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33822      */
33823     allowDecimals : true,
33824     /**
33825      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33826      */
33827     decimalSeparator : ".",
33828     /**
33829      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33830      */
33831     decimalPrecision : 2,
33832     /**
33833      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33834      */
33835     allowNegative : true,
33836     
33837     /**
33838      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33839      */
33840     allowZero: true,
33841     /**
33842      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33843      */
33844     minValue : Number.NEGATIVE_INFINITY,
33845     /**
33846      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33847      */
33848     maxValue : Number.MAX_VALUE,
33849     /**
33850      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33851      */
33852     minText : "The minimum value for this field is {0}",
33853     /**
33854      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33855      */
33856     maxText : "The maximum value for this field is {0}",
33857     /**
33858      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33859      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33860      */
33861     nanText : "{0} is not a valid number",
33862     /**
33863      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33864      */
33865     thousandsDelimiter : false,
33866     /**
33867      * @cfg {String} valueAlign alignment of value
33868      */
33869     valueAlign : "left",
33870
33871     getAutoCreate : function()
33872     {
33873         var hiddenInput = {
33874             tag: 'input',
33875             type: 'hidden',
33876             id: Roo.id(),
33877             cls: 'hidden-number-input'
33878         };
33879         
33880         if (this.name) {
33881             hiddenInput.name = this.name;
33882         }
33883         
33884         this.name = '';
33885         
33886         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33887         
33888         this.name = hiddenInput.name;
33889         
33890         if(cfg.cn.length > 0) {
33891             cfg.cn.push(hiddenInput);
33892         }
33893         
33894         return cfg;
33895     },
33896
33897     // private
33898     initEvents : function()
33899     {   
33900         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33901         
33902         var allowed = "0123456789";
33903         
33904         if(this.allowDecimals){
33905             allowed += this.decimalSeparator;
33906         }
33907         
33908         if(this.allowNegative){
33909             allowed += "-";
33910         }
33911         
33912         if(this.thousandsDelimiter) {
33913             allowed += ",";
33914         }
33915         
33916         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33917         
33918         var keyPress = function(e){
33919             
33920             var k = e.getKey();
33921             
33922             var c = e.getCharCode();
33923             
33924             if(
33925                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33926                     allowed.indexOf(String.fromCharCode(c)) === -1
33927             ){
33928                 e.stopEvent();
33929                 return;
33930             }
33931             
33932             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33933                 return;
33934             }
33935             
33936             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33937                 e.stopEvent();
33938             }
33939         };
33940         
33941         this.el.on("keypress", keyPress, this);
33942     },
33943     
33944     validateValue : function(value)
33945     {
33946         
33947         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33948             return false;
33949         }
33950         
33951         var num = this.parseValue(value);
33952         
33953         if(isNaN(num)){
33954             this.markInvalid(String.format(this.nanText, value));
33955             return false;
33956         }
33957         
33958         if(num < this.minValue){
33959             this.markInvalid(String.format(this.minText, this.minValue));
33960             return false;
33961         }
33962         
33963         if(num > this.maxValue){
33964             this.markInvalid(String.format(this.maxText, this.maxValue));
33965             return false;
33966         }
33967         
33968         return true;
33969     },
33970
33971     getValue : function()
33972     {
33973         var v = this.hiddenEl().getValue();
33974         
33975         return this.fixPrecision(this.parseValue(v));
33976     },
33977
33978     parseValue : function(value)
33979     {
33980         if(this.thousandsDelimiter) {
33981             value += "";
33982             r = new RegExp(",", "g");
33983             value = value.replace(r, "");
33984         }
33985         
33986         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33987         return isNaN(value) ? '' : value;
33988     },
33989
33990     fixPrecision : function(value)
33991     {
33992         if(this.thousandsDelimiter) {
33993             value += "";
33994             r = new RegExp(",", "g");
33995             value = value.replace(r, "");
33996         }
33997         
33998         var nan = isNaN(value);
33999         
34000         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34001             return nan ? '' : value;
34002         }
34003         return parseFloat(value).toFixed(this.decimalPrecision);
34004     },
34005
34006     setValue : function(v)
34007     {
34008         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34009         
34010         this.value = v;
34011         
34012         if(this.rendered){
34013             
34014             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34015             
34016             this.inputEl().dom.value = (v == '') ? '' :
34017                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34018             
34019             if(!this.allowZero && v === '0') {
34020                 this.hiddenEl().dom.value = '';
34021                 this.inputEl().dom.value = '';
34022             }
34023             
34024             this.validate();
34025         }
34026     },
34027
34028     decimalPrecisionFcn : function(v)
34029     {
34030         return Math.floor(v);
34031     },
34032
34033     beforeBlur : function()
34034     {
34035         var v = this.parseValue(this.getRawValue());
34036         
34037         if(v || v === 0 || v === ''){
34038             this.setValue(v);
34039         }
34040     },
34041     
34042     hiddenEl : function()
34043     {
34044         return this.el.select('input.hidden-number-input',true).first();
34045     }
34046     
34047 });
34048
34049  
34050
34051 /*
34052 * Licence: LGPL
34053 */
34054
34055 /**
34056  * @class Roo.bootstrap.DocumentSlider
34057  * @extends Roo.bootstrap.Component
34058  * Bootstrap DocumentSlider class
34059  * 
34060  * @constructor
34061  * Create a new DocumentViewer
34062  * @param {Object} config The config object
34063  */
34064
34065 Roo.bootstrap.DocumentSlider = function(config){
34066     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34067     
34068     this.files = [];
34069     
34070     this.addEvents({
34071         /**
34072          * @event initial
34073          * Fire after initEvent
34074          * @param {Roo.bootstrap.DocumentSlider} this
34075          */
34076         "initial" : true,
34077         /**
34078          * @event update
34079          * Fire after update
34080          * @param {Roo.bootstrap.DocumentSlider} this
34081          */
34082         "update" : true,
34083         /**
34084          * @event click
34085          * Fire after click
34086          * @param {Roo.bootstrap.DocumentSlider} this
34087          */
34088         "click" : true
34089     });
34090 };
34091
34092 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34093     
34094     files : false,
34095     
34096     indicator : 0,
34097     
34098     getAutoCreate : function()
34099     {
34100         var cfg = {
34101             tag : 'div',
34102             cls : 'roo-document-slider',
34103             cn : [
34104                 {
34105                     tag : 'div',
34106                     cls : 'roo-document-slider-header',
34107                     cn : [
34108                         {
34109                             tag : 'div',
34110                             cls : 'roo-document-slider-header-title'
34111                         }
34112                     ]
34113                 },
34114                 {
34115                     tag : 'div',
34116                     cls : 'roo-document-slider-body',
34117                     cn : [
34118                         {
34119                             tag : 'div',
34120                             cls : 'roo-document-slider-prev',
34121                             cn : [
34122                                 {
34123                                     tag : 'i',
34124                                     cls : 'fa fa-chevron-left'
34125                                 }
34126                             ]
34127                         },
34128                         {
34129                             tag : 'div',
34130                             cls : 'roo-document-slider-thumb',
34131                             cn : [
34132                                 {
34133                                     tag : 'img',
34134                                     cls : 'roo-document-slider-image'
34135                                 }
34136                             ]
34137                         },
34138                         {
34139                             tag : 'div',
34140                             cls : 'roo-document-slider-next',
34141                             cn : [
34142                                 {
34143                                     tag : 'i',
34144                                     cls : 'fa fa-chevron-right'
34145                                 }
34146                             ]
34147                         }
34148                     ]
34149                 }
34150             ]
34151         };
34152         
34153         return cfg;
34154     },
34155     
34156     initEvents : function()
34157     {
34158         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34159         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34160         
34161         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34162         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34163         
34164         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34165         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34166         
34167         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34168         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34169         
34170         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34171         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34172         
34173         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34174         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34175         
34176         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34177         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34178         
34179         this.thumbEl.on('click', this.onClick, this);
34180         
34181         this.prevIndicator.on('click', this.prev, this);
34182         
34183         this.nextIndicator.on('click', this.next, this);
34184         
34185     },
34186     
34187     initial : function()
34188     {
34189         if(this.files.length){
34190             this.indicator = 1;
34191             this.update()
34192         }
34193         
34194         this.fireEvent('initial', this);
34195     },
34196     
34197     update : function()
34198     {
34199         this.imageEl.attr('src', this.files[this.indicator - 1]);
34200         
34201         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34202         
34203         this.prevIndicator.show();
34204         
34205         if(this.indicator == 1){
34206             this.prevIndicator.hide();
34207         }
34208         
34209         this.nextIndicator.show();
34210         
34211         if(this.indicator == this.files.length){
34212             this.nextIndicator.hide();
34213         }
34214         
34215         this.thumbEl.scrollTo('top');
34216         
34217         this.fireEvent('update', this);
34218     },
34219     
34220     onClick : function(e)
34221     {
34222         e.preventDefault();
34223         
34224         this.fireEvent('click', this);
34225     },
34226     
34227     prev : function(e)
34228     {
34229         e.preventDefault();
34230         
34231         this.indicator = Math.max(1, this.indicator - 1);
34232         
34233         this.update();
34234     },
34235     
34236     next : function(e)
34237     {
34238         e.preventDefault();
34239         
34240         this.indicator = Math.min(this.files.length, this.indicator + 1);
34241         
34242         this.update();
34243     }
34244 });
34245 /*
34246  * - LGPL
34247  *
34248  * RadioSet
34249  *
34250  *
34251  */
34252
34253 /**
34254  * @class Roo.bootstrap.RadioSet
34255  * @extends Roo.bootstrap.Input
34256  * Bootstrap RadioSet class
34257  * @cfg {String} indicatorpos (left|right) default left
34258  * @cfg {Boolean} inline (true|false) inline the element (default true)
34259  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34260  * @constructor
34261  * Create a new RadioSet
34262  * @param {Object} config The config object
34263  */
34264
34265 Roo.bootstrap.RadioSet = function(config){
34266     
34267     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34268     
34269     this.radioes = [];
34270     
34271     Roo.bootstrap.RadioSet.register(this);
34272     
34273     this.addEvents({
34274         /**
34275         * @event check
34276         * Fires when the element is checked or unchecked.
34277         * @param {Roo.bootstrap.RadioSet} this This radio
34278         * @param {Roo.bootstrap.Radio} item The checked item
34279         */
34280        check : true,
34281        /**
34282         * @event click
34283         * Fires when the element is click.
34284         * @param {Roo.bootstrap.RadioSet} this This radio set
34285         * @param {Roo.bootstrap.Radio} item The checked item
34286         * @param {Roo.EventObject} e The event object
34287         */
34288        click : true
34289     });
34290     
34291 };
34292
34293 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34294
34295     radioes : false,
34296     
34297     inline : true,
34298     
34299     weight : '',
34300     
34301     indicatorpos : 'left',
34302     
34303     getAutoCreate : function()
34304     {
34305         var label = {
34306             tag : 'label',
34307             cls : 'roo-radio-set-label',
34308             cn : [
34309                 {
34310                     tag : 'span',
34311                     html : this.fieldLabel
34312                 }
34313             ]
34314         };
34315         if (Roo.bootstrap.version == 3) {
34316             
34317             
34318             if(this.indicatorpos == 'left'){
34319                 label.cn.unshift({
34320                     tag : 'i',
34321                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34322                     tooltip : 'This field is required'
34323                 });
34324             } else {
34325                 label.cn.push({
34326                     tag : 'i',
34327                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34328                     tooltip : 'This field is required'
34329                 });
34330             }
34331         }
34332         var items = {
34333             tag : 'div',
34334             cls : 'roo-radio-set-items'
34335         };
34336         
34337         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34338         
34339         if (align === 'left' && this.fieldLabel.length) {
34340             
34341             items = {
34342                 cls : "roo-radio-set-right", 
34343                 cn: [
34344                     items
34345                 ]
34346             };
34347             
34348             if(this.labelWidth > 12){
34349                 label.style = "width: " + this.labelWidth + 'px';
34350             }
34351             
34352             if(this.labelWidth < 13 && this.labelmd == 0){
34353                 this.labelmd = this.labelWidth;
34354             }
34355             
34356             if(this.labellg > 0){
34357                 label.cls += ' col-lg-' + this.labellg;
34358                 items.cls += ' col-lg-' + (12 - this.labellg);
34359             }
34360             
34361             if(this.labelmd > 0){
34362                 label.cls += ' col-md-' + this.labelmd;
34363                 items.cls += ' col-md-' + (12 - this.labelmd);
34364             }
34365             
34366             if(this.labelsm > 0){
34367                 label.cls += ' col-sm-' + this.labelsm;
34368                 items.cls += ' col-sm-' + (12 - this.labelsm);
34369             }
34370             
34371             if(this.labelxs > 0){
34372                 label.cls += ' col-xs-' + this.labelxs;
34373                 items.cls += ' col-xs-' + (12 - this.labelxs);
34374             }
34375         }
34376         
34377         var cfg = {
34378             tag : 'div',
34379             cls : 'roo-radio-set',
34380             cn : [
34381                 {
34382                     tag : 'input',
34383                     cls : 'roo-radio-set-input',
34384                     type : 'hidden',
34385                     name : this.name,
34386                     value : this.value ? this.value :  ''
34387                 },
34388                 label,
34389                 items
34390             ]
34391         };
34392         
34393         if(this.weight.length){
34394             cfg.cls += ' roo-radio-' + this.weight;
34395         }
34396         
34397         if(this.inline) {
34398             cfg.cls += ' roo-radio-set-inline';
34399         }
34400         
34401         var settings=this;
34402         ['xs','sm','md','lg'].map(function(size){
34403             if (settings[size]) {
34404                 cfg.cls += ' col-' + size + '-' + settings[size];
34405             }
34406         });
34407         
34408         return cfg;
34409         
34410     },
34411
34412     initEvents : function()
34413     {
34414         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34415         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34416         
34417         if(!this.fieldLabel.length){
34418             this.labelEl.hide();
34419         }
34420         
34421         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34422         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34423         
34424         this.indicator = this.indicatorEl();
34425         
34426         if(this.indicator){
34427             this.indicator.addClass('invisible');
34428         }
34429         
34430         this.originalValue = this.getValue();
34431         
34432     },
34433     
34434     inputEl: function ()
34435     {
34436         return this.el.select('.roo-radio-set-input', true).first();
34437     },
34438     
34439     getChildContainer : function()
34440     {
34441         return this.itemsEl;
34442     },
34443     
34444     register : function(item)
34445     {
34446         this.radioes.push(item);
34447         
34448     },
34449     
34450     validate : function()
34451     {   
34452         if(this.getVisibilityEl().hasClass('hidden')){
34453             return true;
34454         }
34455         
34456         var valid = false;
34457         
34458         Roo.each(this.radioes, function(i){
34459             if(!i.checked){
34460                 return;
34461             }
34462             
34463             valid = true;
34464             return false;
34465         });
34466         
34467         if(this.allowBlank) {
34468             return true;
34469         }
34470         
34471         if(this.disabled || valid){
34472             this.markValid();
34473             return true;
34474         }
34475         
34476         this.markInvalid();
34477         return false;
34478         
34479     },
34480     
34481     markValid : function()
34482     {
34483         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34484             this.indicatorEl().removeClass('visible');
34485             this.indicatorEl().addClass('invisible');
34486         }
34487         
34488         
34489         if (Roo.bootstrap.version == 3) {
34490             this.el.removeClass([this.invalidClass, this.validClass]);
34491             this.el.addClass(this.validClass);
34492         } else {
34493             this.el.removeClass(['is-invalid','is-valid']);
34494             this.el.addClass(['is-valid']);
34495         }
34496         this.fireEvent('valid', this);
34497     },
34498     
34499     markInvalid : function(msg)
34500     {
34501         if(this.allowBlank || this.disabled){
34502             return;
34503         }
34504         
34505         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34506             this.indicatorEl().removeClass('invisible');
34507             this.indicatorEl().addClass('visible');
34508         }
34509         if (Roo.bootstrap.version == 3) {
34510             this.el.removeClass([this.invalidClass, this.validClass]);
34511             this.el.addClass(this.invalidClass);
34512         } else {
34513             this.el.removeClass(['is-invalid','is-valid']);
34514             this.el.addClass(['is-invalid']);
34515         }
34516         
34517         this.fireEvent('invalid', this, msg);
34518         
34519     },
34520     
34521     setValue : function(v, suppressEvent)
34522     {   
34523         if(this.value === v){
34524             return;
34525         }
34526         
34527         this.value = v;
34528         
34529         if(this.rendered){
34530             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34531         }
34532         
34533         Roo.each(this.radioes, function(i){
34534             i.checked = false;
34535             i.el.removeClass('checked');
34536         });
34537         
34538         Roo.each(this.radioes, function(i){
34539             
34540             if(i.value === v || i.value.toString() === v.toString()){
34541                 i.checked = true;
34542                 i.el.addClass('checked');
34543                 
34544                 if(suppressEvent !== true){
34545                     this.fireEvent('check', this, i);
34546                 }
34547                 
34548                 return false;
34549             }
34550             
34551         }, this);
34552         
34553         this.validate();
34554     },
34555     
34556     clearInvalid : function(){
34557         
34558         if(!this.el || this.preventMark){
34559             return;
34560         }
34561         
34562         this.el.removeClass([this.invalidClass]);
34563         
34564         this.fireEvent('valid', this);
34565     }
34566     
34567 });
34568
34569 Roo.apply(Roo.bootstrap.RadioSet, {
34570     
34571     groups: {},
34572     
34573     register : function(set)
34574     {
34575         this.groups[set.name] = set;
34576     },
34577     
34578     get: function(name) 
34579     {
34580         if (typeof(this.groups[name]) == 'undefined') {
34581             return false;
34582         }
34583         
34584         return this.groups[name] ;
34585     }
34586     
34587 });
34588 /*
34589  * Based on:
34590  * Ext JS Library 1.1.1
34591  * Copyright(c) 2006-2007, Ext JS, LLC.
34592  *
34593  * Originally Released Under LGPL - original licence link has changed is not relivant.
34594  *
34595  * Fork - LGPL
34596  * <script type="text/javascript">
34597  */
34598
34599
34600 /**
34601  * @class Roo.bootstrap.SplitBar
34602  * @extends Roo.util.Observable
34603  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34604  * <br><br>
34605  * Usage:
34606  * <pre><code>
34607 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34608                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34609 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34610 split.minSize = 100;
34611 split.maxSize = 600;
34612 split.animate = true;
34613 split.on('moved', splitterMoved);
34614 </code></pre>
34615  * @constructor
34616  * Create a new SplitBar
34617  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34618  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34619  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34620  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34621                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34622                         position of the SplitBar).
34623  */
34624 Roo.bootstrap.SplitBar = function(cfg){
34625     
34626     /** @private */
34627     
34628     //{
34629     //  dragElement : elm
34630     //  resizingElement: el,
34631         // optional..
34632     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34633     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34634         // existingProxy ???
34635     //}
34636     
34637     this.el = Roo.get(cfg.dragElement, true);
34638     this.el.dom.unselectable = "on";
34639     /** @private */
34640     this.resizingEl = Roo.get(cfg.resizingElement, true);
34641
34642     /**
34643      * @private
34644      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34645      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34646      * @type Number
34647      */
34648     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34649     
34650     /**
34651      * The minimum size of the resizing element. (Defaults to 0)
34652      * @type Number
34653      */
34654     this.minSize = 0;
34655     
34656     /**
34657      * The maximum size of the resizing element. (Defaults to 2000)
34658      * @type Number
34659      */
34660     this.maxSize = 2000;
34661     
34662     /**
34663      * Whether to animate the transition to the new size
34664      * @type Boolean
34665      */
34666     this.animate = false;
34667     
34668     /**
34669      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34670      * @type Boolean
34671      */
34672     this.useShim = false;
34673     
34674     /** @private */
34675     this.shim = null;
34676     
34677     if(!cfg.existingProxy){
34678         /** @private */
34679         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34680     }else{
34681         this.proxy = Roo.get(cfg.existingProxy).dom;
34682     }
34683     /** @private */
34684     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34685     
34686     /** @private */
34687     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34688     
34689     /** @private */
34690     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34691     
34692     /** @private */
34693     this.dragSpecs = {};
34694     
34695     /**
34696      * @private The adapter to use to positon and resize elements
34697      */
34698     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34699     this.adapter.init(this);
34700     
34701     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34702         /** @private */
34703         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34704         this.el.addClass("roo-splitbar-h");
34705     }else{
34706         /** @private */
34707         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34708         this.el.addClass("roo-splitbar-v");
34709     }
34710     
34711     this.addEvents({
34712         /**
34713          * @event resize
34714          * Fires when the splitter is moved (alias for {@link #event-moved})
34715          * @param {Roo.bootstrap.SplitBar} this
34716          * @param {Number} newSize the new width or height
34717          */
34718         "resize" : true,
34719         /**
34720          * @event moved
34721          * Fires when the splitter is moved
34722          * @param {Roo.bootstrap.SplitBar} this
34723          * @param {Number} newSize the new width or height
34724          */
34725         "moved" : true,
34726         /**
34727          * @event beforeresize
34728          * Fires before the splitter is dragged
34729          * @param {Roo.bootstrap.SplitBar} this
34730          */
34731         "beforeresize" : true,
34732
34733         "beforeapply" : true
34734     });
34735
34736     Roo.util.Observable.call(this);
34737 };
34738
34739 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34740     onStartProxyDrag : function(x, y){
34741         this.fireEvent("beforeresize", this);
34742         if(!this.overlay){
34743             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34744             o.unselectable();
34745             o.enableDisplayMode("block");
34746             // all splitbars share the same overlay
34747             Roo.bootstrap.SplitBar.prototype.overlay = o;
34748         }
34749         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34750         this.overlay.show();
34751         Roo.get(this.proxy).setDisplayed("block");
34752         var size = this.adapter.getElementSize(this);
34753         this.activeMinSize = this.getMinimumSize();;
34754         this.activeMaxSize = this.getMaximumSize();;
34755         var c1 = size - this.activeMinSize;
34756         var c2 = Math.max(this.activeMaxSize - size, 0);
34757         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34758             this.dd.resetConstraints();
34759             this.dd.setXConstraint(
34760                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34761                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34762             );
34763             this.dd.setYConstraint(0, 0);
34764         }else{
34765             this.dd.resetConstraints();
34766             this.dd.setXConstraint(0, 0);
34767             this.dd.setYConstraint(
34768                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34769                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34770             );
34771          }
34772         this.dragSpecs.startSize = size;
34773         this.dragSpecs.startPoint = [x, y];
34774         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34775     },
34776     
34777     /** 
34778      * @private Called after the drag operation by the DDProxy
34779      */
34780     onEndProxyDrag : function(e){
34781         Roo.get(this.proxy).setDisplayed(false);
34782         var endPoint = Roo.lib.Event.getXY(e);
34783         if(this.overlay){
34784             this.overlay.hide();
34785         }
34786         var newSize;
34787         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34788             newSize = this.dragSpecs.startSize + 
34789                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34790                     endPoint[0] - this.dragSpecs.startPoint[0] :
34791                     this.dragSpecs.startPoint[0] - endPoint[0]
34792                 );
34793         }else{
34794             newSize = this.dragSpecs.startSize + 
34795                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34796                     endPoint[1] - this.dragSpecs.startPoint[1] :
34797                     this.dragSpecs.startPoint[1] - endPoint[1]
34798                 );
34799         }
34800         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34801         if(newSize != this.dragSpecs.startSize){
34802             if(this.fireEvent('beforeapply', this, newSize) !== false){
34803                 this.adapter.setElementSize(this, newSize);
34804                 this.fireEvent("moved", this, newSize);
34805                 this.fireEvent("resize", this, newSize);
34806             }
34807         }
34808     },
34809     
34810     /**
34811      * Get the adapter this SplitBar uses
34812      * @return The adapter object
34813      */
34814     getAdapter : function(){
34815         return this.adapter;
34816     },
34817     
34818     /**
34819      * Set the adapter this SplitBar uses
34820      * @param {Object} adapter A SplitBar adapter object
34821      */
34822     setAdapter : function(adapter){
34823         this.adapter = adapter;
34824         this.adapter.init(this);
34825     },
34826     
34827     /**
34828      * Gets the minimum size for the resizing element
34829      * @return {Number} The minimum size
34830      */
34831     getMinimumSize : function(){
34832         return this.minSize;
34833     },
34834     
34835     /**
34836      * Sets the minimum size for the resizing element
34837      * @param {Number} minSize The minimum size
34838      */
34839     setMinimumSize : function(minSize){
34840         this.minSize = minSize;
34841     },
34842     
34843     /**
34844      * Gets the maximum size for the resizing element
34845      * @return {Number} The maximum size
34846      */
34847     getMaximumSize : function(){
34848         return this.maxSize;
34849     },
34850     
34851     /**
34852      * Sets the maximum size for the resizing element
34853      * @param {Number} maxSize The maximum size
34854      */
34855     setMaximumSize : function(maxSize){
34856         this.maxSize = maxSize;
34857     },
34858     
34859     /**
34860      * Sets the initialize size for the resizing element
34861      * @param {Number} size The initial size
34862      */
34863     setCurrentSize : function(size){
34864         var oldAnimate = this.animate;
34865         this.animate = false;
34866         this.adapter.setElementSize(this, size);
34867         this.animate = oldAnimate;
34868     },
34869     
34870     /**
34871      * Destroy this splitbar. 
34872      * @param {Boolean} removeEl True to remove the element
34873      */
34874     destroy : function(removeEl){
34875         if(this.shim){
34876             this.shim.remove();
34877         }
34878         this.dd.unreg();
34879         this.proxy.parentNode.removeChild(this.proxy);
34880         if(removeEl){
34881             this.el.remove();
34882         }
34883     }
34884 });
34885
34886 /**
34887  * @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.
34888  */
34889 Roo.bootstrap.SplitBar.createProxy = function(dir){
34890     var proxy = new Roo.Element(document.createElement("div"));
34891     proxy.unselectable();
34892     var cls = 'roo-splitbar-proxy';
34893     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34894     document.body.appendChild(proxy.dom);
34895     return proxy.dom;
34896 };
34897
34898 /** 
34899  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34900  * Default Adapter. It assumes the splitter and resizing element are not positioned
34901  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34902  */
34903 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34904 };
34905
34906 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34907     // do nothing for now
34908     init : function(s){
34909     
34910     },
34911     /**
34912      * Called before drag operations to get the current size of the resizing element. 
34913      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34914      */
34915      getElementSize : function(s){
34916         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34917             return s.resizingEl.getWidth();
34918         }else{
34919             return s.resizingEl.getHeight();
34920         }
34921     },
34922     
34923     /**
34924      * Called after drag operations to set the size of the resizing element.
34925      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34926      * @param {Number} newSize The new size to set
34927      * @param {Function} onComplete A function to be invoked when resizing is complete
34928      */
34929     setElementSize : function(s, newSize, onComplete){
34930         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34931             if(!s.animate){
34932                 s.resizingEl.setWidth(newSize);
34933                 if(onComplete){
34934                     onComplete(s, newSize);
34935                 }
34936             }else{
34937                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34938             }
34939         }else{
34940             
34941             if(!s.animate){
34942                 s.resizingEl.setHeight(newSize);
34943                 if(onComplete){
34944                     onComplete(s, newSize);
34945                 }
34946             }else{
34947                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34948             }
34949         }
34950     }
34951 };
34952
34953 /** 
34954  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34955  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34956  * Adapter that  moves the splitter element to align with the resized sizing element. 
34957  * Used with an absolute positioned SplitBar.
34958  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34959  * document.body, make sure you assign an id to the body element.
34960  */
34961 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34962     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34963     this.container = Roo.get(container);
34964 };
34965
34966 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34967     init : function(s){
34968         this.basic.init(s);
34969     },
34970     
34971     getElementSize : function(s){
34972         return this.basic.getElementSize(s);
34973     },
34974     
34975     setElementSize : function(s, newSize, onComplete){
34976         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34977     },
34978     
34979     moveSplitter : function(s){
34980         var yes = Roo.bootstrap.SplitBar;
34981         switch(s.placement){
34982             case yes.LEFT:
34983                 s.el.setX(s.resizingEl.getRight());
34984                 break;
34985             case yes.RIGHT:
34986                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34987                 break;
34988             case yes.TOP:
34989                 s.el.setY(s.resizingEl.getBottom());
34990                 break;
34991             case yes.BOTTOM:
34992                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34993                 break;
34994         }
34995     }
34996 };
34997
34998 /**
34999  * Orientation constant - Create a vertical SplitBar
35000  * @static
35001  * @type Number
35002  */
35003 Roo.bootstrap.SplitBar.VERTICAL = 1;
35004
35005 /**
35006  * Orientation constant - Create a horizontal SplitBar
35007  * @static
35008  * @type Number
35009  */
35010 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35011
35012 /**
35013  * Placement constant - The resizing element is to the left of the splitter element
35014  * @static
35015  * @type Number
35016  */
35017 Roo.bootstrap.SplitBar.LEFT = 1;
35018
35019 /**
35020  * Placement constant - The resizing element is to the right of the splitter element
35021  * @static
35022  * @type Number
35023  */
35024 Roo.bootstrap.SplitBar.RIGHT = 2;
35025
35026 /**
35027  * Placement constant - The resizing element is positioned above the splitter element
35028  * @static
35029  * @type Number
35030  */
35031 Roo.bootstrap.SplitBar.TOP = 3;
35032
35033 /**
35034  * Placement constant - The resizing element is positioned under splitter element
35035  * @static
35036  * @type Number
35037  */
35038 Roo.bootstrap.SplitBar.BOTTOM = 4;
35039 Roo.namespace("Roo.bootstrap.layout");/*
35040  * Based on:
35041  * Ext JS Library 1.1.1
35042  * Copyright(c) 2006-2007, Ext JS, LLC.
35043  *
35044  * Originally Released Under LGPL - original licence link has changed is not relivant.
35045  *
35046  * Fork - LGPL
35047  * <script type="text/javascript">
35048  */
35049
35050 /**
35051  * @class Roo.bootstrap.layout.Manager
35052  * @extends Roo.bootstrap.Component
35053  * Base class for layout managers.
35054  */
35055 Roo.bootstrap.layout.Manager = function(config)
35056 {
35057     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35058
35059
35060
35061
35062
35063     /** false to disable window resize monitoring @type Boolean */
35064     this.monitorWindowResize = true;
35065     this.regions = {};
35066     this.addEvents({
35067         /**
35068          * @event layout
35069          * Fires when a layout is performed.
35070          * @param {Roo.LayoutManager} this
35071          */
35072         "layout" : true,
35073         /**
35074          * @event regionresized
35075          * Fires when the user resizes a region.
35076          * @param {Roo.LayoutRegion} region The resized region
35077          * @param {Number} newSize The new size (width for east/west, height for north/south)
35078          */
35079         "regionresized" : true,
35080         /**
35081          * @event regioncollapsed
35082          * Fires when a region is collapsed.
35083          * @param {Roo.LayoutRegion} region The collapsed region
35084          */
35085         "regioncollapsed" : true,
35086         /**
35087          * @event regionexpanded
35088          * Fires when a region is expanded.
35089          * @param {Roo.LayoutRegion} region The expanded region
35090          */
35091         "regionexpanded" : true
35092     });
35093     this.updating = false;
35094
35095     if (config.el) {
35096         this.el = Roo.get(config.el);
35097         this.initEvents();
35098     }
35099
35100 };
35101
35102 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35103
35104
35105     regions : null,
35106
35107     monitorWindowResize : true,
35108
35109
35110     updating : false,
35111
35112
35113     onRender : function(ct, position)
35114     {
35115         if(!this.el){
35116             this.el = Roo.get(ct);
35117             this.initEvents();
35118         }
35119         //this.fireEvent('render',this);
35120     },
35121
35122
35123     initEvents: function()
35124     {
35125
35126
35127         // ie scrollbar fix
35128         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35129             document.body.scroll = "no";
35130         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35131             this.el.position('relative');
35132         }
35133         this.id = this.el.id;
35134         this.el.addClass("roo-layout-container");
35135         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35136         if(this.el.dom != document.body ) {
35137             this.el.on('resize', this.layout,this);
35138             this.el.on('show', this.layout,this);
35139         }
35140
35141     },
35142
35143     /**
35144      * Returns true if this layout is currently being updated
35145      * @return {Boolean}
35146      */
35147     isUpdating : function(){
35148         return this.updating;
35149     },
35150
35151     /**
35152      * Suspend the LayoutManager from doing auto-layouts while
35153      * making multiple add or remove calls
35154      */
35155     beginUpdate : function(){
35156         this.updating = true;
35157     },
35158
35159     /**
35160      * Restore auto-layouts and optionally disable the manager from performing a layout
35161      * @param {Boolean} noLayout true to disable a layout update
35162      */
35163     endUpdate : function(noLayout){
35164         this.updating = false;
35165         if(!noLayout){
35166             this.layout();
35167         }
35168     },
35169
35170     layout: function(){
35171         // abstract...
35172     },
35173
35174     onRegionResized : function(region, newSize){
35175         this.fireEvent("regionresized", region, newSize);
35176         this.layout();
35177     },
35178
35179     onRegionCollapsed : function(region){
35180         this.fireEvent("regioncollapsed", region);
35181     },
35182
35183     onRegionExpanded : function(region){
35184         this.fireEvent("regionexpanded", region);
35185     },
35186
35187     /**
35188      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35189      * performs box-model adjustments.
35190      * @return {Object} The size as an object {width: (the width), height: (the height)}
35191      */
35192     getViewSize : function()
35193     {
35194         var size;
35195         if(this.el.dom != document.body){
35196             size = this.el.getSize();
35197         }else{
35198             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35199         }
35200         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35201         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35202         return size;
35203     },
35204
35205     /**
35206      * Returns the Element this layout is bound to.
35207      * @return {Roo.Element}
35208      */
35209     getEl : function(){
35210         return this.el;
35211     },
35212
35213     /**
35214      * Returns the specified region.
35215      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35216      * @return {Roo.LayoutRegion}
35217      */
35218     getRegion : function(target){
35219         return this.regions[target.toLowerCase()];
35220     },
35221
35222     onWindowResize : function(){
35223         if(this.monitorWindowResize){
35224             this.layout();
35225         }
35226     }
35227 });
35228 /*
35229  * Based on:
35230  * Ext JS Library 1.1.1
35231  * Copyright(c) 2006-2007, Ext JS, LLC.
35232  *
35233  * Originally Released Under LGPL - original licence link has changed is not relivant.
35234  *
35235  * Fork - LGPL
35236  * <script type="text/javascript">
35237  */
35238 /**
35239  * @class Roo.bootstrap.layout.Border
35240  * @extends Roo.bootstrap.layout.Manager
35241  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35242  * please see: examples/bootstrap/nested.html<br><br>
35243  
35244 <b>The container the layout is rendered into can be either the body element or any other element.
35245 If it is not the body element, the container needs to either be an absolute positioned element,
35246 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35247 the container size if it is not the body element.</b>
35248
35249 * @constructor
35250 * Create a new Border
35251 * @param {Object} config Configuration options
35252  */
35253 Roo.bootstrap.layout.Border = function(config){
35254     config = config || {};
35255     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35256     
35257     
35258     
35259     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35260         if(config[region]){
35261             config[region].region = region;
35262             this.addRegion(config[region]);
35263         }
35264     },this);
35265     
35266 };
35267
35268 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35269
35270 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35271     
35272     parent : false, // this might point to a 'nest' or a ???
35273     
35274     /**
35275      * Creates and adds a new region if it doesn't already exist.
35276      * @param {String} target The target region key (north, south, east, west or center).
35277      * @param {Object} config The regions config object
35278      * @return {BorderLayoutRegion} The new region
35279      */
35280     addRegion : function(config)
35281     {
35282         if(!this.regions[config.region]){
35283             var r = this.factory(config);
35284             this.bindRegion(r);
35285         }
35286         return this.regions[config.region];
35287     },
35288
35289     // private (kinda)
35290     bindRegion : function(r){
35291         this.regions[r.config.region] = r;
35292         
35293         r.on("visibilitychange",    this.layout, this);
35294         r.on("paneladded",          this.layout, this);
35295         r.on("panelremoved",        this.layout, this);
35296         r.on("invalidated",         this.layout, this);
35297         r.on("resized",             this.onRegionResized, this);
35298         r.on("collapsed",           this.onRegionCollapsed, this);
35299         r.on("expanded",            this.onRegionExpanded, this);
35300     },
35301
35302     /**
35303      * Performs a layout update.
35304      */
35305     layout : function()
35306     {
35307         if(this.updating) {
35308             return;
35309         }
35310         
35311         // render all the rebions if they have not been done alreayd?
35312         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35313             if(this.regions[region] && !this.regions[region].bodyEl){
35314                 this.regions[region].onRender(this.el)
35315             }
35316         },this);
35317         
35318         var size = this.getViewSize();
35319         var w = size.width;
35320         var h = size.height;
35321         var centerW = w;
35322         var centerH = h;
35323         var centerY = 0;
35324         var centerX = 0;
35325         //var x = 0, y = 0;
35326
35327         var rs = this.regions;
35328         var north = rs["north"];
35329         var south = rs["south"]; 
35330         var west = rs["west"];
35331         var east = rs["east"];
35332         var center = rs["center"];
35333         //if(this.hideOnLayout){ // not supported anymore
35334             //c.el.setStyle("display", "none");
35335         //}
35336         if(north && north.isVisible()){
35337             var b = north.getBox();
35338             var m = north.getMargins();
35339             b.width = w - (m.left+m.right);
35340             b.x = m.left;
35341             b.y = m.top;
35342             centerY = b.height + b.y + m.bottom;
35343             centerH -= centerY;
35344             north.updateBox(this.safeBox(b));
35345         }
35346         if(south && south.isVisible()){
35347             var b = south.getBox();
35348             var m = south.getMargins();
35349             b.width = w - (m.left+m.right);
35350             b.x = m.left;
35351             var totalHeight = (b.height + m.top + m.bottom);
35352             b.y = h - totalHeight + m.top;
35353             centerH -= totalHeight;
35354             south.updateBox(this.safeBox(b));
35355         }
35356         if(west && west.isVisible()){
35357             var b = west.getBox();
35358             var m = west.getMargins();
35359             b.height = centerH - (m.top+m.bottom);
35360             b.x = m.left;
35361             b.y = centerY + m.top;
35362             var totalWidth = (b.width + m.left + m.right);
35363             centerX += totalWidth;
35364             centerW -= totalWidth;
35365             west.updateBox(this.safeBox(b));
35366         }
35367         if(east && east.isVisible()){
35368             var b = east.getBox();
35369             var m = east.getMargins();
35370             b.height = centerH - (m.top+m.bottom);
35371             var totalWidth = (b.width + m.left + m.right);
35372             b.x = w - totalWidth + m.left;
35373             b.y = centerY + m.top;
35374             centerW -= totalWidth;
35375             east.updateBox(this.safeBox(b));
35376         }
35377         if(center){
35378             var m = center.getMargins();
35379             var centerBox = {
35380                 x: centerX + m.left,
35381                 y: centerY + m.top,
35382                 width: centerW - (m.left+m.right),
35383                 height: centerH - (m.top+m.bottom)
35384             };
35385             //if(this.hideOnLayout){
35386                 //center.el.setStyle("display", "block");
35387             //}
35388             center.updateBox(this.safeBox(centerBox));
35389         }
35390         this.el.repaint();
35391         this.fireEvent("layout", this);
35392     },
35393
35394     // private
35395     safeBox : function(box){
35396         box.width = Math.max(0, box.width);
35397         box.height = Math.max(0, box.height);
35398         return box;
35399     },
35400
35401     /**
35402      * Adds a ContentPanel (or subclass) to this layout.
35403      * @param {String} target The target region key (north, south, east, west or center).
35404      * @param {Roo.ContentPanel} panel The panel to add
35405      * @return {Roo.ContentPanel} The added panel
35406      */
35407     add : function(target, panel){
35408          
35409         target = target.toLowerCase();
35410         return this.regions[target].add(panel);
35411     },
35412
35413     /**
35414      * Remove a ContentPanel (or subclass) to this layout.
35415      * @param {String} target The target region key (north, south, east, west or center).
35416      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35417      * @return {Roo.ContentPanel} The removed panel
35418      */
35419     remove : function(target, panel){
35420         target = target.toLowerCase();
35421         return this.regions[target].remove(panel);
35422     },
35423
35424     /**
35425      * Searches all regions for a panel with the specified id
35426      * @param {String} panelId
35427      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35428      */
35429     findPanel : function(panelId){
35430         var rs = this.regions;
35431         for(var target in rs){
35432             if(typeof rs[target] != "function"){
35433                 var p = rs[target].getPanel(panelId);
35434                 if(p){
35435                     return p;
35436                 }
35437             }
35438         }
35439         return null;
35440     },
35441
35442     /**
35443      * Searches all regions for a panel with the specified id and activates (shows) it.
35444      * @param {String/ContentPanel} panelId The panels id or the panel itself
35445      * @return {Roo.ContentPanel} The shown panel or null
35446      */
35447     showPanel : function(panelId) {
35448       var rs = this.regions;
35449       for(var target in rs){
35450          var r = rs[target];
35451          if(typeof r != "function"){
35452             if(r.hasPanel(panelId)){
35453                return r.showPanel(panelId);
35454             }
35455          }
35456       }
35457       return null;
35458    },
35459
35460    /**
35461      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35462      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35463      */
35464    /*
35465     restoreState : function(provider){
35466         if(!provider){
35467             provider = Roo.state.Manager;
35468         }
35469         var sm = new Roo.LayoutStateManager();
35470         sm.init(this, provider);
35471     },
35472 */
35473  
35474  
35475     /**
35476      * Adds a xtype elements to the layout.
35477      * <pre><code>
35478
35479 layout.addxtype({
35480        xtype : 'ContentPanel',
35481        region: 'west',
35482        items: [ .... ]
35483    }
35484 );
35485
35486 layout.addxtype({
35487         xtype : 'NestedLayoutPanel',
35488         region: 'west',
35489         layout: {
35490            center: { },
35491            west: { }   
35492         },
35493         items : [ ... list of content panels or nested layout panels.. ]
35494    }
35495 );
35496 </code></pre>
35497      * @param {Object} cfg Xtype definition of item to add.
35498      */
35499     addxtype : function(cfg)
35500     {
35501         // basically accepts a pannel...
35502         // can accept a layout region..!?!?
35503         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35504         
35505         
35506         // theory?  children can only be panels??
35507         
35508         //if (!cfg.xtype.match(/Panel$/)) {
35509         //    return false;
35510         //}
35511         var ret = false;
35512         
35513         if (typeof(cfg.region) == 'undefined') {
35514             Roo.log("Failed to add Panel, region was not set");
35515             Roo.log(cfg);
35516             return false;
35517         }
35518         var region = cfg.region;
35519         delete cfg.region;
35520         
35521           
35522         var xitems = [];
35523         if (cfg.items) {
35524             xitems = cfg.items;
35525             delete cfg.items;
35526         }
35527         var nb = false;
35528         
35529         if ( region == 'center') {
35530             Roo.log("Center: " + cfg.title);
35531         }
35532         
35533         
35534         switch(cfg.xtype) 
35535         {
35536             case 'Content':  // ContentPanel (el, cfg)
35537             case 'Scroll':  // ContentPanel (el, cfg)
35538             case 'View': 
35539                 cfg.autoCreate = cfg.autoCreate || true;
35540                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35541                 //} else {
35542                 //    var el = this.el.createChild();
35543                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35544                 //}
35545                 
35546                 this.add(region, ret);
35547                 break;
35548             
35549             /*
35550             case 'TreePanel': // our new panel!
35551                 cfg.el = this.el.createChild();
35552                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35553                 this.add(region, ret);
35554                 break;
35555             */
35556             
35557             case 'Nest': 
35558                 // create a new Layout (which is  a Border Layout...
35559                 
35560                 var clayout = cfg.layout;
35561                 clayout.el  = this.el.createChild();
35562                 clayout.items   = clayout.items  || [];
35563                 
35564                 delete cfg.layout;
35565                 
35566                 // replace this exitems with the clayout ones..
35567                 xitems = clayout.items;
35568                  
35569                 // force background off if it's in center...
35570                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35571                     cfg.background = false;
35572                 }
35573                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35574                 
35575                 
35576                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35577                 //console.log('adding nested layout panel '  + cfg.toSource());
35578                 this.add(region, ret);
35579                 nb = {}; /// find first...
35580                 break;
35581             
35582             case 'Grid':
35583                 
35584                 // needs grid and region
35585                 
35586                 //var el = this.getRegion(region).el.createChild();
35587                 /*
35588                  *var el = this.el.createChild();
35589                 // create the grid first...
35590                 cfg.grid.container = el;
35591                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35592                 */
35593                 
35594                 if (region == 'center' && this.active ) {
35595                     cfg.background = false;
35596                 }
35597                 
35598                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35599                 
35600                 this.add(region, ret);
35601                 /*
35602                 if (cfg.background) {
35603                     // render grid on panel activation (if panel background)
35604                     ret.on('activate', function(gp) {
35605                         if (!gp.grid.rendered) {
35606                     //        gp.grid.render(el);
35607                         }
35608                     });
35609                 } else {
35610                   //  cfg.grid.render(el);
35611                 }
35612                 */
35613                 break;
35614            
35615            
35616             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35617                 // it was the old xcomponent building that caused this before.
35618                 // espeically if border is the top element in the tree.
35619                 ret = this;
35620                 break; 
35621                 
35622                     
35623                 
35624                 
35625                 
35626             default:
35627                 /*
35628                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35629                     
35630                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35631                     this.add(region, ret);
35632                 } else {
35633                 */
35634                     Roo.log(cfg);
35635                     throw "Can not add '" + cfg.xtype + "' to Border";
35636                     return null;
35637              
35638                                 
35639              
35640         }
35641         this.beginUpdate();
35642         // add children..
35643         var region = '';
35644         var abn = {};
35645         Roo.each(xitems, function(i)  {
35646             region = nb && i.region ? i.region : false;
35647             
35648             var add = ret.addxtype(i);
35649            
35650             if (region) {
35651                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35652                 if (!i.background) {
35653                     abn[region] = nb[region] ;
35654                 }
35655             }
35656             
35657         });
35658         this.endUpdate();
35659
35660         // make the last non-background panel active..
35661         //if (nb) { Roo.log(abn); }
35662         if (nb) {
35663             
35664             for(var r in abn) {
35665                 region = this.getRegion(r);
35666                 if (region) {
35667                     // tried using nb[r], but it does not work..
35668                      
35669                     region.showPanel(abn[r]);
35670                    
35671                 }
35672             }
35673         }
35674         return ret;
35675         
35676     },
35677     
35678     
35679 // private
35680     factory : function(cfg)
35681     {
35682         
35683         var validRegions = Roo.bootstrap.layout.Border.regions;
35684
35685         var target = cfg.region;
35686         cfg.mgr = this;
35687         
35688         var r = Roo.bootstrap.layout;
35689         Roo.log(target);
35690         switch(target){
35691             case "north":
35692                 return new r.North(cfg);
35693             case "south":
35694                 return new r.South(cfg);
35695             case "east":
35696                 return new r.East(cfg);
35697             case "west":
35698                 return new r.West(cfg);
35699             case "center":
35700                 return new r.Center(cfg);
35701         }
35702         throw 'Layout region "'+target+'" not supported.';
35703     }
35704     
35705     
35706 });
35707  /*
35708  * Based on:
35709  * Ext JS Library 1.1.1
35710  * Copyright(c) 2006-2007, Ext JS, LLC.
35711  *
35712  * Originally Released Under LGPL - original licence link has changed is not relivant.
35713  *
35714  * Fork - LGPL
35715  * <script type="text/javascript">
35716  */
35717  
35718 /**
35719  * @class Roo.bootstrap.layout.Basic
35720  * @extends Roo.util.Observable
35721  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35722  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35723  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35724  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35725  * @cfg {string}   region  the region that it inhabits..
35726  * @cfg {bool}   skipConfig skip config?
35727  * 
35728
35729  */
35730 Roo.bootstrap.layout.Basic = function(config){
35731     
35732     this.mgr = config.mgr;
35733     
35734     this.position = config.region;
35735     
35736     var skipConfig = config.skipConfig;
35737     
35738     this.events = {
35739         /**
35740          * @scope Roo.BasicLayoutRegion
35741          */
35742         
35743         /**
35744          * @event beforeremove
35745          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35746          * @param {Roo.LayoutRegion} this
35747          * @param {Roo.ContentPanel} panel The panel
35748          * @param {Object} e The cancel event object
35749          */
35750         "beforeremove" : true,
35751         /**
35752          * @event invalidated
35753          * Fires when the layout for this region is changed.
35754          * @param {Roo.LayoutRegion} this
35755          */
35756         "invalidated" : true,
35757         /**
35758          * @event visibilitychange
35759          * Fires when this region is shown or hidden 
35760          * @param {Roo.LayoutRegion} this
35761          * @param {Boolean} visibility true or false
35762          */
35763         "visibilitychange" : true,
35764         /**
35765          * @event paneladded
35766          * Fires when a panel is added. 
35767          * @param {Roo.LayoutRegion} this
35768          * @param {Roo.ContentPanel} panel The panel
35769          */
35770         "paneladded" : true,
35771         /**
35772          * @event panelremoved
35773          * Fires when a panel is removed. 
35774          * @param {Roo.LayoutRegion} this
35775          * @param {Roo.ContentPanel} panel The panel
35776          */
35777         "panelremoved" : true,
35778         /**
35779          * @event beforecollapse
35780          * Fires when this region before collapse.
35781          * @param {Roo.LayoutRegion} this
35782          */
35783         "beforecollapse" : true,
35784         /**
35785          * @event collapsed
35786          * Fires when this region is collapsed.
35787          * @param {Roo.LayoutRegion} this
35788          */
35789         "collapsed" : true,
35790         /**
35791          * @event expanded
35792          * Fires when this region is expanded.
35793          * @param {Roo.LayoutRegion} this
35794          */
35795         "expanded" : true,
35796         /**
35797          * @event slideshow
35798          * Fires when this region is slid into view.
35799          * @param {Roo.LayoutRegion} this
35800          */
35801         "slideshow" : true,
35802         /**
35803          * @event slidehide
35804          * Fires when this region slides out of view. 
35805          * @param {Roo.LayoutRegion} this
35806          */
35807         "slidehide" : true,
35808         /**
35809          * @event panelactivated
35810          * Fires when a panel is activated. 
35811          * @param {Roo.LayoutRegion} this
35812          * @param {Roo.ContentPanel} panel The activated panel
35813          */
35814         "panelactivated" : true,
35815         /**
35816          * @event resized
35817          * Fires when the user resizes this region. 
35818          * @param {Roo.LayoutRegion} this
35819          * @param {Number} newSize The new size (width for east/west, height for north/south)
35820          */
35821         "resized" : true
35822     };
35823     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35824     this.panels = new Roo.util.MixedCollection();
35825     this.panels.getKey = this.getPanelId.createDelegate(this);
35826     this.box = null;
35827     this.activePanel = null;
35828     // ensure listeners are added...
35829     
35830     if (config.listeners || config.events) {
35831         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35832             listeners : config.listeners || {},
35833             events : config.events || {}
35834         });
35835     }
35836     
35837     if(skipConfig !== true){
35838         this.applyConfig(config);
35839     }
35840 };
35841
35842 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35843 {
35844     getPanelId : function(p){
35845         return p.getId();
35846     },
35847     
35848     applyConfig : function(config){
35849         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35850         this.config = config;
35851         
35852     },
35853     
35854     /**
35855      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35856      * the width, for horizontal (north, south) the height.
35857      * @param {Number} newSize The new width or height
35858      */
35859     resizeTo : function(newSize){
35860         var el = this.el ? this.el :
35861                  (this.activePanel ? this.activePanel.getEl() : null);
35862         if(el){
35863             switch(this.position){
35864                 case "east":
35865                 case "west":
35866                     el.setWidth(newSize);
35867                     this.fireEvent("resized", this, newSize);
35868                 break;
35869                 case "north":
35870                 case "south":
35871                     el.setHeight(newSize);
35872                     this.fireEvent("resized", this, newSize);
35873                 break;                
35874             }
35875         }
35876     },
35877     
35878     getBox : function(){
35879         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35880     },
35881     
35882     getMargins : function(){
35883         return this.margins;
35884     },
35885     
35886     updateBox : function(box){
35887         this.box = box;
35888         var el = this.activePanel.getEl();
35889         el.dom.style.left = box.x + "px";
35890         el.dom.style.top = box.y + "px";
35891         this.activePanel.setSize(box.width, box.height);
35892     },
35893     
35894     /**
35895      * Returns the container element for this region.
35896      * @return {Roo.Element}
35897      */
35898     getEl : function(){
35899         return this.activePanel;
35900     },
35901     
35902     /**
35903      * Returns true if this region is currently visible.
35904      * @return {Boolean}
35905      */
35906     isVisible : function(){
35907         return this.activePanel ? true : false;
35908     },
35909     
35910     setActivePanel : function(panel){
35911         panel = this.getPanel(panel);
35912         if(this.activePanel && this.activePanel != panel){
35913             this.activePanel.setActiveState(false);
35914             this.activePanel.getEl().setLeftTop(-10000,-10000);
35915         }
35916         this.activePanel = panel;
35917         panel.setActiveState(true);
35918         if(this.box){
35919             panel.setSize(this.box.width, this.box.height);
35920         }
35921         this.fireEvent("panelactivated", this, panel);
35922         this.fireEvent("invalidated");
35923     },
35924     
35925     /**
35926      * Show the specified panel.
35927      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35928      * @return {Roo.ContentPanel} The shown panel or null
35929      */
35930     showPanel : function(panel){
35931         panel = this.getPanel(panel);
35932         if(panel){
35933             this.setActivePanel(panel);
35934         }
35935         return panel;
35936     },
35937     
35938     /**
35939      * Get the active panel for this region.
35940      * @return {Roo.ContentPanel} The active panel or null
35941      */
35942     getActivePanel : function(){
35943         return this.activePanel;
35944     },
35945     
35946     /**
35947      * Add the passed ContentPanel(s)
35948      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35949      * @return {Roo.ContentPanel} The panel added (if only one was added)
35950      */
35951     add : function(panel){
35952         if(arguments.length > 1){
35953             for(var i = 0, len = arguments.length; i < len; i++) {
35954                 this.add(arguments[i]);
35955             }
35956             return null;
35957         }
35958         if(this.hasPanel(panel)){
35959             this.showPanel(panel);
35960             return panel;
35961         }
35962         var el = panel.getEl();
35963         if(el.dom.parentNode != this.mgr.el.dom){
35964             this.mgr.el.dom.appendChild(el.dom);
35965         }
35966         if(panel.setRegion){
35967             panel.setRegion(this);
35968         }
35969         this.panels.add(panel);
35970         el.setStyle("position", "absolute");
35971         if(!panel.background){
35972             this.setActivePanel(panel);
35973             if(this.config.initialSize && this.panels.getCount()==1){
35974                 this.resizeTo(this.config.initialSize);
35975             }
35976         }
35977         this.fireEvent("paneladded", this, panel);
35978         return panel;
35979     },
35980     
35981     /**
35982      * Returns true if the panel is in this region.
35983      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35984      * @return {Boolean}
35985      */
35986     hasPanel : function(panel){
35987         if(typeof panel == "object"){ // must be panel obj
35988             panel = panel.getId();
35989         }
35990         return this.getPanel(panel) ? true : false;
35991     },
35992     
35993     /**
35994      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35995      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35996      * @param {Boolean} preservePanel Overrides the config preservePanel option
35997      * @return {Roo.ContentPanel} The panel that was removed
35998      */
35999     remove : function(panel, preservePanel){
36000         panel = this.getPanel(panel);
36001         if(!panel){
36002             return null;
36003         }
36004         var e = {};
36005         this.fireEvent("beforeremove", this, panel, e);
36006         if(e.cancel === true){
36007             return null;
36008         }
36009         var panelId = panel.getId();
36010         this.panels.removeKey(panelId);
36011         return panel;
36012     },
36013     
36014     /**
36015      * Returns the panel specified or null if it's not in this region.
36016      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36017      * @return {Roo.ContentPanel}
36018      */
36019     getPanel : function(id){
36020         if(typeof id == "object"){ // must be panel obj
36021             return id;
36022         }
36023         return this.panels.get(id);
36024     },
36025     
36026     /**
36027      * Returns this regions position (north/south/east/west/center).
36028      * @return {String} 
36029      */
36030     getPosition: function(){
36031         return this.position;    
36032     }
36033 });/*
36034  * Based on:
36035  * Ext JS Library 1.1.1
36036  * Copyright(c) 2006-2007, Ext JS, LLC.
36037  *
36038  * Originally Released Under LGPL - original licence link has changed is not relivant.
36039  *
36040  * Fork - LGPL
36041  * <script type="text/javascript">
36042  */
36043  
36044 /**
36045  * @class Roo.bootstrap.layout.Region
36046  * @extends Roo.bootstrap.layout.Basic
36047  * This class represents a region in a layout manager.
36048  
36049  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36050  * @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})
36051  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36052  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36053  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36054  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36055  * @cfg {String}    title           The title for the region (overrides panel titles)
36056  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36057  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36058  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36059  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36060  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36061  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36062  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36063  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36064  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36065  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36066
36067  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36068  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36069  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36070  * @cfg {Number}    width           For East/West panels
36071  * @cfg {Number}    height          For North/South panels
36072  * @cfg {Boolean}   split           To show the splitter
36073  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36074  * 
36075  * @cfg {string}   cls             Extra CSS classes to add to region
36076  * 
36077  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36078  * @cfg {string}   region  the region that it inhabits..
36079  *
36080
36081  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36082  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36083
36084  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36085  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36086  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36087  */
36088 Roo.bootstrap.layout.Region = function(config)
36089 {
36090     this.applyConfig(config);
36091
36092     var mgr = config.mgr;
36093     var pos = config.region;
36094     config.skipConfig = true;
36095     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36096     
36097     if (mgr.el) {
36098         this.onRender(mgr.el);   
36099     }
36100      
36101     this.visible = true;
36102     this.collapsed = false;
36103     this.unrendered_panels = [];
36104 };
36105
36106 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36107
36108     position: '', // set by wrapper (eg. north/south etc..)
36109     unrendered_panels : null,  // unrendered panels.
36110     
36111     tabPosition : false,
36112     
36113     mgr: false, // points to 'Border'
36114     
36115     
36116     createBody : function(){
36117         /** This region's body element 
36118         * @type Roo.Element */
36119         this.bodyEl = this.el.createChild({
36120                 tag: "div",
36121                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36122         });
36123     },
36124
36125     onRender: function(ctr, pos)
36126     {
36127         var dh = Roo.DomHelper;
36128         /** This region's container element 
36129         * @type Roo.Element */
36130         this.el = dh.append(ctr.dom, {
36131                 tag: "div",
36132                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36133             }, true);
36134         /** This region's title element 
36135         * @type Roo.Element */
36136     
36137         this.titleEl = dh.append(this.el.dom,  {
36138                 tag: "div",
36139                 unselectable: "on",
36140                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36141                 children:[
36142                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36143                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36144                 ]
36145             }, true);
36146         
36147         this.titleEl.enableDisplayMode();
36148         /** This region's title text element 
36149         * @type HTMLElement */
36150         this.titleTextEl = this.titleEl.dom.firstChild;
36151         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36152         /*
36153         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36154         this.closeBtn.enableDisplayMode();
36155         this.closeBtn.on("click", this.closeClicked, this);
36156         this.closeBtn.hide();
36157     */
36158         this.createBody(this.config);
36159         if(this.config.hideWhenEmpty){
36160             this.hide();
36161             this.on("paneladded", this.validateVisibility, this);
36162             this.on("panelremoved", this.validateVisibility, this);
36163         }
36164         if(this.autoScroll){
36165             this.bodyEl.setStyle("overflow", "auto");
36166         }else{
36167             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36168         }
36169         //if(c.titlebar !== false){
36170             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36171                 this.titleEl.hide();
36172             }else{
36173                 this.titleEl.show();
36174                 if(this.config.title){
36175                     this.titleTextEl.innerHTML = this.config.title;
36176                 }
36177             }
36178         //}
36179         if(this.config.collapsed){
36180             this.collapse(true);
36181         }
36182         if(this.config.hidden){
36183             this.hide();
36184         }
36185         
36186         if (this.unrendered_panels && this.unrendered_panels.length) {
36187             for (var i =0;i< this.unrendered_panels.length; i++) {
36188                 this.add(this.unrendered_panels[i]);
36189             }
36190             this.unrendered_panels = null;
36191             
36192         }
36193         
36194     },
36195     
36196     applyConfig : function(c)
36197     {
36198         /*
36199          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36200             var dh = Roo.DomHelper;
36201             if(c.titlebar !== false){
36202                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36203                 this.collapseBtn.on("click", this.collapse, this);
36204                 this.collapseBtn.enableDisplayMode();
36205                 /*
36206                 if(c.showPin === true || this.showPin){
36207                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36208                     this.stickBtn.enableDisplayMode();
36209                     this.stickBtn.on("click", this.expand, this);
36210                     this.stickBtn.hide();
36211                 }
36212                 
36213             }
36214             */
36215             /** This region's collapsed element
36216             * @type Roo.Element */
36217             /*
36218              *
36219             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36220                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36221             ]}, true);
36222             
36223             if(c.floatable !== false){
36224                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36225                this.collapsedEl.on("click", this.collapseClick, this);
36226             }
36227
36228             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36229                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36230                    id: "message", unselectable: "on", style:{"float":"left"}});
36231                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36232              }
36233             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36234             this.expandBtn.on("click", this.expand, this);
36235             
36236         }
36237         
36238         if(this.collapseBtn){
36239             this.collapseBtn.setVisible(c.collapsible == true);
36240         }
36241         
36242         this.cmargins = c.cmargins || this.cmargins ||
36243                          (this.position == "west" || this.position == "east" ?
36244                              {top: 0, left: 2, right:2, bottom: 0} :
36245                              {top: 2, left: 0, right:0, bottom: 2});
36246         */
36247         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36248         
36249         
36250         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36251         
36252         this.autoScroll = c.autoScroll || false;
36253         
36254         
36255        
36256         
36257         this.duration = c.duration || .30;
36258         this.slideDuration = c.slideDuration || .45;
36259         this.config = c;
36260        
36261     },
36262     /**
36263      * Returns true if this region is currently visible.
36264      * @return {Boolean}
36265      */
36266     isVisible : function(){
36267         return this.visible;
36268     },
36269
36270     /**
36271      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36272      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36273      */
36274     //setCollapsedTitle : function(title){
36275     //    title = title || "&#160;";
36276      //   if(this.collapsedTitleTextEl){
36277       //      this.collapsedTitleTextEl.innerHTML = title;
36278        // }
36279     //},
36280
36281     getBox : function(){
36282         var b;
36283       //  if(!this.collapsed){
36284             b = this.el.getBox(false, true);
36285        // }else{
36286           //  b = this.collapsedEl.getBox(false, true);
36287         //}
36288         return b;
36289     },
36290
36291     getMargins : function(){
36292         return this.margins;
36293         //return this.collapsed ? this.cmargins : this.margins;
36294     },
36295 /*
36296     highlight : function(){
36297         this.el.addClass("x-layout-panel-dragover");
36298     },
36299
36300     unhighlight : function(){
36301         this.el.removeClass("x-layout-panel-dragover");
36302     },
36303 */
36304     updateBox : function(box)
36305     {
36306         if (!this.bodyEl) {
36307             return; // not rendered yet..
36308         }
36309         
36310         this.box = box;
36311         if(!this.collapsed){
36312             this.el.dom.style.left = box.x + "px";
36313             this.el.dom.style.top = box.y + "px";
36314             this.updateBody(box.width, box.height);
36315         }else{
36316             this.collapsedEl.dom.style.left = box.x + "px";
36317             this.collapsedEl.dom.style.top = box.y + "px";
36318             this.collapsedEl.setSize(box.width, box.height);
36319         }
36320         if(this.tabs){
36321             this.tabs.autoSizeTabs();
36322         }
36323     },
36324
36325     updateBody : function(w, h)
36326     {
36327         if(w !== null){
36328             this.el.setWidth(w);
36329             w -= this.el.getBorderWidth("rl");
36330             if(this.config.adjustments){
36331                 w += this.config.adjustments[0];
36332             }
36333         }
36334         if(h !== null && h > 0){
36335             this.el.setHeight(h);
36336             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36337             h -= this.el.getBorderWidth("tb");
36338             if(this.config.adjustments){
36339                 h += this.config.adjustments[1];
36340             }
36341             this.bodyEl.setHeight(h);
36342             if(this.tabs){
36343                 h = this.tabs.syncHeight(h);
36344             }
36345         }
36346         if(this.panelSize){
36347             w = w !== null ? w : this.panelSize.width;
36348             h = h !== null ? h : this.panelSize.height;
36349         }
36350         if(this.activePanel){
36351             var el = this.activePanel.getEl();
36352             w = w !== null ? w : el.getWidth();
36353             h = h !== null ? h : el.getHeight();
36354             this.panelSize = {width: w, height: h};
36355             this.activePanel.setSize(w, h);
36356         }
36357         if(Roo.isIE && this.tabs){
36358             this.tabs.el.repaint();
36359         }
36360     },
36361
36362     /**
36363      * Returns the container element for this region.
36364      * @return {Roo.Element}
36365      */
36366     getEl : function(){
36367         return this.el;
36368     },
36369
36370     /**
36371      * Hides this region.
36372      */
36373     hide : function(){
36374         //if(!this.collapsed){
36375             this.el.dom.style.left = "-2000px";
36376             this.el.hide();
36377         //}else{
36378          //   this.collapsedEl.dom.style.left = "-2000px";
36379          //   this.collapsedEl.hide();
36380        // }
36381         this.visible = false;
36382         this.fireEvent("visibilitychange", this, false);
36383     },
36384
36385     /**
36386      * Shows this region if it was previously hidden.
36387      */
36388     show : function(){
36389         //if(!this.collapsed){
36390             this.el.show();
36391         //}else{
36392         //    this.collapsedEl.show();
36393        // }
36394         this.visible = true;
36395         this.fireEvent("visibilitychange", this, true);
36396     },
36397 /*
36398     closeClicked : function(){
36399         if(this.activePanel){
36400             this.remove(this.activePanel);
36401         }
36402     },
36403
36404     collapseClick : function(e){
36405         if(this.isSlid){
36406            e.stopPropagation();
36407            this.slideIn();
36408         }else{
36409            e.stopPropagation();
36410            this.slideOut();
36411         }
36412     },
36413 */
36414     /**
36415      * Collapses this region.
36416      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36417      */
36418     /*
36419     collapse : function(skipAnim, skipCheck = false){
36420         if(this.collapsed) {
36421             return;
36422         }
36423         
36424         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36425             
36426             this.collapsed = true;
36427             if(this.split){
36428                 this.split.el.hide();
36429             }
36430             if(this.config.animate && skipAnim !== true){
36431                 this.fireEvent("invalidated", this);
36432                 this.animateCollapse();
36433             }else{
36434                 this.el.setLocation(-20000,-20000);
36435                 this.el.hide();
36436                 this.collapsedEl.show();
36437                 this.fireEvent("collapsed", this);
36438                 this.fireEvent("invalidated", this);
36439             }
36440         }
36441         
36442     },
36443 */
36444     animateCollapse : function(){
36445         // overridden
36446     },
36447
36448     /**
36449      * Expands this region if it was previously collapsed.
36450      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36451      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36452      */
36453     /*
36454     expand : function(e, skipAnim){
36455         if(e) {
36456             e.stopPropagation();
36457         }
36458         if(!this.collapsed || this.el.hasActiveFx()) {
36459             return;
36460         }
36461         if(this.isSlid){
36462             this.afterSlideIn();
36463             skipAnim = true;
36464         }
36465         this.collapsed = false;
36466         if(this.config.animate && skipAnim !== true){
36467             this.animateExpand();
36468         }else{
36469             this.el.show();
36470             if(this.split){
36471                 this.split.el.show();
36472             }
36473             this.collapsedEl.setLocation(-2000,-2000);
36474             this.collapsedEl.hide();
36475             this.fireEvent("invalidated", this);
36476             this.fireEvent("expanded", this);
36477         }
36478     },
36479 */
36480     animateExpand : function(){
36481         // overridden
36482     },
36483
36484     initTabs : function()
36485     {
36486         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36487         
36488         var ts = new Roo.bootstrap.panel.Tabs({
36489             el: this.bodyEl.dom,
36490             region : this,
36491             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36492             disableTooltips: this.config.disableTabTips,
36493             toolbar : this.config.toolbar
36494         });
36495         
36496         if(this.config.hideTabs){
36497             ts.stripWrap.setDisplayed(false);
36498         }
36499         this.tabs = ts;
36500         ts.resizeTabs = this.config.resizeTabs === true;
36501         ts.minTabWidth = this.config.minTabWidth || 40;
36502         ts.maxTabWidth = this.config.maxTabWidth || 250;
36503         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36504         ts.monitorResize = false;
36505         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36506         ts.bodyEl.addClass('roo-layout-tabs-body');
36507         this.panels.each(this.initPanelAsTab, this);
36508     },
36509
36510     initPanelAsTab : function(panel){
36511         var ti = this.tabs.addTab(
36512             panel.getEl().id,
36513             panel.getTitle(),
36514             null,
36515             this.config.closeOnTab && panel.isClosable(),
36516             panel.tpl
36517         );
36518         if(panel.tabTip !== undefined){
36519             ti.setTooltip(panel.tabTip);
36520         }
36521         ti.on("activate", function(){
36522               this.setActivePanel(panel);
36523         }, this);
36524         
36525         if(this.config.closeOnTab){
36526             ti.on("beforeclose", function(t, e){
36527                 e.cancel = true;
36528                 this.remove(panel);
36529             }, this);
36530         }
36531         
36532         panel.tabItem = ti;
36533         
36534         return ti;
36535     },
36536
36537     updatePanelTitle : function(panel, title)
36538     {
36539         if(this.activePanel == panel){
36540             this.updateTitle(title);
36541         }
36542         if(this.tabs){
36543             var ti = this.tabs.getTab(panel.getEl().id);
36544             ti.setText(title);
36545             if(panel.tabTip !== undefined){
36546                 ti.setTooltip(panel.tabTip);
36547             }
36548         }
36549     },
36550
36551     updateTitle : function(title){
36552         if(this.titleTextEl && !this.config.title){
36553             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36554         }
36555     },
36556
36557     setActivePanel : function(panel)
36558     {
36559         panel = this.getPanel(panel);
36560         if(this.activePanel && this.activePanel != panel){
36561             if(this.activePanel.setActiveState(false) === false){
36562                 return;
36563             }
36564         }
36565         this.activePanel = panel;
36566         panel.setActiveState(true);
36567         if(this.panelSize){
36568             panel.setSize(this.panelSize.width, this.panelSize.height);
36569         }
36570         if(this.closeBtn){
36571             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36572         }
36573         this.updateTitle(panel.getTitle());
36574         if(this.tabs){
36575             this.fireEvent("invalidated", this);
36576         }
36577         this.fireEvent("panelactivated", this, panel);
36578     },
36579
36580     /**
36581      * Shows the specified panel.
36582      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36583      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36584      */
36585     showPanel : function(panel)
36586     {
36587         panel = this.getPanel(panel);
36588         if(panel){
36589             if(this.tabs){
36590                 var tab = this.tabs.getTab(panel.getEl().id);
36591                 if(tab.isHidden()){
36592                     this.tabs.unhideTab(tab.id);
36593                 }
36594                 tab.activate();
36595             }else{
36596                 this.setActivePanel(panel);
36597             }
36598         }
36599         return panel;
36600     },
36601
36602     /**
36603      * Get the active panel for this region.
36604      * @return {Roo.ContentPanel} The active panel or null
36605      */
36606     getActivePanel : function(){
36607         return this.activePanel;
36608     },
36609
36610     validateVisibility : function(){
36611         if(this.panels.getCount() < 1){
36612             this.updateTitle("&#160;");
36613             this.closeBtn.hide();
36614             this.hide();
36615         }else{
36616             if(!this.isVisible()){
36617                 this.show();
36618             }
36619         }
36620     },
36621
36622     /**
36623      * Adds the passed ContentPanel(s) to this region.
36624      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36625      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36626      */
36627     add : function(panel)
36628     {
36629         if(arguments.length > 1){
36630             for(var i = 0, len = arguments.length; i < len; i++) {
36631                 this.add(arguments[i]);
36632             }
36633             return null;
36634         }
36635         
36636         // if we have not been rendered yet, then we can not really do much of this..
36637         if (!this.bodyEl) {
36638             this.unrendered_panels.push(panel);
36639             return panel;
36640         }
36641         
36642         
36643         
36644         
36645         if(this.hasPanel(panel)){
36646             this.showPanel(panel);
36647             return panel;
36648         }
36649         panel.setRegion(this);
36650         this.panels.add(panel);
36651        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36652             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36653             // and hide them... ???
36654             this.bodyEl.dom.appendChild(panel.getEl().dom);
36655             if(panel.background !== true){
36656                 this.setActivePanel(panel);
36657             }
36658             this.fireEvent("paneladded", this, panel);
36659             return panel;
36660         }
36661         */
36662         if(!this.tabs){
36663             this.initTabs();
36664         }else{
36665             this.initPanelAsTab(panel);
36666         }
36667         
36668         
36669         if(panel.background !== true){
36670             this.tabs.activate(panel.getEl().id);
36671         }
36672         this.fireEvent("paneladded", this, panel);
36673         return panel;
36674     },
36675
36676     /**
36677      * Hides the tab for the specified panel.
36678      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36679      */
36680     hidePanel : function(panel){
36681         if(this.tabs && (panel = this.getPanel(panel))){
36682             this.tabs.hideTab(panel.getEl().id);
36683         }
36684     },
36685
36686     /**
36687      * Unhides the tab for a previously hidden panel.
36688      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36689      */
36690     unhidePanel : function(panel){
36691         if(this.tabs && (panel = this.getPanel(panel))){
36692             this.tabs.unhideTab(panel.getEl().id);
36693         }
36694     },
36695
36696     clearPanels : function(){
36697         while(this.panels.getCount() > 0){
36698              this.remove(this.panels.first());
36699         }
36700     },
36701
36702     /**
36703      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36704      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36705      * @param {Boolean} preservePanel Overrides the config preservePanel option
36706      * @return {Roo.ContentPanel} The panel that was removed
36707      */
36708     remove : function(panel, preservePanel)
36709     {
36710         panel = this.getPanel(panel);
36711         if(!panel){
36712             return null;
36713         }
36714         var e = {};
36715         this.fireEvent("beforeremove", this, panel, e);
36716         if(e.cancel === true){
36717             return null;
36718         }
36719         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36720         var panelId = panel.getId();
36721         this.panels.removeKey(panelId);
36722         if(preservePanel){
36723             document.body.appendChild(panel.getEl().dom);
36724         }
36725         if(this.tabs){
36726             this.tabs.removeTab(panel.getEl().id);
36727         }else if (!preservePanel){
36728             this.bodyEl.dom.removeChild(panel.getEl().dom);
36729         }
36730         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36731             var p = this.panels.first();
36732             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36733             tempEl.appendChild(p.getEl().dom);
36734             this.bodyEl.update("");
36735             this.bodyEl.dom.appendChild(p.getEl().dom);
36736             tempEl = null;
36737             this.updateTitle(p.getTitle());
36738             this.tabs = null;
36739             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36740             this.setActivePanel(p);
36741         }
36742         panel.setRegion(null);
36743         if(this.activePanel == panel){
36744             this.activePanel = null;
36745         }
36746         if(this.config.autoDestroy !== false && preservePanel !== true){
36747             try{panel.destroy();}catch(e){}
36748         }
36749         this.fireEvent("panelremoved", this, panel);
36750         return panel;
36751     },
36752
36753     /**
36754      * Returns the TabPanel component used by this region
36755      * @return {Roo.TabPanel}
36756      */
36757     getTabs : function(){
36758         return this.tabs;
36759     },
36760
36761     createTool : function(parentEl, className){
36762         var btn = Roo.DomHelper.append(parentEl, {
36763             tag: "div",
36764             cls: "x-layout-tools-button",
36765             children: [ {
36766                 tag: "div",
36767                 cls: "roo-layout-tools-button-inner " + className,
36768                 html: "&#160;"
36769             }]
36770         }, true);
36771         btn.addClassOnOver("roo-layout-tools-button-over");
36772         return btn;
36773     }
36774 });/*
36775  * Based on:
36776  * Ext JS Library 1.1.1
36777  * Copyright(c) 2006-2007, Ext JS, LLC.
36778  *
36779  * Originally Released Under LGPL - original licence link has changed is not relivant.
36780  *
36781  * Fork - LGPL
36782  * <script type="text/javascript">
36783  */
36784  
36785
36786
36787 /**
36788  * @class Roo.SplitLayoutRegion
36789  * @extends Roo.LayoutRegion
36790  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36791  */
36792 Roo.bootstrap.layout.Split = function(config){
36793     this.cursor = config.cursor;
36794     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36795 };
36796
36797 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36798 {
36799     splitTip : "Drag to resize.",
36800     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36801     useSplitTips : false,
36802
36803     applyConfig : function(config){
36804         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36805     },
36806     
36807     onRender : function(ctr,pos) {
36808         
36809         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36810         if(!this.config.split){
36811             return;
36812         }
36813         if(!this.split){
36814             
36815             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36816                             tag: "div",
36817                             id: this.el.id + "-split",
36818                             cls: "roo-layout-split roo-layout-split-"+this.position,
36819                             html: "&#160;"
36820             });
36821             /** The SplitBar for this region 
36822             * @type Roo.SplitBar */
36823             // does not exist yet...
36824             Roo.log([this.position, this.orientation]);
36825             
36826             this.split = new Roo.bootstrap.SplitBar({
36827                 dragElement : splitEl,
36828                 resizingElement: this.el,
36829                 orientation : this.orientation
36830             });
36831             
36832             this.split.on("moved", this.onSplitMove, this);
36833             this.split.useShim = this.config.useShim === true;
36834             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36835             if(this.useSplitTips){
36836                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36837             }
36838             //if(config.collapsible){
36839             //    this.split.el.on("dblclick", this.collapse,  this);
36840             //}
36841         }
36842         if(typeof this.config.minSize != "undefined"){
36843             this.split.minSize = this.config.minSize;
36844         }
36845         if(typeof this.config.maxSize != "undefined"){
36846             this.split.maxSize = this.config.maxSize;
36847         }
36848         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36849             this.hideSplitter();
36850         }
36851         
36852     },
36853
36854     getHMaxSize : function(){
36855          var cmax = this.config.maxSize || 10000;
36856          var center = this.mgr.getRegion("center");
36857          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36858     },
36859
36860     getVMaxSize : function(){
36861          var cmax = this.config.maxSize || 10000;
36862          var center = this.mgr.getRegion("center");
36863          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36864     },
36865
36866     onSplitMove : function(split, newSize){
36867         this.fireEvent("resized", this, newSize);
36868     },
36869     
36870     /** 
36871      * Returns the {@link Roo.SplitBar} for this region.
36872      * @return {Roo.SplitBar}
36873      */
36874     getSplitBar : function(){
36875         return this.split;
36876     },
36877     
36878     hide : function(){
36879         this.hideSplitter();
36880         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36881     },
36882
36883     hideSplitter : function(){
36884         if(this.split){
36885             this.split.el.setLocation(-2000,-2000);
36886             this.split.el.hide();
36887         }
36888     },
36889
36890     show : function(){
36891         if(this.split){
36892             this.split.el.show();
36893         }
36894         Roo.bootstrap.layout.Split.superclass.show.call(this);
36895     },
36896     
36897     beforeSlide: function(){
36898         if(Roo.isGecko){// firefox overflow auto bug workaround
36899             this.bodyEl.clip();
36900             if(this.tabs) {
36901                 this.tabs.bodyEl.clip();
36902             }
36903             if(this.activePanel){
36904                 this.activePanel.getEl().clip();
36905                 
36906                 if(this.activePanel.beforeSlide){
36907                     this.activePanel.beforeSlide();
36908                 }
36909             }
36910         }
36911     },
36912     
36913     afterSlide : function(){
36914         if(Roo.isGecko){// firefox overflow auto bug workaround
36915             this.bodyEl.unclip();
36916             if(this.tabs) {
36917                 this.tabs.bodyEl.unclip();
36918             }
36919             if(this.activePanel){
36920                 this.activePanel.getEl().unclip();
36921                 if(this.activePanel.afterSlide){
36922                     this.activePanel.afterSlide();
36923                 }
36924             }
36925         }
36926     },
36927
36928     initAutoHide : function(){
36929         if(this.autoHide !== false){
36930             if(!this.autoHideHd){
36931                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36932                 this.autoHideHd = {
36933                     "mouseout": function(e){
36934                         if(!e.within(this.el, true)){
36935                             st.delay(500);
36936                         }
36937                     },
36938                     "mouseover" : function(e){
36939                         st.cancel();
36940                     },
36941                     scope : this
36942                 };
36943             }
36944             this.el.on(this.autoHideHd);
36945         }
36946     },
36947
36948     clearAutoHide : function(){
36949         if(this.autoHide !== false){
36950             this.el.un("mouseout", this.autoHideHd.mouseout);
36951             this.el.un("mouseover", this.autoHideHd.mouseover);
36952         }
36953     },
36954
36955     clearMonitor : function(){
36956         Roo.get(document).un("click", this.slideInIf, this);
36957     },
36958
36959     // these names are backwards but not changed for compat
36960     slideOut : function(){
36961         if(this.isSlid || this.el.hasActiveFx()){
36962             return;
36963         }
36964         this.isSlid = true;
36965         if(this.collapseBtn){
36966             this.collapseBtn.hide();
36967         }
36968         this.closeBtnState = this.closeBtn.getStyle('display');
36969         this.closeBtn.hide();
36970         if(this.stickBtn){
36971             this.stickBtn.show();
36972         }
36973         this.el.show();
36974         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36975         this.beforeSlide();
36976         this.el.setStyle("z-index", 10001);
36977         this.el.slideIn(this.getSlideAnchor(), {
36978             callback: function(){
36979                 this.afterSlide();
36980                 this.initAutoHide();
36981                 Roo.get(document).on("click", this.slideInIf, this);
36982                 this.fireEvent("slideshow", this);
36983             },
36984             scope: this,
36985             block: true
36986         });
36987     },
36988
36989     afterSlideIn : function(){
36990         this.clearAutoHide();
36991         this.isSlid = false;
36992         this.clearMonitor();
36993         this.el.setStyle("z-index", "");
36994         if(this.collapseBtn){
36995             this.collapseBtn.show();
36996         }
36997         this.closeBtn.setStyle('display', this.closeBtnState);
36998         if(this.stickBtn){
36999             this.stickBtn.hide();
37000         }
37001         this.fireEvent("slidehide", this);
37002     },
37003
37004     slideIn : function(cb){
37005         if(!this.isSlid || this.el.hasActiveFx()){
37006             Roo.callback(cb);
37007             return;
37008         }
37009         this.isSlid = false;
37010         this.beforeSlide();
37011         this.el.slideOut(this.getSlideAnchor(), {
37012             callback: function(){
37013                 this.el.setLeftTop(-10000, -10000);
37014                 this.afterSlide();
37015                 this.afterSlideIn();
37016                 Roo.callback(cb);
37017             },
37018             scope: this,
37019             block: true
37020         });
37021     },
37022     
37023     slideInIf : function(e){
37024         if(!e.within(this.el)){
37025             this.slideIn();
37026         }
37027     },
37028
37029     animateCollapse : function(){
37030         this.beforeSlide();
37031         this.el.setStyle("z-index", 20000);
37032         var anchor = this.getSlideAnchor();
37033         this.el.slideOut(anchor, {
37034             callback : function(){
37035                 this.el.setStyle("z-index", "");
37036                 this.collapsedEl.slideIn(anchor, {duration:.3});
37037                 this.afterSlide();
37038                 this.el.setLocation(-10000,-10000);
37039                 this.el.hide();
37040                 this.fireEvent("collapsed", this);
37041             },
37042             scope: this,
37043             block: true
37044         });
37045     },
37046
37047     animateExpand : function(){
37048         this.beforeSlide();
37049         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37050         this.el.setStyle("z-index", 20000);
37051         this.collapsedEl.hide({
37052             duration:.1
37053         });
37054         this.el.slideIn(this.getSlideAnchor(), {
37055             callback : function(){
37056                 this.el.setStyle("z-index", "");
37057                 this.afterSlide();
37058                 if(this.split){
37059                     this.split.el.show();
37060                 }
37061                 this.fireEvent("invalidated", this);
37062                 this.fireEvent("expanded", this);
37063             },
37064             scope: this,
37065             block: true
37066         });
37067     },
37068
37069     anchors : {
37070         "west" : "left",
37071         "east" : "right",
37072         "north" : "top",
37073         "south" : "bottom"
37074     },
37075
37076     sanchors : {
37077         "west" : "l",
37078         "east" : "r",
37079         "north" : "t",
37080         "south" : "b"
37081     },
37082
37083     canchors : {
37084         "west" : "tl-tr",
37085         "east" : "tr-tl",
37086         "north" : "tl-bl",
37087         "south" : "bl-tl"
37088     },
37089
37090     getAnchor : function(){
37091         return this.anchors[this.position];
37092     },
37093
37094     getCollapseAnchor : function(){
37095         return this.canchors[this.position];
37096     },
37097
37098     getSlideAnchor : function(){
37099         return this.sanchors[this.position];
37100     },
37101
37102     getAlignAdj : function(){
37103         var cm = this.cmargins;
37104         switch(this.position){
37105             case "west":
37106                 return [0, 0];
37107             break;
37108             case "east":
37109                 return [0, 0];
37110             break;
37111             case "north":
37112                 return [0, 0];
37113             break;
37114             case "south":
37115                 return [0, 0];
37116             break;
37117         }
37118     },
37119
37120     getExpandAdj : function(){
37121         var c = this.collapsedEl, cm = this.cmargins;
37122         switch(this.position){
37123             case "west":
37124                 return [-(cm.right+c.getWidth()+cm.left), 0];
37125             break;
37126             case "east":
37127                 return [cm.right+c.getWidth()+cm.left, 0];
37128             break;
37129             case "north":
37130                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37131             break;
37132             case "south":
37133                 return [0, cm.top+cm.bottom+c.getHeight()];
37134             break;
37135         }
37136     }
37137 });/*
37138  * Based on:
37139  * Ext JS Library 1.1.1
37140  * Copyright(c) 2006-2007, Ext JS, LLC.
37141  *
37142  * Originally Released Under LGPL - original licence link has changed is not relivant.
37143  *
37144  * Fork - LGPL
37145  * <script type="text/javascript">
37146  */
37147 /*
37148  * These classes are private internal classes
37149  */
37150 Roo.bootstrap.layout.Center = function(config){
37151     config.region = "center";
37152     Roo.bootstrap.layout.Region.call(this, config);
37153     this.visible = true;
37154     this.minWidth = config.minWidth || 20;
37155     this.minHeight = config.minHeight || 20;
37156 };
37157
37158 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37159     hide : function(){
37160         // center panel can't be hidden
37161     },
37162     
37163     show : function(){
37164         // center panel can't be hidden
37165     },
37166     
37167     getMinWidth: function(){
37168         return this.minWidth;
37169     },
37170     
37171     getMinHeight: function(){
37172         return this.minHeight;
37173     }
37174 });
37175
37176
37177
37178
37179  
37180
37181
37182
37183
37184
37185
37186 Roo.bootstrap.layout.North = function(config)
37187 {
37188     config.region = 'north';
37189     config.cursor = 'n-resize';
37190     
37191     Roo.bootstrap.layout.Split.call(this, config);
37192     
37193     
37194     if(this.split){
37195         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37196         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37197         this.split.el.addClass("roo-layout-split-v");
37198     }
37199     var size = config.initialSize || config.height;
37200     if(typeof size != "undefined"){
37201         this.el.setHeight(size);
37202     }
37203 };
37204 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37205 {
37206     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37207     
37208     
37209     
37210     getBox : function(){
37211         if(this.collapsed){
37212             return this.collapsedEl.getBox();
37213         }
37214         var box = this.el.getBox();
37215         if(this.split){
37216             box.height += this.split.el.getHeight();
37217         }
37218         return box;
37219     },
37220     
37221     updateBox : function(box){
37222         if(this.split && !this.collapsed){
37223             box.height -= this.split.el.getHeight();
37224             this.split.el.setLeft(box.x);
37225             this.split.el.setTop(box.y+box.height);
37226             this.split.el.setWidth(box.width);
37227         }
37228         if(this.collapsed){
37229             this.updateBody(box.width, null);
37230         }
37231         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37232     }
37233 });
37234
37235
37236
37237
37238
37239 Roo.bootstrap.layout.South = function(config){
37240     config.region = 'south';
37241     config.cursor = 's-resize';
37242     Roo.bootstrap.layout.Split.call(this, config);
37243     if(this.split){
37244         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37245         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37246         this.split.el.addClass("roo-layout-split-v");
37247     }
37248     var size = config.initialSize || config.height;
37249     if(typeof size != "undefined"){
37250         this.el.setHeight(size);
37251     }
37252 };
37253
37254 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37255     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37256     getBox : function(){
37257         if(this.collapsed){
37258             return this.collapsedEl.getBox();
37259         }
37260         var box = this.el.getBox();
37261         if(this.split){
37262             var sh = this.split.el.getHeight();
37263             box.height += sh;
37264             box.y -= sh;
37265         }
37266         return box;
37267     },
37268     
37269     updateBox : function(box){
37270         if(this.split && !this.collapsed){
37271             var sh = this.split.el.getHeight();
37272             box.height -= sh;
37273             box.y += sh;
37274             this.split.el.setLeft(box.x);
37275             this.split.el.setTop(box.y-sh);
37276             this.split.el.setWidth(box.width);
37277         }
37278         if(this.collapsed){
37279             this.updateBody(box.width, null);
37280         }
37281         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37282     }
37283 });
37284
37285 Roo.bootstrap.layout.East = function(config){
37286     config.region = "east";
37287     config.cursor = "e-resize";
37288     Roo.bootstrap.layout.Split.call(this, config);
37289     if(this.split){
37290         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37291         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37292         this.split.el.addClass("roo-layout-split-h");
37293     }
37294     var size = config.initialSize || config.width;
37295     if(typeof size != "undefined"){
37296         this.el.setWidth(size);
37297     }
37298 };
37299 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37300     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37301     getBox : function(){
37302         if(this.collapsed){
37303             return this.collapsedEl.getBox();
37304         }
37305         var box = this.el.getBox();
37306         if(this.split){
37307             var sw = this.split.el.getWidth();
37308             box.width += sw;
37309             box.x -= sw;
37310         }
37311         return box;
37312     },
37313
37314     updateBox : function(box){
37315         if(this.split && !this.collapsed){
37316             var sw = this.split.el.getWidth();
37317             box.width -= sw;
37318             this.split.el.setLeft(box.x);
37319             this.split.el.setTop(box.y);
37320             this.split.el.setHeight(box.height);
37321             box.x += sw;
37322         }
37323         if(this.collapsed){
37324             this.updateBody(null, box.height);
37325         }
37326         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37327     }
37328 });
37329
37330 Roo.bootstrap.layout.West = function(config){
37331     config.region = "west";
37332     config.cursor = "w-resize";
37333     
37334     Roo.bootstrap.layout.Split.call(this, config);
37335     if(this.split){
37336         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37337         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37338         this.split.el.addClass("roo-layout-split-h");
37339     }
37340     
37341 };
37342 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37343     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37344     
37345     onRender: function(ctr, pos)
37346     {
37347         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37348         var size = this.config.initialSize || this.config.width;
37349         if(typeof size != "undefined"){
37350             this.el.setWidth(size);
37351         }
37352     },
37353     
37354     getBox : function(){
37355         if(this.collapsed){
37356             return this.collapsedEl.getBox();
37357         }
37358         var box = this.el.getBox();
37359         if(this.split){
37360             box.width += this.split.el.getWidth();
37361         }
37362         return box;
37363     },
37364     
37365     updateBox : function(box){
37366         if(this.split && !this.collapsed){
37367             var sw = this.split.el.getWidth();
37368             box.width -= sw;
37369             this.split.el.setLeft(box.x+box.width);
37370             this.split.el.setTop(box.y);
37371             this.split.el.setHeight(box.height);
37372         }
37373         if(this.collapsed){
37374             this.updateBody(null, box.height);
37375         }
37376         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37377     }
37378 });Roo.namespace("Roo.bootstrap.panel");/*
37379  * Based on:
37380  * Ext JS Library 1.1.1
37381  * Copyright(c) 2006-2007, Ext JS, LLC.
37382  *
37383  * Originally Released Under LGPL - original licence link has changed is not relivant.
37384  *
37385  * Fork - LGPL
37386  * <script type="text/javascript">
37387  */
37388 /**
37389  * @class Roo.ContentPanel
37390  * @extends Roo.util.Observable
37391  * A basic ContentPanel element.
37392  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37393  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37394  * @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
37395  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37396  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37397  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37398  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37399  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37400  * @cfg {String} title          The title for this panel
37401  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37402  * @cfg {String} url            Calls {@link #setUrl} with this value
37403  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37404  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37405  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37406  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37407  * @cfg {Boolean} badges render the badges
37408
37409  * @constructor
37410  * Create a new ContentPanel.
37411  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37412  * @param {String/Object} config A string to set only the title or a config object
37413  * @param {String} content (optional) Set the HTML content for this panel
37414  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37415  */
37416 Roo.bootstrap.panel.Content = function( config){
37417     
37418     this.tpl = config.tpl || false;
37419     
37420     var el = config.el;
37421     var content = config.content;
37422
37423     if(config.autoCreate){ // xtype is available if this is called from factory
37424         el = Roo.id();
37425     }
37426     this.el = Roo.get(el);
37427     if(!this.el && config && config.autoCreate){
37428         if(typeof config.autoCreate == "object"){
37429             if(!config.autoCreate.id){
37430                 config.autoCreate.id = config.id||el;
37431             }
37432             this.el = Roo.DomHelper.append(document.body,
37433                         config.autoCreate, true);
37434         }else{
37435             var elcfg =  {   tag: "div",
37436                             cls: "roo-layout-inactive-content",
37437                             id: config.id||el
37438                             };
37439             if (config.html) {
37440                 elcfg.html = config.html;
37441                 
37442             }
37443                         
37444             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37445         }
37446     } 
37447     this.closable = false;
37448     this.loaded = false;
37449     this.active = false;
37450    
37451       
37452     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37453         
37454         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37455         
37456         this.wrapEl = this.el; //this.el.wrap();
37457         var ti = [];
37458         if (config.toolbar.items) {
37459             ti = config.toolbar.items ;
37460             delete config.toolbar.items ;
37461         }
37462         
37463         var nitems = [];
37464         this.toolbar.render(this.wrapEl, 'before');
37465         for(var i =0;i < ti.length;i++) {
37466           //  Roo.log(['add child', items[i]]);
37467             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37468         }
37469         this.toolbar.items = nitems;
37470         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37471         delete config.toolbar;
37472         
37473     }
37474     /*
37475     // xtype created footer. - not sure if will work as we normally have to render first..
37476     if (this.footer && !this.footer.el && this.footer.xtype) {
37477         if (!this.wrapEl) {
37478             this.wrapEl = this.el.wrap();
37479         }
37480     
37481         this.footer.container = this.wrapEl.createChild();
37482          
37483         this.footer = Roo.factory(this.footer, Roo);
37484         
37485     }
37486     */
37487     
37488      if(typeof config == "string"){
37489         this.title = config;
37490     }else{
37491         Roo.apply(this, config);
37492     }
37493     
37494     if(this.resizeEl){
37495         this.resizeEl = Roo.get(this.resizeEl, true);
37496     }else{
37497         this.resizeEl = this.el;
37498     }
37499     // handle view.xtype
37500     
37501  
37502     
37503     
37504     this.addEvents({
37505         /**
37506          * @event activate
37507          * Fires when this panel is activated. 
37508          * @param {Roo.ContentPanel} this
37509          */
37510         "activate" : true,
37511         /**
37512          * @event deactivate
37513          * Fires when this panel is activated. 
37514          * @param {Roo.ContentPanel} this
37515          */
37516         "deactivate" : true,
37517
37518         /**
37519          * @event resize
37520          * Fires when this panel is resized if fitToFrame is true.
37521          * @param {Roo.ContentPanel} this
37522          * @param {Number} width The width after any component adjustments
37523          * @param {Number} height The height after any component adjustments
37524          */
37525         "resize" : true,
37526         
37527          /**
37528          * @event render
37529          * Fires when this tab is created
37530          * @param {Roo.ContentPanel} this
37531          */
37532         "render" : true
37533         
37534         
37535         
37536     });
37537     
37538
37539     
37540     
37541     if(this.autoScroll){
37542         this.resizeEl.setStyle("overflow", "auto");
37543     } else {
37544         // fix randome scrolling
37545         //this.el.on('scroll', function() {
37546         //    Roo.log('fix random scolling');
37547         //    this.scrollTo('top',0); 
37548         //});
37549     }
37550     content = content || this.content;
37551     if(content){
37552         this.setContent(content);
37553     }
37554     if(config && config.url){
37555         this.setUrl(this.url, this.params, this.loadOnce);
37556     }
37557     
37558     
37559     
37560     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37561     
37562     if (this.view && typeof(this.view.xtype) != 'undefined') {
37563         this.view.el = this.el.appendChild(document.createElement("div"));
37564         this.view = Roo.factory(this.view); 
37565         this.view.render  &&  this.view.render(false, '');  
37566     }
37567     
37568     
37569     this.fireEvent('render', this);
37570 };
37571
37572 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37573     
37574     tabTip : '',
37575     
37576     setRegion : function(region){
37577         this.region = region;
37578         this.setActiveClass(region && !this.background);
37579     },
37580     
37581     
37582     setActiveClass: function(state)
37583     {
37584         if(state){
37585            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37586            this.el.setStyle('position','relative');
37587         }else{
37588            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37589            this.el.setStyle('position', 'absolute');
37590         } 
37591     },
37592     
37593     /**
37594      * Returns the toolbar for this Panel if one was configured. 
37595      * @return {Roo.Toolbar} 
37596      */
37597     getToolbar : function(){
37598         return this.toolbar;
37599     },
37600     
37601     setActiveState : function(active)
37602     {
37603         this.active = active;
37604         this.setActiveClass(active);
37605         if(!active){
37606             if(this.fireEvent("deactivate", this) === false){
37607                 return false;
37608             }
37609             return true;
37610         }
37611         this.fireEvent("activate", this);
37612         return true;
37613     },
37614     /**
37615      * Updates this panel's element
37616      * @param {String} content The new content
37617      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37618     */
37619     setContent : function(content, loadScripts){
37620         this.el.update(content, loadScripts);
37621     },
37622
37623     ignoreResize : function(w, h){
37624         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37625             return true;
37626         }else{
37627             this.lastSize = {width: w, height: h};
37628             return false;
37629         }
37630     },
37631     /**
37632      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37633      * @return {Roo.UpdateManager} The UpdateManager
37634      */
37635     getUpdateManager : function(){
37636         return this.el.getUpdateManager();
37637     },
37638      /**
37639      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37640      * @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:
37641 <pre><code>
37642 panel.load({
37643     url: "your-url.php",
37644     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37645     callback: yourFunction,
37646     scope: yourObject, //(optional scope)
37647     discardUrl: false,
37648     nocache: false,
37649     text: "Loading...",
37650     timeout: 30,
37651     scripts: false
37652 });
37653 </code></pre>
37654      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37655      * 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.
37656      * @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}
37657      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37658      * @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.
37659      * @return {Roo.ContentPanel} this
37660      */
37661     load : function(){
37662         var um = this.el.getUpdateManager();
37663         um.update.apply(um, arguments);
37664         return this;
37665     },
37666
37667
37668     /**
37669      * 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.
37670      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37671      * @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)
37672      * @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)
37673      * @return {Roo.UpdateManager} The UpdateManager
37674      */
37675     setUrl : function(url, params, loadOnce){
37676         if(this.refreshDelegate){
37677             this.removeListener("activate", this.refreshDelegate);
37678         }
37679         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37680         this.on("activate", this.refreshDelegate);
37681         return this.el.getUpdateManager();
37682     },
37683     
37684     _handleRefresh : function(url, params, loadOnce){
37685         if(!loadOnce || !this.loaded){
37686             var updater = this.el.getUpdateManager();
37687             updater.update(url, params, this._setLoaded.createDelegate(this));
37688         }
37689     },
37690     
37691     _setLoaded : function(){
37692         this.loaded = true;
37693     }, 
37694     
37695     /**
37696      * Returns this panel's id
37697      * @return {String} 
37698      */
37699     getId : function(){
37700         return this.el.id;
37701     },
37702     
37703     /** 
37704      * Returns this panel's element - used by regiosn to add.
37705      * @return {Roo.Element} 
37706      */
37707     getEl : function(){
37708         return this.wrapEl || this.el;
37709     },
37710     
37711    
37712     
37713     adjustForComponents : function(width, height)
37714     {
37715         //Roo.log('adjustForComponents ');
37716         if(this.resizeEl != this.el){
37717             width -= this.el.getFrameWidth('lr');
37718             height -= this.el.getFrameWidth('tb');
37719         }
37720         if(this.toolbar){
37721             var te = this.toolbar.getEl();
37722             te.setWidth(width);
37723             height -= te.getHeight();
37724         }
37725         if(this.footer){
37726             var te = this.footer.getEl();
37727             te.setWidth(width);
37728             height -= te.getHeight();
37729         }
37730         
37731         
37732         if(this.adjustments){
37733             width += this.adjustments[0];
37734             height += this.adjustments[1];
37735         }
37736         return {"width": width, "height": height};
37737     },
37738     
37739     setSize : function(width, height){
37740         if(this.fitToFrame && !this.ignoreResize(width, height)){
37741             if(this.fitContainer && this.resizeEl != this.el){
37742                 this.el.setSize(width, height);
37743             }
37744             var size = this.adjustForComponents(width, height);
37745             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37746             this.fireEvent('resize', this, size.width, size.height);
37747         }
37748     },
37749     
37750     /**
37751      * Returns this panel's title
37752      * @return {String} 
37753      */
37754     getTitle : function(){
37755         
37756         if (typeof(this.title) != 'object') {
37757             return this.title;
37758         }
37759         
37760         var t = '';
37761         for (var k in this.title) {
37762             if (!this.title.hasOwnProperty(k)) {
37763                 continue;
37764             }
37765             
37766             if (k.indexOf('-') >= 0) {
37767                 var s = k.split('-');
37768                 for (var i = 0; i<s.length; i++) {
37769                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37770                 }
37771             } else {
37772                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37773             }
37774         }
37775         return t;
37776     },
37777     
37778     /**
37779      * Set this panel's title
37780      * @param {String} title
37781      */
37782     setTitle : function(title){
37783         this.title = title;
37784         if(this.region){
37785             this.region.updatePanelTitle(this, title);
37786         }
37787     },
37788     
37789     /**
37790      * Returns true is this panel was configured to be closable
37791      * @return {Boolean} 
37792      */
37793     isClosable : function(){
37794         return this.closable;
37795     },
37796     
37797     beforeSlide : function(){
37798         this.el.clip();
37799         this.resizeEl.clip();
37800     },
37801     
37802     afterSlide : function(){
37803         this.el.unclip();
37804         this.resizeEl.unclip();
37805     },
37806     
37807     /**
37808      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37809      *   Will fail silently if the {@link #setUrl} method has not been called.
37810      *   This does not activate the panel, just updates its content.
37811      */
37812     refresh : function(){
37813         if(this.refreshDelegate){
37814            this.loaded = false;
37815            this.refreshDelegate();
37816         }
37817     },
37818     
37819     /**
37820      * Destroys this panel
37821      */
37822     destroy : function(){
37823         this.el.removeAllListeners();
37824         var tempEl = document.createElement("span");
37825         tempEl.appendChild(this.el.dom);
37826         tempEl.innerHTML = "";
37827         this.el.remove();
37828         this.el = null;
37829     },
37830     
37831     /**
37832      * form - if the content panel contains a form - this is a reference to it.
37833      * @type {Roo.form.Form}
37834      */
37835     form : false,
37836     /**
37837      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37838      *    This contains a reference to it.
37839      * @type {Roo.View}
37840      */
37841     view : false,
37842     
37843       /**
37844      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37845      * <pre><code>
37846
37847 layout.addxtype({
37848        xtype : 'Form',
37849        items: [ .... ]
37850    }
37851 );
37852
37853 </code></pre>
37854      * @param {Object} cfg Xtype definition of item to add.
37855      */
37856     
37857     
37858     getChildContainer: function () {
37859         return this.getEl();
37860     }
37861     
37862     
37863     /*
37864         var  ret = new Roo.factory(cfg);
37865         return ret;
37866         
37867         
37868         // add form..
37869         if (cfg.xtype.match(/^Form$/)) {
37870             
37871             var el;
37872             //if (this.footer) {
37873             //    el = this.footer.container.insertSibling(false, 'before');
37874             //} else {
37875                 el = this.el.createChild();
37876             //}
37877
37878             this.form = new  Roo.form.Form(cfg);
37879             
37880             
37881             if ( this.form.allItems.length) {
37882                 this.form.render(el.dom);
37883             }
37884             return this.form;
37885         }
37886         // should only have one of theses..
37887         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37888             // views.. should not be just added - used named prop 'view''
37889             
37890             cfg.el = this.el.appendChild(document.createElement("div"));
37891             // factory?
37892             
37893             var ret = new Roo.factory(cfg);
37894              
37895              ret.render && ret.render(false, ''); // render blank..
37896             this.view = ret;
37897             return ret;
37898         }
37899         return false;
37900     }
37901     \*/
37902 });
37903  
37904 /**
37905  * @class Roo.bootstrap.panel.Grid
37906  * @extends Roo.bootstrap.panel.Content
37907  * @constructor
37908  * Create a new GridPanel.
37909  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37910  * @param {Object} config A the config object
37911   
37912  */
37913
37914
37915
37916 Roo.bootstrap.panel.Grid = function(config)
37917 {
37918     
37919       
37920     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37921         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37922
37923     config.el = this.wrapper;
37924     //this.el = this.wrapper;
37925     
37926       if (config.container) {
37927         // ctor'ed from a Border/panel.grid
37928         
37929         
37930         this.wrapper.setStyle("overflow", "hidden");
37931         this.wrapper.addClass('roo-grid-container');
37932
37933     }
37934     
37935     
37936     if(config.toolbar){
37937         var tool_el = this.wrapper.createChild();    
37938         this.toolbar = Roo.factory(config.toolbar);
37939         var ti = [];
37940         if (config.toolbar.items) {
37941             ti = config.toolbar.items ;
37942             delete config.toolbar.items ;
37943         }
37944         
37945         var nitems = [];
37946         this.toolbar.render(tool_el);
37947         for(var i =0;i < ti.length;i++) {
37948           //  Roo.log(['add child', items[i]]);
37949             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37950         }
37951         this.toolbar.items = nitems;
37952         
37953         delete config.toolbar;
37954     }
37955     
37956     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37957     config.grid.scrollBody = true;;
37958     config.grid.monitorWindowResize = false; // turn off autosizing
37959     config.grid.autoHeight = false;
37960     config.grid.autoWidth = false;
37961     
37962     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37963     
37964     if (config.background) {
37965         // render grid on panel activation (if panel background)
37966         this.on('activate', function(gp) {
37967             if (!gp.grid.rendered) {
37968                 gp.grid.render(this.wrapper);
37969                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37970             }
37971         });
37972             
37973     } else {
37974         this.grid.render(this.wrapper);
37975         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37976
37977     }
37978     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37979     // ??? needed ??? config.el = this.wrapper;
37980     
37981     
37982     
37983   
37984     // xtype created footer. - not sure if will work as we normally have to render first..
37985     if (this.footer && !this.footer.el && this.footer.xtype) {
37986         
37987         var ctr = this.grid.getView().getFooterPanel(true);
37988         this.footer.dataSource = this.grid.dataSource;
37989         this.footer = Roo.factory(this.footer, Roo);
37990         this.footer.render(ctr);
37991         
37992     }
37993     
37994     
37995     
37996     
37997      
37998 };
37999
38000 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38001     getId : function(){
38002         return this.grid.id;
38003     },
38004     
38005     /**
38006      * Returns the grid for this panel
38007      * @return {Roo.bootstrap.Table} 
38008      */
38009     getGrid : function(){
38010         return this.grid;    
38011     },
38012     
38013     setSize : function(width, height){
38014         if(!this.ignoreResize(width, height)){
38015             var grid = this.grid;
38016             var size = this.adjustForComponents(width, height);
38017             var gridel = grid.getGridEl();
38018             gridel.setSize(size.width, size.height);
38019             /*
38020             var thd = grid.getGridEl().select('thead',true).first();
38021             var tbd = grid.getGridEl().select('tbody', true).first();
38022             if (tbd) {
38023                 tbd.setSize(width, height - thd.getHeight());
38024             }
38025             */
38026             grid.autoSize();
38027         }
38028     },
38029      
38030     
38031     
38032     beforeSlide : function(){
38033         this.grid.getView().scroller.clip();
38034     },
38035     
38036     afterSlide : function(){
38037         this.grid.getView().scroller.unclip();
38038     },
38039     
38040     destroy : function(){
38041         this.grid.destroy();
38042         delete this.grid;
38043         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38044     }
38045 });
38046
38047 /**
38048  * @class Roo.bootstrap.panel.Nest
38049  * @extends Roo.bootstrap.panel.Content
38050  * @constructor
38051  * Create a new Panel, that can contain a layout.Border.
38052  * 
38053  * 
38054  * @param {Roo.BorderLayout} layout The layout for this panel
38055  * @param {String/Object} config A string to set only the title or a config object
38056  */
38057 Roo.bootstrap.panel.Nest = function(config)
38058 {
38059     // construct with only one argument..
38060     /* FIXME - implement nicer consturctors
38061     if (layout.layout) {
38062         config = layout;
38063         layout = config.layout;
38064         delete config.layout;
38065     }
38066     if (layout.xtype && !layout.getEl) {
38067         // then layout needs constructing..
38068         layout = Roo.factory(layout, Roo);
38069     }
38070     */
38071     
38072     config.el =  config.layout.getEl();
38073     
38074     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38075     
38076     config.layout.monitorWindowResize = false; // turn off autosizing
38077     this.layout = config.layout;
38078     this.layout.getEl().addClass("roo-layout-nested-layout");
38079     this.layout.parent = this;
38080     
38081     
38082     
38083     
38084 };
38085
38086 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38087
38088     setSize : function(width, height){
38089         if(!this.ignoreResize(width, height)){
38090             var size = this.adjustForComponents(width, height);
38091             var el = this.layout.getEl();
38092             if (size.height < 1) {
38093                 el.setWidth(size.width);   
38094             } else {
38095                 el.setSize(size.width, size.height);
38096             }
38097             var touch = el.dom.offsetWidth;
38098             this.layout.layout();
38099             // ie requires a double layout on the first pass
38100             if(Roo.isIE && !this.initialized){
38101                 this.initialized = true;
38102                 this.layout.layout();
38103             }
38104         }
38105     },
38106     
38107     // activate all subpanels if not currently active..
38108     
38109     setActiveState : function(active){
38110         this.active = active;
38111         this.setActiveClass(active);
38112         
38113         if(!active){
38114             this.fireEvent("deactivate", this);
38115             return;
38116         }
38117         
38118         this.fireEvent("activate", this);
38119         // not sure if this should happen before or after..
38120         if (!this.layout) {
38121             return; // should not happen..
38122         }
38123         var reg = false;
38124         for (var r in this.layout.regions) {
38125             reg = this.layout.getRegion(r);
38126             if (reg.getActivePanel()) {
38127                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38128                 reg.setActivePanel(reg.getActivePanel());
38129                 continue;
38130             }
38131             if (!reg.panels.length) {
38132                 continue;
38133             }
38134             reg.showPanel(reg.getPanel(0));
38135         }
38136         
38137         
38138         
38139         
38140     },
38141     
38142     /**
38143      * Returns the nested BorderLayout for this panel
38144      * @return {Roo.BorderLayout} 
38145      */
38146     getLayout : function(){
38147         return this.layout;
38148     },
38149     
38150      /**
38151      * Adds a xtype elements to the layout of the nested panel
38152      * <pre><code>
38153
38154 panel.addxtype({
38155        xtype : 'ContentPanel',
38156        region: 'west',
38157        items: [ .... ]
38158    }
38159 );
38160
38161 panel.addxtype({
38162         xtype : 'NestedLayoutPanel',
38163         region: 'west',
38164         layout: {
38165            center: { },
38166            west: { }   
38167         },
38168         items : [ ... list of content panels or nested layout panels.. ]
38169    }
38170 );
38171 </code></pre>
38172      * @param {Object} cfg Xtype definition of item to add.
38173      */
38174     addxtype : function(cfg) {
38175         return this.layout.addxtype(cfg);
38176     
38177     }
38178 });/*
38179  * Based on:
38180  * Ext JS Library 1.1.1
38181  * Copyright(c) 2006-2007, Ext JS, LLC.
38182  *
38183  * Originally Released Under LGPL - original licence link has changed is not relivant.
38184  *
38185  * Fork - LGPL
38186  * <script type="text/javascript">
38187  */
38188 /**
38189  * @class Roo.TabPanel
38190  * @extends Roo.util.Observable
38191  * A lightweight tab container.
38192  * <br><br>
38193  * Usage:
38194  * <pre><code>
38195 // basic tabs 1, built from existing content
38196 var tabs = new Roo.TabPanel("tabs1");
38197 tabs.addTab("script", "View Script");
38198 tabs.addTab("markup", "View Markup");
38199 tabs.activate("script");
38200
38201 // more advanced tabs, built from javascript
38202 var jtabs = new Roo.TabPanel("jtabs");
38203 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38204
38205 // set up the UpdateManager
38206 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38207 var updater = tab2.getUpdateManager();
38208 updater.setDefaultUrl("ajax1.htm");
38209 tab2.on('activate', updater.refresh, updater, true);
38210
38211 // Use setUrl for Ajax loading
38212 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38213 tab3.setUrl("ajax2.htm", null, true);
38214
38215 // Disabled tab
38216 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38217 tab4.disable();
38218
38219 jtabs.activate("jtabs-1");
38220  * </code></pre>
38221  * @constructor
38222  * Create a new TabPanel.
38223  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38224  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38225  */
38226 Roo.bootstrap.panel.Tabs = function(config){
38227     /**
38228     * The container element for this TabPanel.
38229     * @type Roo.Element
38230     */
38231     this.el = Roo.get(config.el);
38232     delete config.el;
38233     if(config){
38234         if(typeof config == "boolean"){
38235             this.tabPosition = config ? "bottom" : "top";
38236         }else{
38237             Roo.apply(this, config);
38238         }
38239     }
38240     
38241     if(this.tabPosition == "bottom"){
38242         // if tabs are at the bottom = create the body first.
38243         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38244         this.el.addClass("roo-tabs-bottom");
38245     }
38246     // next create the tabs holders
38247     
38248     if (this.tabPosition == "west"){
38249         
38250         var reg = this.region; // fake it..
38251         while (reg) {
38252             if (!reg.mgr.parent) {
38253                 break;
38254             }
38255             reg = reg.mgr.parent.region;
38256         }
38257         Roo.log("got nest?");
38258         Roo.log(reg);
38259         if (reg.mgr.getRegion('west')) {
38260             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38261             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38262             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38263             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38264             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38265         
38266             
38267         }
38268         
38269         
38270     } else {
38271      
38272         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38273         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38274         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38275         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38276     }
38277     
38278     
38279     if(Roo.isIE){
38280         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38281     }
38282     
38283     // finally - if tabs are at the top, then create the body last..
38284     if(this.tabPosition != "bottom"){
38285         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38286          * @type Roo.Element
38287          */
38288         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38289         this.el.addClass("roo-tabs-top");
38290     }
38291     this.items = [];
38292
38293     this.bodyEl.setStyle("position", "relative");
38294
38295     this.active = null;
38296     this.activateDelegate = this.activate.createDelegate(this);
38297
38298     this.addEvents({
38299         /**
38300          * @event tabchange
38301          * Fires when the active tab changes
38302          * @param {Roo.TabPanel} this
38303          * @param {Roo.TabPanelItem} activePanel The new active tab
38304          */
38305         "tabchange": true,
38306         /**
38307          * @event beforetabchange
38308          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38309          * @param {Roo.TabPanel} this
38310          * @param {Object} e Set cancel to true on this object to cancel the tab change
38311          * @param {Roo.TabPanelItem} tab The tab being changed to
38312          */
38313         "beforetabchange" : true
38314     });
38315
38316     Roo.EventManager.onWindowResize(this.onResize, this);
38317     this.cpad = this.el.getPadding("lr");
38318     this.hiddenCount = 0;
38319
38320
38321     // toolbar on the tabbar support...
38322     if (this.toolbar) {
38323         alert("no toolbar support yet");
38324         this.toolbar  = false;
38325         /*
38326         var tcfg = this.toolbar;
38327         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38328         this.toolbar = new Roo.Toolbar(tcfg);
38329         if (Roo.isSafari) {
38330             var tbl = tcfg.container.child('table', true);
38331             tbl.setAttribute('width', '100%');
38332         }
38333         */
38334         
38335     }
38336    
38337
38338
38339     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38340 };
38341
38342 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38343     /*
38344      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38345      */
38346     tabPosition : "top",
38347     /*
38348      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38349      */
38350     currentTabWidth : 0,
38351     /*
38352      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38353      */
38354     minTabWidth : 40,
38355     /*
38356      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38357      */
38358     maxTabWidth : 250,
38359     /*
38360      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38361      */
38362     preferredTabWidth : 175,
38363     /*
38364      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38365      */
38366     resizeTabs : false,
38367     /*
38368      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38369      */
38370     monitorResize : true,
38371     /*
38372      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38373      */
38374     toolbar : false,  // set by caller..
38375     
38376     region : false, /// set by caller
38377     
38378     disableTooltips : true, // not used yet...
38379
38380     /**
38381      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38382      * @param {String} id The id of the div to use <b>or create</b>
38383      * @param {String} text The text for the tab
38384      * @param {String} content (optional) Content to put in the TabPanelItem body
38385      * @param {Boolean} closable (optional) True to create a close icon on the tab
38386      * @return {Roo.TabPanelItem} The created TabPanelItem
38387      */
38388     addTab : function(id, text, content, closable, tpl)
38389     {
38390         var item = new Roo.bootstrap.panel.TabItem({
38391             panel: this,
38392             id : id,
38393             text : text,
38394             closable : closable,
38395             tpl : tpl
38396         });
38397         this.addTabItem(item);
38398         if(content){
38399             item.setContent(content);
38400         }
38401         return item;
38402     },
38403
38404     /**
38405      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38406      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38407      * @return {Roo.TabPanelItem}
38408      */
38409     getTab : function(id){
38410         return this.items[id];
38411     },
38412
38413     /**
38414      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38415      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38416      */
38417     hideTab : function(id){
38418         var t = this.items[id];
38419         if(!t.isHidden()){
38420            t.setHidden(true);
38421            this.hiddenCount++;
38422            this.autoSizeTabs();
38423         }
38424     },
38425
38426     /**
38427      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38428      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38429      */
38430     unhideTab : function(id){
38431         var t = this.items[id];
38432         if(t.isHidden()){
38433            t.setHidden(false);
38434            this.hiddenCount--;
38435            this.autoSizeTabs();
38436         }
38437     },
38438
38439     /**
38440      * Adds an existing {@link Roo.TabPanelItem}.
38441      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38442      */
38443     addTabItem : function(item)
38444     {
38445         this.items[item.id] = item;
38446         this.items.push(item);
38447         this.autoSizeTabs();
38448       //  if(this.resizeTabs){
38449     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38450   //         this.autoSizeTabs();
38451 //        }else{
38452 //            item.autoSize();
38453        // }
38454     },
38455
38456     /**
38457      * Removes a {@link Roo.TabPanelItem}.
38458      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38459      */
38460     removeTab : function(id){
38461         var items = this.items;
38462         var tab = items[id];
38463         if(!tab) { return; }
38464         var index = items.indexOf(tab);
38465         if(this.active == tab && items.length > 1){
38466             var newTab = this.getNextAvailable(index);
38467             if(newTab) {
38468                 newTab.activate();
38469             }
38470         }
38471         this.stripEl.dom.removeChild(tab.pnode.dom);
38472         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38473             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38474         }
38475         items.splice(index, 1);
38476         delete this.items[tab.id];
38477         tab.fireEvent("close", tab);
38478         tab.purgeListeners();
38479         this.autoSizeTabs();
38480     },
38481
38482     getNextAvailable : function(start){
38483         var items = this.items;
38484         var index = start;
38485         // look for a next tab that will slide over to
38486         // replace the one being removed
38487         while(index < items.length){
38488             var item = items[++index];
38489             if(item && !item.isHidden()){
38490                 return item;
38491             }
38492         }
38493         // if one isn't found select the previous tab (on the left)
38494         index = start;
38495         while(index >= 0){
38496             var item = items[--index];
38497             if(item && !item.isHidden()){
38498                 return item;
38499             }
38500         }
38501         return null;
38502     },
38503
38504     /**
38505      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38506      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38507      */
38508     disableTab : function(id){
38509         var tab = this.items[id];
38510         if(tab && this.active != tab){
38511             tab.disable();
38512         }
38513     },
38514
38515     /**
38516      * Enables a {@link Roo.TabPanelItem} that is disabled.
38517      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38518      */
38519     enableTab : function(id){
38520         var tab = this.items[id];
38521         tab.enable();
38522     },
38523
38524     /**
38525      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38526      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38527      * @return {Roo.TabPanelItem} The TabPanelItem.
38528      */
38529     activate : function(id)
38530     {
38531         //Roo.log('activite:'  + id);
38532         
38533         var tab = this.items[id];
38534         if(!tab){
38535             return null;
38536         }
38537         if(tab == this.active || tab.disabled){
38538             return tab;
38539         }
38540         var e = {};
38541         this.fireEvent("beforetabchange", this, e, tab);
38542         if(e.cancel !== true && !tab.disabled){
38543             if(this.active){
38544                 this.active.hide();
38545             }
38546             this.active = this.items[id];
38547             this.active.show();
38548             this.fireEvent("tabchange", this, this.active);
38549         }
38550         return tab;
38551     },
38552
38553     /**
38554      * Gets the active {@link Roo.TabPanelItem}.
38555      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38556      */
38557     getActiveTab : function(){
38558         return this.active;
38559     },
38560
38561     /**
38562      * Updates the tab body element to fit the height of the container element
38563      * for overflow scrolling
38564      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38565      */
38566     syncHeight : function(targetHeight){
38567         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38568         var bm = this.bodyEl.getMargins();
38569         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38570         this.bodyEl.setHeight(newHeight);
38571         return newHeight;
38572     },
38573
38574     onResize : function(){
38575         if(this.monitorResize){
38576             this.autoSizeTabs();
38577         }
38578     },
38579
38580     /**
38581      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38582      */
38583     beginUpdate : function(){
38584         this.updating = true;
38585     },
38586
38587     /**
38588      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38589      */
38590     endUpdate : function(){
38591         this.updating = false;
38592         this.autoSizeTabs();
38593     },
38594
38595     /**
38596      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38597      */
38598     autoSizeTabs : function()
38599     {
38600         var count = this.items.length;
38601         var vcount = count - this.hiddenCount;
38602         
38603         if (vcount < 2) {
38604             this.stripEl.hide();
38605         } else {
38606             this.stripEl.show();
38607         }
38608         
38609         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38610             return;
38611         }
38612         
38613         
38614         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38615         var availWidth = Math.floor(w / vcount);
38616         var b = this.stripBody;
38617         if(b.getWidth() > w){
38618             var tabs = this.items;
38619             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38620             if(availWidth < this.minTabWidth){
38621                 /*if(!this.sleft){    // incomplete scrolling code
38622                     this.createScrollButtons();
38623                 }
38624                 this.showScroll();
38625                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38626             }
38627         }else{
38628             if(this.currentTabWidth < this.preferredTabWidth){
38629                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38630             }
38631         }
38632     },
38633
38634     /**
38635      * Returns the number of tabs in this TabPanel.
38636      * @return {Number}
38637      */
38638      getCount : function(){
38639          return this.items.length;
38640      },
38641
38642     /**
38643      * Resizes all the tabs to the passed width
38644      * @param {Number} The new width
38645      */
38646     setTabWidth : function(width){
38647         this.currentTabWidth = width;
38648         for(var i = 0, len = this.items.length; i < len; i++) {
38649                 if(!this.items[i].isHidden()) {
38650                 this.items[i].setWidth(width);
38651             }
38652         }
38653     },
38654
38655     /**
38656      * Destroys this TabPanel
38657      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38658      */
38659     destroy : function(removeEl){
38660         Roo.EventManager.removeResizeListener(this.onResize, this);
38661         for(var i = 0, len = this.items.length; i < len; i++){
38662             this.items[i].purgeListeners();
38663         }
38664         if(removeEl === true){
38665             this.el.update("");
38666             this.el.remove();
38667         }
38668     },
38669     
38670     createStrip : function(container)
38671     {
38672         var strip = document.createElement("nav");
38673         strip.className = Roo.bootstrap.version == 4 ?
38674             "navbar-light bg-light" : 
38675             "navbar navbar-default"; //"x-tabs-wrap";
38676         container.appendChild(strip);
38677         return strip;
38678     },
38679     
38680     createStripList : function(strip)
38681     {
38682         // div wrapper for retard IE
38683         // returns the "tr" element.
38684         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38685         //'<div class="x-tabs-strip-wrap">'+
38686           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38687           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38688         return strip.firstChild; //.firstChild.firstChild.firstChild;
38689     },
38690     createBody : function(container)
38691     {
38692         var body = document.createElement("div");
38693         Roo.id(body, "tab-body");
38694         //Roo.fly(body).addClass("x-tabs-body");
38695         Roo.fly(body).addClass("tab-content");
38696         container.appendChild(body);
38697         return body;
38698     },
38699     createItemBody :function(bodyEl, id){
38700         var body = Roo.getDom(id);
38701         if(!body){
38702             body = document.createElement("div");
38703             body.id = id;
38704         }
38705         //Roo.fly(body).addClass("x-tabs-item-body");
38706         Roo.fly(body).addClass("tab-pane");
38707          bodyEl.insertBefore(body, bodyEl.firstChild);
38708         return body;
38709     },
38710     /** @private */
38711     createStripElements :  function(stripEl, text, closable, tpl)
38712     {
38713         var td = document.createElement("li"); // was td..
38714         td.className = 'nav-item';
38715         
38716         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38717         
38718         
38719         stripEl.appendChild(td);
38720         /*if(closable){
38721             td.className = "x-tabs-closable";
38722             if(!this.closeTpl){
38723                 this.closeTpl = new Roo.Template(
38724                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38725                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38726                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38727                 );
38728             }
38729             var el = this.closeTpl.overwrite(td, {"text": text});
38730             var close = el.getElementsByTagName("div")[0];
38731             var inner = el.getElementsByTagName("em")[0];
38732             return {"el": el, "close": close, "inner": inner};
38733         } else {
38734         */
38735         // not sure what this is..
38736 //            if(!this.tabTpl){
38737                 //this.tabTpl = new Roo.Template(
38738                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38739                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38740                 //);
38741 //                this.tabTpl = new Roo.Template(
38742 //                   '<a href="#">' +
38743 //                   '<span unselectable="on"' +
38744 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38745 //                            ' >{text}</span></a>'
38746 //                );
38747 //                
38748 //            }
38749
38750
38751             var template = tpl || this.tabTpl || false;
38752             
38753             if(!template){
38754                 template =  new Roo.Template(
38755                         Roo.bootstrap.version == 4 ? 
38756                             (
38757                                 '<a class="nav-link" href="#" unselectable="on"' +
38758                                      (this.disableTooltips ? '' : ' title="{text}"') +
38759                                      ' >{text}</a>'
38760                             ) : (
38761                                 '<a class="nav-link" href="#">' +
38762                                 '<span unselectable="on"' +
38763                                          (this.disableTooltips ? '' : ' title="{text}"') +
38764                                     ' >{text}</span></a>'
38765                             )
38766                 );
38767             }
38768             
38769             switch (typeof(template)) {
38770                 case 'object' :
38771                     break;
38772                 case 'string' :
38773                     template = new Roo.Template(template);
38774                     break;
38775                 default :
38776                     break;
38777             }
38778             
38779             var el = template.overwrite(td, {"text": text});
38780             
38781             var inner = el.getElementsByTagName("span")[0];
38782             
38783             return {"el": el, "inner": inner};
38784             
38785     }
38786         
38787     
38788 });
38789
38790 /**
38791  * @class Roo.TabPanelItem
38792  * @extends Roo.util.Observable
38793  * Represents an individual item (tab plus body) in a TabPanel.
38794  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38795  * @param {String} id The id of this TabPanelItem
38796  * @param {String} text The text for the tab of this TabPanelItem
38797  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38798  */
38799 Roo.bootstrap.panel.TabItem = function(config){
38800     /**
38801      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38802      * @type Roo.TabPanel
38803      */
38804     this.tabPanel = config.panel;
38805     /**
38806      * The id for this TabPanelItem
38807      * @type String
38808      */
38809     this.id = config.id;
38810     /** @private */
38811     this.disabled = false;
38812     /** @private */
38813     this.text = config.text;
38814     /** @private */
38815     this.loaded = false;
38816     this.closable = config.closable;
38817
38818     /**
38819      * The body element for this TabPanelItem.
38820      * @type Roo.Element
38821      */
38822     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38823     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38824     this.bodyEl.setStyle("display", "block");
38825     this.bodyEl.setStyle("zoom", "1");
38826     //this.hideAction();
38827
38828     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38829     /** @private */
38830     this.el = Roo.get(els.el);
38831     this.inner = Roo.get(els.inner, true);
38832      this.textEl = Roo.bootstrap.version == 4 ?
38833         this.el : Roo.get(this.el.dom.firstChild, true);
38834
38835     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38836     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38837
38838     
38839 //    this.el.on("mousedown", this.onTabMouseDown, this);
38840     this.el.on("click", this.onTabClick, this);
38841     /** @private */
38842     if(config.closable){
38843         var c = Roo.get(els.close, true);
38844         c.dom.title = this.closeText;
38845         c.addClassOnOver("close-over");
38846         c.on("click", this.closeClick, this);
38847      }
38848
38849     this.addEvents({
38850          /**
38851          * @event activate
38852          * Fires when this tab becomes the active tab.
38853          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38854          * @param {Roo.TabPanelItem} this
38855          */
38856         "activate": true,
38857         /**
38858          * @event beforeclose
38859          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38860          * @param {Roo.TabPanelItem} this
38861          * @param {Object} e Set cancel to true on this object to cancel the close.
38862          */
38863         "beforeclose": true,
38864         /**
38865          * @event close
38866          * Fires when this tab is closed.
38867          * @param {Roo.TabPanelItem} this
38868          */
38869          "close": true,
38870         /**
38871          * @event deactivate
38872          * Fires when this tab is no longer the active tab.
38873          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38874          * @param {Roo.TabPanelItem} this
38875          */
38876          "deactivate" : true
38877     });
38878     this.hidden = false;
38879
38880     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38881 };
38882
38883 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38884            {
38885     purgeListeners : function(){
38886        Roo.util.Observable.prototype.purgeListeners.call(this);
38887        this.el.removeAllListeners();
38888     },
38889     /**
38890      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38891      */
38892     show : function(){
38893         this.status_node.addClass("active");
38894         this.showAction();
38895         if(Roo.isOpera){
38896             this.tabPanel.stripWrap.repaint();
38897         }
38898         this.fireEvent("activate", this.tabPanel, this);
38899     },
38900
38901     /**
38902      * Returns true if this tab is the active tab.
38903      * @return {Boolean}
38904      */
38905     isActive : function(){
38906         return this.tabPanel.getActiveTab() == this;
38907     },
38908
38909     /**
38910      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38911      */
38912     hide : function(){
38913         this.status_node.removeClass("active");
38914         this.hideAction();
38915         this.fireEvent("deactivate", this.tabPanel, this);
38916     },
38917
38918     hideAction : function(){
38919         this.bodyEl.hide();
38920         this.bodyEl.setStyle("position", "absolute");
38921         this.bodyEl.setLeft("-20000px");
38922         this.bodyEl.setTop("-20000px");
38923     },
38924
38925     showAction : function(){
38926         this.bodyEl.setStyle("position", "relative");
38927         this.bodyEl.setTop("");
38928         this.bodyEl.setLeft("");
38929         this.bodyEl.show();
38930     },
38931
38932     /**
38933      * Set the tooltip for the tab.
38934      * @param {String} tooltip The tab's tooltip
38935      */
38936     setTooltip : function(text){
38937         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38938             this.textEl.dom.qtip = text;
38939             this.textEl.dom.removeAttribute('title');
38940         }else{
38941             this.textEl.dom.title = text;
38942         }
38943     },
38944
38945     onTabClick : function(e){
38946         e.preventDefault();
38947         this.tabPanel.activate(this.id);
38948     },
38949
38950     onTabMouseDown : function(e){
38951         e.preventDefault();
38952         this.tabPanel.activate(this.id);
38953     },
38954 /*
38955     getWidth : function(){
38956         return this.inner.getWidth();
38957     },
38958
38959     setWidth : function(width){
38960         var iwidth = width - this.linode.getPadding("lr");
38961         this.inner.setWidth(iwidth);
38962         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38963         this.linode.setWidth(width);
38964     },
38965 */
38966     /**
38967      * Show or hide the tab
38968      * @param {Boolean} hidden True to hide or false to show.
38969      */
38970     setHidden : function(hidden){
38971         this.hidden = hidden;
38972         this.linode.setStyle("display", hidden ? "none" : "");
38973     },
38974
38975     /**
38976      * Returns true if this tab is "hidden"
38977      * @return {Boolean}
38978      */
38979     isHidden : function(){
38980         return this.hidden;
38981     },
38982
38983     /**
38984      * Returns the text for this tab
38985      * @return {String}
38986      */
38987     getText : function(){
38988         return this.text;
38989     },
38990     /*
38991     autoSize : function(){
38992         //this.el.beginMeasure();
38993         this.textEl.setWidth(1);
38994         /*
38995          *  #2804 [new] Tabs in Roojs
38996          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38997          */
38998         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38999         //this.el.endMeasure();
39000     //},
39001
39002     /**
39003      * Sets the text for the tab (Note: this also sets the tooltip text)
39004      * @param {String} text The tab's text and tooltip
39005      */
39006     setText : function(text){
39007         this.text = text;
39008         this.textEl.update(text);
39009         this.setTooltip(text);
39010         //if(!this.tabPanel.resizeTabs){
39011         //    this.autoSize();
39012         //}
39013     },
39014     /**
39015      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39016      */
39017     activate : function(){
39018         this.tabPanel.activate(this.id);
39019     },
39020
39021     /**
39022      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39023      */
39024     disable : function(){
39025         if(this.tabPanel.active != this){
39026             this.disabled = true;
39027             this.status_node.addClass("disabled");
39028         }
39029     },
39030
39031     /**
39032      * Enables this TabPanelItem if it was previously disabled.
39033      */
39034     enable : function(){
39035         this.disabled = false;
39036         this.status_node.removeClass("disabled");
39037     },
39038
39039     /**
39040      * Sets the content for this TabPanelItem.
39041      * @param {String} content The content
39042      * @param {Boolean} loadScripts true to look for and load scripts
39043      */
39044     setContent : function(content, loadScripts){
39045         this.bodyEl.update(content, loadScripts);
39046     },
39047
39048     /**
39049      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39050      * @return {Roo.UpdateManager} The UpdateManager
39051      */
39052     getUpdateManager : function(){
39053         return this.bodyEl.getUpdateManager();
39054     },
39055
39056     /**
39057      * Set a URL to be used to load the content for this TabPanelItem.
39058      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39059      * @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)
39060      * @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)
39061      * @return {Roo.UpdateManager} The UpdateManager
39062      */
39063     setUrl : function(url, params, loadOnce){
39064         if(this.refreshDelegate){
39065             this.un('activate', this.refreshDelegate);
39066         }
39067         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39068         this.on("activate", this.refreshDelegate);
39069         return this.bodyEl.getUpdateManager();
39070     },
39071
39072     /** @private */
39073     _handleRefresh : function(url, params, loadOnce){
39074         if(!loadOnce || !this.loaded){
39075             var updater = this.bodyEl.getUpdateManager();
39076             updater.update(url, params, this._setLoaded.createDelegate(this));
39077         }
39078     },
39079
39080     /**
39081      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39082      *   Will fail silently if the setUrl method has not been called.
39083      *   This does not activate the panel, just updates its content.
39084      */
39085     refresh : function(){
39086         if(this.refreshDelegate){
39087            this.loaded = false;
39088            this.refreshDelegate();
39089         }
39090     },
39091
39092     /** @private */
39093     _setLoaded : function(){
39094         this.loaded = true;
39095     },
39096
39097     /** @private */
39098     closeClick : function(e){
39099         var o = {};
39100         e.stopEvent();
39101         this.fireEvent("beforeclose", this, o);
39102         if(o.cancel !== true){
39103             this.tabPanel.removeTab(this.id);
39104         }
39105     },
39106     /**
39107      * The text displayed in the tooltip for the close icon.
39108      * @type String
39109      */
39110     closeText : "Close this tab"
39111 });
39112 /**
39113 *    This script refer to:
39114 *    Title: International Telephone Input
39115 *    Author: Jack O'Connor
39116 *    Code version:  v12.1.12
39117 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39118 **/
39119
39120 Roo.bootstrap.PhoneInputData = function() {
39121     var d = [
39122       [
39123         "Afghanistan (‫افغانستان‬‎)",
39124         "af",
39125         "93"
39126       ],
39127       [
39128         "Albania (Shqipëri)",
39129         "al",
39130         "355"
39131       ],
39132       [
39133         "Algeria (‫الجزائر‬‎)",
39134         "dz",
39135         "213"
39136       ],
39137       [
39138         "American Samoa",
39139         "as",
39140         "1684"
39141       ],
39142       [
39143         "Andorra",
39144         "ad",
39145         "376"
39146       ],
39147       [
39148         "Angola",
39149         "ao",
39150         "244"
39151       ],
39152       [
39153         "Anguilla",
39154         "ai",
39155         "1264"
39156       ],
39157       [
39158         "Antigua and Barbuda",
39159         "ag",
39160         "1268"
39161       ],
39162       [
39163         "Argentina",
39164         "ar",
39165         "54"
39166       ],
39167       [
39168         "Armenia (Հայաստան)",
39169         "am",
39170         "374"
39171       ],
39172       [
39173         "Aruba",
39174         "aw",
39175         "297"
39176       ],
39177       [
39178         "Australia",
39179         "au",
39180         "61",
39181         0
39182       ],
39183       [
39184         "Austria (Österreich)",
39185         "at",
39186         "43"
39187       ],
39188       [
39189         "Azerbaijan (Azərbaycan)",
39190         "az",
39191         "994"
39192       ],
39193       [
39194         "Bahamas",
39195         "bs",
39196         "1242"
39197       ],
39198       [
39199         "Bahrain (‫البحرين‬‎)",
39200         "bh",
39201         "973"
39202       ],
39203       [
39204         "Bangladesh (বাংলাদেশ)",
39205         "bd",
39206         "880"
39207       ],
39208       [
39209         "Barbados",
39210         "bb",
39211         "1246"
39212       ],
39213       [
39214         "Belarus (Беларусь)",
39215         "by",
39216         "375"
39217       ],
39218       [
39219         "Belgium (België)",
39220         "be",
39221         "32"
39222       ],
39223       [
39224         "Belize",
39225         "bz",
39226         "501"
39227       ],
39228       [
39229         "Benin (Bénin)",
39230         "bj",
39231         "229"
39232       ],
39233       [
39234         "Bermuda",
39235         "bm",
39236         "1441"
39237       ],
39238       [
39239         "Bhutan (འབྲུག)",
39240         "bt",
39241         "975"
39242       ],
39243       [
39244         "Bolivia",
39245         "bo",
39246         "591"
39247       ],
39248       [
39249         "Bosnia and Herzegovina (Босна и Херцеговина)",
39250         "ba",
39251         "387"
39252       ],
39253       [
39254         "Botswana",
39255         "bw",
39256         "267"
39257       ],
39258       [
39259         "Brazil (Brasil)",
39260         "br",
39261         "55"
39262       ],
39263       [
39264         "British Indian Ocean Territory",
39265         "io",
39266         "246"
39267       ],
39268       [
39269         "British Virgin Islands",
39270         "vg",
39271         "1284"
39272       ],
39273       [
39274         "Brunei",
39275         "bn",
39276         "673"
39277       ],
39278       [
39279         "Bulgaria (България)",
39280         "bg",
39281         "359"
39282       ],
39283       [
39284         "Burkina Faso",
39285         "bf",
39286         "226"
39287       ],
39288       [
39289         "Burundi (Uburundi)",
39290         "bi",
39291         "257"
39292       ],
39293       [
39294         "Cambodia (កម្ពុជា)",
39295         "kh",
39296         "855"
39297       ],
39298       [
39299         "Cameroon (Cameroun)",
39300         "cm",
39301         "237"
39302       ],
39303       [
39304         "Canada",
39305         "ca",
39306         "1",
39307         1,
39308         ["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"]
39309       ],
39310       [
39311         "Cape Verde (Kabu Verdi)",
39312         "cv",
39313         "238"
39314       ],
39315       [
39316         "Caribbean Netherlands",
39317         "bq",
39318         "599",
39319         1
39320       ],
39321       [
39322         "Cayman Islands",
39323         "ky",
39324         "1345"
39325       ],
39326       [
39327         "Central African Republic (République centrafricaine)",
39328         "cf",
39329         "236"
39330       ],
39331       [
39332         "Chad (Tchad)",
39333         "td",
39334         "235"
39335       ],
39336       [
39337         "Chile",
39338         "cl",
39339         "56"
39340       ],
39341       [
39342         "China (中国)",
39343         "cn",
39344         "86"
39345       ],
39346       [
39347         "Christmas Island",
39348         "cx",
39349         "61",
39350         2
39351       ],
39352       [
39353         "Cocos (Keeling) Islands",
39354         "cc",
39355         "61",
39356         1
39357       ],
39358       [
39359         "Colombia",
39360         "co",
39361         "57"
39362       ],
39363       [
39364         "Comoros (‫جزر القمر‬‎)",
39365         "km",
39366         "269"
39367       ],
39368       [
39369         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39370         "cd",
39371         "243"
39372       ],
39373       [
39374         "Congo (Republic) (Congo-Brazzaville)",
39375         "cg",
39376         "242"
39377       ],
39378       [
39379         "Cook Islands",
39380         "ck",
39381         "682"
39382       ],
39383       [
39384         "Costa Rica",
39385         "cr",
39386         "506"
39387       ],
39388       [
39389         "Côte d’Ivoire",
39390         "ci",
39391         "225"
39392       ],
39393       [
39394         "Croatia (Hrvatska)",
39395         "hr",
39396         "385"
39397       ],
39398       [
39399         "Cuba",
39400         "cu",
39401         "53"
39402       ],
39403       [
39404         "Curaçao",
39405         "cw",
39406         "599",
39407         0
39408       ],
39409       [
39410         "Cyprus (Κύπρος)",
39411         "cy",
39412         "357"
39413       ],
39414       [
39415         "Czech Republic (Česká republika)",
39416         "cz",
39417         "420"
39418       ],
39419       [
39420         "Denmark (Danmark)",
39421         "dk",
39422         "45"
39423       ],
39424       [
39425         "Djibouti",
39426         "dj",
39427         "253"
39428       ],
39429       [
39430         "Dominica",
39431         "dm",
39432         "1767"
39433       ],
39434       [
39435         "Dominican Republic (República Dominicana)",
39436         "do",
39437         "1",
39438         2,
39439         ["809", "829", "849"]
39440       ],
39441       [
39442         "Ecuador",
39443         "ec",
39444         "593"
39445       ],
39446       [
39447         "Egypt (‫مصر‬‎)",
39448         "eg",
39449         "20"
39450       ],
39451       [
39452         "El Salvador",
39453         "sv",
39454         "503"
39455       ],
39456       [
39457         "Equatorial Guinea (Guinea Ecuatorial)",
39458         "gq",
39459         "240"
39460       ],
39461       [
39462         "Eritrea",
39463         "er",
39464         "291"
39465       ],
39466       [
39467         "Estonia (Eesti)",
39468         "ee",
39469         "372"
39470       ],
39471       [
39472         "Ethiopia",
39473         "et",
39474         "251"
39475       ],
39476       [
39477         "Falkland Islands (Islas Malvinas)",
39478         "fk",
39479         "500"
39480       ],
39481       [
39482         "Faroe Islands (Føroyar)",
39483         "fo",
39484         "298"
39485       ],
39486       [
39487         "Fiji",
39488         "fj",
39489         "679"
39490       ],
39491       [
39492         "Finland (Suomi)",
39493         "fi",
39494         "358",
39495         0
39496       ],
39497       [
39498         "France",
39499         "fr",
39500         "33"
39501       ],
39502       [
39503         "French Guiana (Guyane française)",
39504         "gf",
39505         "594"
39506       ],
39507       [
39508         "French Polynesia (Polynésie française)",
39509         "pf",
39510         "689"
39511       ],
39512       [
39513         "Gabon",
39514         "ga",
39515         "241"
39516       ],
39517       [
39518         "Gambia",
39519         "gm",
39520         "220"
39521       ],
39522       [
39523         "Georgia (საქართველო)",
39524         "ge",
39525         "995"
39526       ],
39527       [
39528         "Germany (Deutschland)",
39529         "de",
39530         "49"
39531       ],
39532       [
39533         "Ghana (Gaana)",
39534         "gh",
39535         "233"
39536       ],
39537       [
39538         "Gibraltar",
39539         "gi",
39540         "350"
39541       ],
39542       [
39543         "Greece (Ελλάδα)",
39544         "gr",
39545         "30"
39546       ],
39547       [
39548         "Greenland (Kalaallit Nunaat)",
39549         "gl",
39550         "299"
39551       ],
39552       [
39553         "Grenada",
39554         "gd",
39555         "1473"
39556       ],
39557       [
39558         "Guadeloupe",
39559         "gp",
39560         "590",
39561         0
39562       ],
39563       [
39564         "Guam",
39565         "gu",
39566         "1671"
39567       ],
39568       [
39569         "Guatemala",
39570         "gt",
39571         "502"
39572       ],
39573       [
39574         "Guernsey",
39575         "gg",
39576         "44",
39577         1
39578       ],
39579       [
39580         "Guinea (Guinée)",
39581         "gn",
39582         "224"
39583       ],
39584       [
39585         "Guinea-Bissau (Guiné Bissau)",
39586         "gw",
39587         "245"
39588       ],
39589       [
39590         "Guyana",
39591         "gy",
39592         "592"
39593       ],
39594       [
39595         "Haiti",
39596         "ht",
39597         "509"
39598       ],
39599       [
39600         "Honduras",
39601         "hn",
39602         "504"
39603       ],
39604       [
39605         "Hong Kong (香港)",
39606         "hk",
39607         "852"
39608       ],
39609       [
39610         "Hungary (Magyarország)",
39611         "hu",
39612         "36"
39613       ],
39614       [
39615         "Iceland (Ísland)",
39616         "is",
39617         "354"
39618       ],
39619       [
39620         "India (भारत)",
39621         "in",
39622         "91"
39623       ],
39624       [
39625         "Indonesia",
39626         "id",
39627         "62"
39628       ],
39629       [
39630         "Iran (‫ایران‬‎)",
39631         "ir",
39632         "98"
39633       ],
39634       [
39635         "Iraq (‫العراق‬‎)",
39636         "iq",
39637         "964"
39638       ],
39639       [
39640         "Ireland",
39641         "ie",
39642         "353"
39643       ],
39644       [
39645         "Isle of Man",
39646         "im",
39647         "44",
39648         2
39649       ],
39650       [
39651         "Israel (‫ישראל‬‎)",
39652         "il",
39653         "972"
39654       ],
39655       [
39656         "Italy (Italia)",
39657         "it",
39658         "39",
39659         0
39660       ],
39661       [
39662         "Jamaica",
39663         "jm",
39664         "1876"
39665       ],
39666       [
39667         "Japan (日本)",
39668         "jp",
39669         "81"
39670       ],
39671       [
39672         "Jersey",
39673         "je",
39674         "44",
39675         3
39676       ],
39677       [
39678         "Jordan (‫الأردن‬‎)",
39679         "jo",
39680         "962"
39681       ],
39682       [
39683         "Kazakhstan (Казахстан)",
39684         "kz",
39685         "7",
39686         1
39687       ],
39688       [
39689         "Kenya",
39690         "ke",
39691         "254"
39692       ],
39693       [
39694         "Kiribati",
39695         "ki",
39696         "686"
39697       ],
39698       [
39699         "Kosovo",
39700         "xk",
39701         "383"
39702       ],
39703       [
39704         "Kuwait (‫الكويت‬‎)",
39705         "kw",
39706         "965"
39707       ],
39708       [
39709         "Kyrgyzstan (Кыргызстан)",
39710         "kg",
39711         "996"
39712       ],
39713       [
39714         "Laos (ລາວ)",
39715         "la",
39716         "856"
39717       ],
39718       [
39719         "Latvia (Latvija)",
39720         "lv",
39721         "371"
39722       ],
39723       [
39724         "Lebanon (‫لبنان‬‎)",
39725         "lb",
39726         "961"
39727       ],
39728       [
39729         "Lesotho",
39730         "ls",
39731         "266"
39732       ],
39733       [
39734         "Liberia",
39735         "lr",
39736         "231"
39737       ],
39738       [
39739         "Libya (‫ليبيا‬‎)",
39740         "ly",
39741         "218"
39742       ],
39743       [
39744         "Liechtenstein",
39745         "li",
39746         "423"
39747       ],
39748       [
39749         "Lithuania (Lietuva)",
39750         "lt",
39751         "370"
39752       ],
39753       [
39754         "Luxembourg",
39755         "lu",
39756         "352"
39757       ],
39758       [
39759         "Macau (澳門)",
39760         "mo",
39761         "853"
39762       ],
39763       [
39764         "Macedonia (FYROM) (Македонија)",
39765         "mk",
39766         "389"
39767       ],
39768       [
39769         "Madagascar (Madagasikara)",
39770         "mg",
39771         "261"
39772       ],
39773       [
39774         "Malawi",
39775         "mw",
39776         "265"
39777       ],
39778       [
39779         "Malaysia",
39780         "my",
39781         "60"
39782       ],
39783       [
39784         "Maldives",
39785         "mv",
39786         "960"
39787       ],
39788       [
39789         "Mali",
39790         "ml",
39791         "223"
39792       ],
39793       [
39794         "Malta",
39795         "mt",
39796         "356"
39797       ],
39798       [
39799         "Marshall Islands",
39800         "mh",
39801         "692"
39802       ],
39803       [
39804         "Martinique",
39805         "mq",
39806         "596"
39807       ],
39808       [
39809         "Mauritania (‫موريتانيا‬‎)",
39810         "mr",
39811         "222"
39812       ],
39813       [
39814         "Mauritius (Moris)",
39815         "mu",
39816         "230"
39817       ],
39818       [
39819         "Mayotte",
39820         "yt",
39821         "262",
39822         1
39823       ],
39824       [
39825         "Mexico (México)",
39826         "mx",
39827         "52"
39828       ],
39829       [
39830         "Micronesia",
39831         "fm",
39832         "691"
39833       ],
39834       [
39835         "Moldova (Republica Moldova)",
39836         "md",
39837         "373"
39838       ],
39839       [
39840         "Monaco",
39841         "mc",
39842         "377"
39843       ],
39844       [
39845         "Mongolia (Монгол)",
39846         "mn",
39847         "976"
39848       ],
39849       [
39850         "Montenegro (Crna Gora)",
39851         "me",
39852         "382"
39853       ],
39854       [
39855         "Montserrat",
39856         "ms",
39857         "1664"
39858       ],
39859       [
39860         "Morocco (‫المغرب‬‎)",
39861         "ma",
39862         "212",
39863         0
39864       ],
39865       [
39866         "Mozambique (Moçambique)",
39867         "mz",
39868         "258"
39869       ],
39870       [
39871         "Myanmar (Burma) (မြန်မာ)",
39872         "mm",
39873         "95"
39874       ],
39875       [
39876         "Namibia (Namibië)",
39877         "na",
39878         "264"
39879       ],
39880       [
39881         "Nauru",
39882         "nr",
39883         "674"
39884       ],
39885       [
39886         "Nepal (नेपाल)",
39887         "np",
39888         "977"
39889       ],
39890       [
39891         "Netherlands (Nederland)",
39892         "nl",
39893         "31"
39894       ],
39895       [
39896         "New Caledonia (Nouvelle-Calédonie)",
39897         "nc",
39898         "687"
39899       ],
39900       [
39901         "New Zealand",
39902         "nz",
39903         "64"
39904       ],
39905       [
39906         "Nicaragua",
39907         "ni",
39908         "505"
39909       ],
39910       [
39911         "Niger (Nijar)",
39912         "ne",
39913         "227"
39914       ],
39915       [
39916         "Nigeria",
39917         "ng",
39918         "234"
39919       ],
39920       [
39921         "Niue",
39922         "nu",
39923         "683"
39924       ],
39925       [
39926         "Norfolk Island",
39927         "nf",
39928         "672"
39929       ],
39930       [
39931         "North Korea (조선 민주주의 인민 공화국)",
39932         "kp",
39933         "850"
39934       ],
39935       [
39936         "Northern Mariana Islands",
39937         "mp",
39938         "1670"
39939       ],
39940       [
39941         "Norway (Norge)",
39942         "no",
39943         "47",
39944         0
39945       ],
39946       [
39947         "Oman (‫عُمان‬‎)",
39948         "om",
39949         "968"
39950       ],
39951       [
39952         "Pakistan (‫پاکستان‬‎)",
39953         "pk",
39954         "92"
39955       ],
39956       [
39957         "Palau",
39958         "pw",
39959         "680"
39960       ],
39961       [
39962         "Palestine (‫فلسطين‬‎)",
39963         "ps",
39964         "970"
39965       ],
39966       [
39967         "Panama (Panamá)",
39968         "pa",
39969         "507"
39970       ],
39971       [
39972         "Papua New Guinea",
39973         "pg",
39974         "675"
39975       ],
39976       [
39977         "Paraguay",
39978         "py",
39979         "595"
39980       ],
39981       [
39982         "Peru (Perú)",
39983         "pe",
39984         "51"
39985       ],
39986       [
39987         "Philippines",
39988         "ph",
39989         "63"
39990       ],
39991       [
39992         "Poland (Polska)",
39993         "pl",
39994         "48"
39995       ],
39996       [
39997         "Portugal",
39998         "pt",
39999         "351"
40000       ],
40001       [
40002         "Puerto Rico",
40003         "pr",
40004         "1",
40005         3,
40006         ["787", "939"]
40007       ],
40008       [
40009         "Qatar (‫قطر‬‎)",
40010         "qa",
40011         "974"
40012       ],
40013       [
40014         "Réunion (La Réunion)",
40015         "re",
40016         "262",
40017         0
40018       ],
40019       [
40020         "Romania (România)",
40021         "ro",
40022         "40"
40023       ],
40024       [
40025         "Russia (Россия)",
40026         "ru",
40027         "7",
40028         0
40029       ],
40030       [
40031         "Rwanda",
40032         "rw",
40033         "250"
40034       ],
40035       [
40036         "Saint Barthélemy",
40037         "bl",
40038         "590",
40039         1
40040       ],
40041       [
40042         "Saint Helena",
40043         "sh",
40044         "290"
40045       ],
40046       [
40047         "Saint Kitts and Nevis",
40048         "kn",
40049         "1869"
40050       ],
40051       [
40052         "Saint Lucia",
40053         "lc",
40054         "1758"
40055       ],
40056       [
40057         "Saint Martin (Saint-Martin (partie française))",
40058         "mf",
40059         "590",
40060         2
40061       ],
40062       [
40063         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40064         "pm",
40065         "508"
40066       ],
40067       [
40068         "Saint Vincent and the Grenadines",
40069         "vc",
40070         "1784"
40071       ],
40072       [
40073         "Samoa",
40074         "ws",
40075         "685"
40076       ],
40077       [
40078         "San Marino",
40079         "sm",
40080         "378"
40081       ],
40082       [
40083         "São Tomé and Príncipe (São Tomé e Príncipe)",
40084         "st",
40085         "239"
40086       ],
40087       [
40088         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40089         "sa",
40090         "966"
40091       ],
40092       [
40093         "Senegal (Sénégal)",
40094         "sn",
40095         "221"
40096       ],
40097       [
40098         "Serbia (Србија)",
40099         "rs",
40100         "381"
40101       ],
40102       [
40103         "Seychelles",
40104         "sc",
40105         "248"
40106       ],
40107       [
40108         "Sierra Leone",
40109         "sl",
40110         "232"
40111       ],
40112       [
40113         "Singapore",
40114         "sg",
40115         "65"
40116       ],
40117       [
40118         "Sint Maarten",
40119         "sx",
40120         "1721"
40121       ],
40122       [
40123         "Slovakia (Slovensko)",
40124         "sk",
40125         "421"
40126       ],
40127       [
40128         "Slovenia (Slovenija)",
40129         "si",
40130         "386"
40131       ],
40132       [
40133         "Solomon Islands",
40134         "sb",
40135         "677"
40136       ],
40137       [
40138         "Somalia (Soomaaliya)",
40139         "so",
40140         "252"
40141       ],
40142       [
40143         "South Africa",
40144         "za",
40145         "27"
40146       ],
40147       [
40148         "South Korea (대한민국)",
40149         "kr",
40150         "82"
40151       ],
40152       [
40153         "South Sudan (‫جنوب السودان‬‎)",
40154         "ss",
40155         "211"
40156       ],
40157       [
40158         "Spain (España)",
40159         "es",
40160         "34"
40161       ],
40162       [
40163         "Sri Lanka (ශ්‍රී ලංකාව)",
40164         "lk",
40165         "94"
40166       ],
40167       [
40168         "Sudan (‫السودان‬‎)",
40169         "sd",
40170         "249"
40171       ],
40172       [
40173         "Suriname",
40174         "sr",
40175         "597"
40176       ],
40177       [
40178         "Svalbard and Jan Mayen",
40179         "sj",
40180         "47",
40181         1
40182       ],
40183       [
40184         "Swaziland",
40185         "sz",
40186         "268"
40187       ],
40188       [
40189         "Sweden (Sverige)",
40190         "se",
40191         "46"
40192       ],
40193       [
40194         "Switzerland (Schweiz)",
40195         "ch",
40196         "41"
40197       ],
40198       [
40199         "Syria (‫سوريا‬‎)",
40200         "sy",
40201         "963"
40202       ],
40203       [
40204         "Taiwan (台灣)",
40205         "tw",
40206         "886"
40207       ],
40208       [
40209         "Tajikistan",
40210         "tj",
40211         "992"
40212       ],
40213       [
40214         "Tanzania",
40215         "tz",
40216         "255"
40217       ],
40218       [
40219         "Thailand (ไทย)",
40220         "th",
40221         "66"
40222       ],
40223       [
40224         "Timor-Leste",
40225         "tl",
40226         "670"
40227       ],
40228       [
40229         "Togo",
40230         "tg",
40231         "228"
40232       ],
40233       [
40234         "Tokelau",
40235         "tk",
40236         "690"
40237       ],
40238       [
40239         "Tonga",
40240         "to",
40241         "676"
40242       ],
40243       [
40244         "Trinidad and Tobago",
40245         "tt",
40246         "1868"
40247       ],
40248       [
40249         "Tunisia (‫تونس‬‎)",
40250         "tn",
40251         "216"
40252       ],
40253       [
40254         "Turkey (Türkiye)",
40255         "tr",
40256         "90"
40257       ],
40258       [
40259         "Turkmenistan",
40260         "tm",
40261         "993"
40262       ],
40263       [
40264         "Turks and Caicos Islands",
40265         "tc",
40266         "1649"
40267       ],
40268       [
40269         "Tuvalu",
40270         "tv",
40271         "688"
40272       ],
40273       [
40274         "U.S. Virgin Islands",
40275         "vi",
40276         "1340"
40277       ],
40278       [
40279         "Uganda",
40280         "ug",
40281         "256"
40282       ],
40283       [
40284         "Ukraine (Україна)",
40285         "ua",
40286         "380"
40287       ],
40288       [
40289         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40290         "ae",
40291         "971"
40292       ],
40293       [
40294         "United Kingdom",
40295         "gb",
40296         "44",
40297         0
40298       ],
40299       [
40300         "United States",
40301         "us",
40302         "1",
40303         0
40304       ],
40305       [
40306         "Uruguay",
40307         "uy",
40308         "598"
40309       ],
40310       [
40311         "Uzbekistan (Oʻzbekiston)",
40312         "uz",
40313         "998"
40314       ],
40315       [
40316         "Vanuatu",
40317         "vu",
40318         "678"
40319       ],
40320       [
40321         "Vatican City (Città del Vaticano)",
40322         "va",
40323         "39",
40324         1
40325       ],
40326       [
40327         "Venezuela",
40328         "ve",
40329         "58"
40330       ],
40331       [
40332         "Vietnam (Việt Nam)",
40333         "vn",
40334         "84"
40335       ],
40336       [
40337         "Wallis and Futuna (Wallis-et-Futuna)",
40338         "wf",
40339         "681"
40340       ],
40341       [
40342         "Western Sahara (‫الصحراء الغربية‬‎)",
40343         "eh",
40344         "212",
40345         1
40346       ],
40347       [
40348         "Yemen (‫اليمن‬‎)",
40349         "ye",
40350         "967"
40351       ],
40352       [
40353         "Zambia",
40354         "zm",
40355         "260"
40356       ],
40357       [
40358         "Zimbabwe",
40359         "zw",
40360         "263"
40361       ],
40362       [
40363         "Åland Islands",
40364         "ax",
40365         "358",
40366         1
40367       ]
40368   ];
40369   
40370   return d;
40371 }/**
40372 *    This script refer to:
40373 *    Title: International Telephone Input
40374 *    Author: Jack O'Connor
40375 *    Code version:  v12.1.12
40376 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40377 **/
40378
40379 /**
40380  * @class Roo.bootstrap.PhoneInput
40381  * @extends Roo.bootstrap.TriggerField
40382  * An input with International dial-code selection
40383  
40384  * @cfg {String} defaultDialCode default '+852'
40385  * @cfg {Array} preferedCountries default []
40386   
40387  * @constructor
40388  * Create a new PhoneInput.
40389  * @param {Object} config Configuration options
40390  */
40391
40392 Roo.bootstrap.PhoneInput = function(config) {
40393     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40394 };
40395
40396 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40397         
40398         listWidth: undefined,
40399         
40400         selectedClass: 'active',
40401         
40402         invalidClass : "has-warning",
40403         
40404         validClass: 'has-success',
40405         
40406         allowed: '0123456789',
40407         
40408         max_length: 15,
40409         
40410         /**
40411          * @cfg {String} defaultDialCode The default dial code when initializing the input
40412          */
40413         defaultDialCode: '+852',
40414         
40415         /**
40416          * @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
40417          */
40418         preferedCountries: false,
40419         
40420         getAutoCreate : function()
40421         {
40422             var data = Roo.bootstrap.PhoneInputData();
40423             var align = this.labelAlign || this.parentLabelAlign();
40424             var id = Roo.id();
40425             
40426             this.allCountries = [];
40427             this.dialCodeMapping = [];
40428             
40429             for (var i = 0; i < data.length; i++) {
40430               var c = data[i];
40431               this.allCountries[i] = {
40432                 name: c[0],
40433                 iso2: c[1],
40434                 dialCode: c[2],
40435                 priority: c[3] || 0,
40436                 areaCodes: c[4] || null
40437               };
40438               this.dialCodeMapping[c[2]] = {
40439                   name: c[0],
40440                   iso2: c[1],
40441                   priority: c[3] || 0,
40442                   areaCodes: c[4] || null
40443               };
40444             }
40445             
40446             var cfg = {
40447                 cls: 'form-group',
40448                 cn: []
40449             };
40450             
40451             var input =  {
40452                 tag: 'input',
40453                 id : id,
40454                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40455                 maxlength: this.max_length,
40456                 cls : 'form-control tel-input',
40457                 autocomplete: 'new-password'
40458             };
40459             
40460             var hiddenInput = {
40461                 tag: 'input',
40462                 type: 'hidden',
40463                 cls: 'hidden-tel-input'
40464             };
40465             
40466             if (this.name) {
40467                 hiddenInput.name = this.name;
40468             }
40469             
40470             if (this.disabled) {
40471                 input.disabled = true;
40472             }
40473             
40474             var flag_container = {
40475                 tag: 'div',
40476                 cls: 'flag-box',
40477                 cn: [
40478                     {
40479                         tag: 'div',
40480                         cls: 'flag'
40481                     },
40482                     {
40483                         tag: 'div',
40484                         cls: 'caret'
40485                     }
40486                 ]
40487             };
40488             
40489             var box = {
40490                 tag: 'div',
40491                 cls: this.hasFeedback ? 'has-feedback' : '',
40492                 cn: [
40493                     hiddenInput,
40494                     input,
40495                     {
40496                         tag: 'input',
40497                         cls: 'dial-code-holder',
40498                         disabled: true
40499                     }
40500                 ]
40501             };
40502             
40503             var container = {
40504                 cls: 'roo-select2-container input-group',
40505                 cn: [
40506                     flag_container,
40507                     box
40508                 ]
40509             };
40510             
40511             if (this.fieldLabel.length) {
40512                 var indicator = {
40513                     tag: 'i',
40514                     tooltip: 'This field is required'
40515                 };
40516                 
40517                 var label = {
40518                     tag: 'label',
40519                     'for':  id,
40520                     cls: 'control-label',
40521                     cn: []
40522                 };
40523                 
40524                 var label_text = {
40525                     tag: 'span',
40526                     html: this.fieldLabel
40527                 };
40528                 
40529                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40530                 label.cn = [
40531                     indicator,
40532                     label_text
40533                 ];
40534                 
40535                 if(this.indicatorpos == 'right') {
40536                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40537                     label.cn = [
40538                         label_text,
40539                         indicator
40540                     ];
40541                 }
40542                 
40543                 if(align == 'left') {
40544                     container = {
40545                         tag: 'div',
40546                         cn: [
40547                             container
40548                         ]
40549                     };
40550                     
40551                     if(this.labelWidth > 12){
40552                         label.style = "width: " + this.labelWidth + 'px';
40553                     }
40554                     if(this.labelWidth < 13 && this.labelmd == 0){
40555                         this.labelmd = this.labelWidth;
40556                     }
40557                     if(this.labellg > 0){
40558                         label.cls += ' col-lg-' + this.labellg;
40559                         input.cls += ' col-lg-' + (12 - this.labellg);
40560                     }
40561                     if(this.labelmd > 0){
40562                         label.cls += ' col-md-' + this.labelmd;
40563                         container.cls += ' col-md-' + (12 - this.labelmd);
40564                     }
40565                     if(this.labelsm > 0){
40566                         label.cls += ' col-sm-' + this.labelsm;
40567                         container.cls += ' col-sm-' + (12 - this.labelsm);
40568                     }
40569                     if(this.labelxs > 0){
40570                         label.cls += ' col-xs-' + this.labelxs;
40571                         container.cls += ' col-xs-' + (12 - this.labelxs);
40572                     }
40573                 }
40574             }
40575             
40576             cfg.cn = [
40577                 label,
40578                 container
40579             ];
40580             
40581             var settings = this;
40582             
40583             ['xs','sm','md','lg'].map(function(size){
40584                 if (settings[size]) {
40585                     cfg.cls += ' col-' + size + '-' + settings[size];
40586                 }
40587             });
40588             
40589             this.store = new Roo.data.Store({
40590                 proxy : new Roo.data.MemoryProxy({}),
40591                 reader : new Roo.data.JsonReader({
40592                     fields : [
40593                         {
40594                             'name' : 'name',
40595                             'type' : 'string'
40596                         },
40597                         {
40598                             'name' : 'iso2',
40599                             'type' : 'string'
40600                         },
40601                         {
40602                             'name' : 'dialCode',
40603                             'type' : 'string'
40604                         },
40605                         {
40606                             'name' : 'priority',
40607                             'type' : 'string'
40608                         },
40609                         {
40610                             'name' : 'areaCodes',
40611                             'type' : 'string'
40612                         }
40613                     ]
40614                 })
40615             });
40616             
40617             if(!this.preferedCountries) {
40618                 this.preferedCountries = [
40619                     'hk',
40620                     'gb',
40621                     'us'
40622                 ];
40623             }
40624             
40625             var p = this.preferedCountries.reverse();
40626             
40627             if(p) {
40628                 for (var i = 0; i < p.length; i++) {
40629                     for (var j = 0; j < this.allCountries.length; j++) {
40630                         if(this.allCountries[j].iso2 == p[i]) {
40631                             var t = this.allCountries[j];
40632                             this.allCountries.splice(j,1);
40633                             this.allCountries.unshift(t);
40634                         }
40635                     } 
40636                 }
40637             }
40638             
40639             this.store.proxy.data = {
40640                 success: true,
40641                 data: this.allCountries
40642             };
40643             
40644             return cfg;
40645         },
40646         
40647         initEvents : function()
40648         {
40649             this.createList();
40650             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40651             
40652             this.indicator = this.indicatorEl();
40653             this.flag = this.flagEl();
40654             this.dialCodeHolder = this.dialCodeHolderEl();
40655             
40656             this.trigger = this.el.select('div.flag-box',true).first();
40657             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40658             
40659             var _this = this;
40660             
40661             (function(){
40662                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40663                 _this.list.setWidth(lw);
40664             }).defer(100);
40665             
40666             this.list.on('mouseover', this.onViewOver, this);
40667             this.list.on('mousemove', this.onViewMove, this);
40668             this.inputEl().on("keyup", this.onKeyUp, this);
40669             this.inputEl().on("keypress", this.onKeyPress, this);
40670             
40671             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40672
40673             this.view = new Roo.View(this.list, this.tpl, {
40674                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40675             });
40676             
40677             this.view.on('click', this.onViewClick, this);
40678             this.setValue(this.defaultDialCode);
40679         },
40680         
40681         onTriggerClick : function(e)
40682         {
40683             Roo.log('trigger click');
40684             if(this.disabled){
40685                 return;
40686             }
40687             
40688             if(this.isExpanded()){
40689                 this.collapse();
40690                 this.hasFocus = false;
40691             }else {
40692                 this.store.load({});
40693                 this.hasFocus = true;
40694                 this.expand();
40695             }
40696         },
40697         
40698         isExpanded : function()
40699         {
40700             return this.list.isVisible();
40701         },
40702         
40703         collapse : function()
40704         {
40705             if(!this.isExpanded()){
40706                 return;
40707             }
40708             this.list.hide();
40709             Roo.get(document).un('mousedown', this.collapseIf, this);
40710             Roo.get(document).un('mousewheel', this.collapseIf, this);
40711             this.fireEvent('collapse', this);
40712             this.validate();
40713         },
40714         
40715         expand : function()
40716         {
40717             Roo.log('expand');
40718
40719             if(this.isExpanded() || !this.hasFocus){
40720                 return;
40721             }
40722             
40723             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40724             this.list.setWidth(lw);
40725             
40726             this.list.show();
40727             this.restrictHeight();
40728             
40729             Roo.get(document).on('mousedown', this.collapseIf, this);
40730             Roo.get(document).on('mousewheel', this.collapseIf, this);
40731             
40732             this.fireEvent('expand', this);
40733         },
40734         
40735         restrictHeight : function()
40736         {
40737             this.list.alignTo(this.inputEl(), this.listAlign);
40738             this.list.alignTo(this.inputEl(), this.listAlign);
40739         },
40740         
40741         onViewOver : function(e, t)
40742         {
40743             if(this.inKeyMode){
40744                 return;
40745             }
40746             var item = this.view.findItemFromChild(t);
40747             
40748             if(item){
40749                 var index = this.view.indexOf(item);
40750                 this.select(index, false);
40751             }
40752         },
40753
40754         // private
40755         onViewClick : function(view, doFocus, el, e)
40756         {
40757             var index = this.view.getSelectedIndexes()[0];
40758             
40759             var r = this.store.getAt(index);
40760             
40761             if(r){
40762                 this.onSelect(r, index);
40763             }
40764             if(doFocus !== false && !this.blockFocus){
40765                 this.inputEl().focus();
40766             }
40767         },
40768         
40769         onViewMove : function(e, t)
40770         {
40771             this.inKeyMode = false;
40772         },
40773         
40774         select : function(index, scrollIntoView)
40775         {
40776             this.selectedIndex = index;
40777             this.view.select(index);
40778             if(scrollIntoView !== false){
40779                 var el = this.view.getNode(index);
40780                 if(el){
40781                     this.list.scrollChildIntoView(el, false);
40782                 }
40783             }
40784         },
40785         
40786         createList : function()
40787         {
40788             this.list = Roo.get(document.body).createChild({
40789                 tag: 'ul',
40790                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40791                 style: 'display:none'
40792             });
40793             
40794             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40795         },
40796         
40797         collapseIf : function(e)
40798         {
40799             var in_combo  = e.within(this.el);
40800             var in_list =  e.within(this.list);
40801             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40802             
40803             if (in_combo || in_list || is_list) {
40804                 return;
40805             }
40806             this.collapse();
40807         },
40808         
40809         onSelect : function(record, index)
40810         {
40811             if(this.fireEvent('beforeselect', this, record, index) !== false){
40812                 
40813                 this.setFlagClass(record.data.iso2);
40814                 this.setDialCode(record.data.dialCode);
40815                 this.hasFocus = false;
40816                 this.collapse();
40817                 this.fireEvent('select', this, record, index);
40818             }
40819         },
40820         
40821         flagEl : function()
40822         {
40823             var flag = this.el.select('div.flag',true).first();
40824             if(!flag){
40825                 return false;
40826             }
40827             return flag;
40828         },
40829         
40830         dialCodeHolderEl : function()
40831         {
40832             var d = this.el.select('input.dial-code-holder',true).first();
40833             if(!d){
40834                 return false;
40835             }
40836             return d;
40837         },
40838         
40839         setDialCode : function(v)
40840         {
40841             this.dialCodeHolder.dom.value = '+'+v;
40842         },
40843         
40844         setFlagClass : function(n)
40845         {
40846             this.flag.dom.className = 'flag '+n;
40847         },
40848         
40849         getValue : function()
40850         {
40851             var v = this.inputEl().getValue();
40852             if(this.dialCodeHolder) {
40853                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40854             }
40855             return v;
40856         },
40857         
40858         setValue : function(v)
40859         {
40860             var d = this.getDialCode(v);
40861             
40862             //invalid dial code
40863             if(v.length == 0 || !d || d.length == 0) {
40864                 if(this.rendered){
40865                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40866                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40867                 }
40868                 return;
40869             }
40870             
40871             //valid dial code
40872             this.setFlagClass(this.dialCodeMapping[d].iso2);
40873             this.setDialCode(d);
40874             this.inputEl().dom.value = v.replace('+'+d,'');
40875             this.hiddenEl().dom.value = this.getValue();
40876             
40877             this.validate();
40878         },
40879         
40880         getDialCode : function(v)
40881         {
40882             v = v ||  '';
40883             
40884             if (v.length == 0) {
40885                 return this.dialCodeHolder.dom.value;
40886             }
40887             
40888             var dialCode = "";
40889             if (v.charAt(0) != "+") {
40890                 return false;
40891             }
40892             var numericChars = "";
40893             for (var i = 1; i < v.length; i++) {
40894               var c = v.charAt(i);
40895               if (!isNaN(c)) {
40896                 numericChars += c;
40897                 if (this.dialCodeMapping[numericChars]) {
40898                   dialCode = v.substr(1, i);
40899                 }
40900                 if (numericChars.length == 4) {
40901                   break;
40902                 }
40903               }
40904             }
40905             return dialCode;
40906         },
40907         
40908         reset : function()
40909         {
40910             this.setValue(this.defaultDialCode);
40911             this.validate();
40912         },
40913         
40914         hiddenEl : function()
40915         {
40916             return this.el.select('input.hidden-tel-input',true).first();
40917         },
40918         
40919         // after setting val
40920         onKeyUp : function(e){
40921             this.setValue(this.getValue());
40922         },
40923         
40924         onKeyPress : function(e){
40925             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40926                 e.stopEvent();
40927             }
40928         }
40929         
40930 });
40931 /**
40932  * @class Roo.bootstrap.MoneyField
40933  * @extends Roo.bootstrap.ComboBox
40934  * Bootstrap MoneyField class
40935  * 
40936  * @constructor
40937  * Create a new MoneyField.
40938  * @param {Object} config Configuration options
40939  */
40940
40941 Roo.bootstrap.MoneyField = function(config) {
40942     
40943     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40944     
40945 };
40946
40947 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40948     
40949     /**
40950      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40951      */
40952     allowDecimals : true,
40953     /**
40954      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40955      */
40956     decimalSeparator : ".",
40957     /**
40958      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40959      */
40960     decimalPrecision : 0,
40961     /**
40962      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40963      */
40964     allowNegative : true,
40965     /**
40966      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40967      */
40968     allowZero: true,
40969     /**
40970      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40971      */
40972     minValue : Number.NEGATIVE_INFINITY,
40973     /**
40974      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40975      */
40976     maxValue : Number.MAX_VALUE,
40977     /**
40978      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40979      */
40980     minText : "The minimum value for this field is {0}",
40981     /**
40982      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40983      */
40984     maxText : "The maximum value for this field is {0}",
40985     /**
40986      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40987      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40988      */
40989     nanText : "{0} is not a valid number",
40990     /**
40991      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40992      */
40993     castInt : true,
40994     /**
40995      * @cfg {String} defaults currency of the MoneyField
40996      * value should be in lkey
40997      */
40998     defaultCurrency : false,
40999     /**
41000      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41001      */
41002     thousandsDelimiter : false,
41003     /**
41004      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41005      */
41006     max_length: false,
41007     
41008     inputlg : 9,
41009     inputmd : 9,
41010     inputsm : 9,
41011     inputxs : 6,
41012     
41013     store : false,
41014     
41015     getAutoCreate : function()
41016     {
41017         var align = this.labelAlign || this.parentLabelAlign();
41018         
41019         var id = Roo.id();
41020
41021         var cfg = {
41022             cls: 'form-group',
41023             cn: []
41024         };
41025
41026         var input =  {
41027             tag: 'input',
41028             id : id,
41029             cls : 'form-control roo-money-amount-input',
41030             autocomplete: 'new-password'
41031         };
41032         
41033         var hiddenInput = {
41034             tag: 'input',
41035             type: 'hidden',
41036             id: Roo.id(),
41037             cls: 'hidden-number-input'
41038         };
41039         
41040         if(this.max_length) {
41041             input.maxlength = this.max_length; 
41042         }
41043         
41044         if (this.name) {
41045             hiddenInput.name = this.name;
41046         }
41047
41048         if (this.disabled) {
41049             input.disabled = true;
41050         }
41051
41052         var clg = 12 - this.inputlg;
41053         var cmd = 12 - this.inputmd;
41054         var csm = 12 - this.inputsm;
41055         var cxs = 12 - this.inputxs;
41056         
41057         var container = {
41058             tag : 'div',
41059             cls : 'row roo-money-field',
41060             cn : [
41061                 {
41062                     tag : 'div',
41063                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41064                     cn : [
41065                         {
41066                             tag : 'div',
41067                             cls: 'roo-select2-container input-group',
41068                             cn: [
41069                                 {
41070                                     tag : 'input',
41071                                     cls : 'form-control roo-money-currency-input',
41072                                     autocomplete: 'new-password',
41073                                     readOnly : 1,
41074                                     name : this.currencyName
41075                                 },
41076                                 {
41077                                     tag :'span',
41078                                     cls : 'input-group-addon',
41079                                     cn : [
41080                                         {
41081                                             tag: 'span',
41082                                             cls: 'caret'
41083                                         }
41084                                     ]
41085                                 }
41086                             ]
41087                         }
41088                     ]
41089                 },
41090                 {
41091                     tag : 'div',
41092                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41093                     cn : [
41094                         {
41095                             tag: 'div',
41096                             cls: this.hasFeedback ? 'has-feedback' : '',
41097                             cn: [
41098                                 input
41099                             ]
41100                         }
41101                     ]
41102                 }
41103             ]
41104             
41105         };
41106         
41107         if (this.fieldLabel.length) {
41108             var indicator = {
41109                 tag: 'i',
41110                 tooltip: 'This field is required'
41111             };
41112
41113             var label = {
41114                 tag: 'label',
41115                 'for':  id,
41116                 cls: 'control-label',
41117                 cn: []
41118             };
41119
41120             var label_text = {
41121                 tag: 'span',
41122                 html: this.fieldLabel
41123             };
41124
41125             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41126             label.cn = [
41127                 indicator,
41128                 label_text
41129             ];
41130
41131             if(this.indicatorpos == 'right') {
41132                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41133                 label.cn = [
41134                     label_text,
41135                     indicator
41136                 ];
41137             }
41138
41139             if(align == 'left') {
41140                 container = {
41141                     tag: 'div',
41142                     cn: [
41143                         container
41144                     ]
41145                 };
41146
41147                 if(this.labelWidth > 12){
41148                     label.style = "width: " + this.labelWidth + 'px';
41149                 }
41150                 if(this.labelWidth < 13 && this.labelmd == 0){
41151                     this.labelmd = this.labelWidth;
41152                 }
41153                 if(this.labellg > 0){
41154                     label.cls += ' col-lg-' + this.labellg;
41155                     input.cls += ' col-lg-' + (12 - this.labellg);
41156                 }
41157                 if(this.labelmd > 0){
41158                     label.cls += ' col-md-' + this.labelmd;
41159                     container.cls += ' col-md-' + (12 - this.labelmd);
41160                 }
41161                 if(this.labelsm > 0){
41162                     label.cls += ' col-sm-' + this.labelsm;
41163                     container.cls += ' col-sm-' + (12 - this.labelsm);
41164                 }
41165                 if(this.labelxs > 0){
41166                     label.cls += ' col-xs-' + this.labelxs;
41167                     container.cls += ' col-xs-' + (12 - this.labelxs);
41168                 }
41169             }
41170         }
41171
41172         cfg.cn = [
41173             label,
41174             container,
41175             hiddenInput
41176         ];
41177         
41178         var settings = this;
41179
41180         ['xs','sm','md','lg'].map(function(size){
41181             if (settings[size]) {
41182                 cfg.cls += ' col-' + size + '-' + settings[size];
41183             }
41184         });
41185         
41186         return cfg;
41187     },
41188     
41189     initEvents : function()
41190     {
41191         this.indicator = this.indicatorEl();
41192         
41193         this.initCurrencyEvent();
41194         
41195         this.initNumberEvent();
41196     },
41197     
41198     initCurrencyEvent : function()
41199     {
41200         if (!this.store) {
41201             throw "can not find store for combo";
41202         }
41203         
41204         this.store = Roo.factory(this.store, Roo.data);
41205         this.store.parent = this;
41206         
41207         this.createList();
41208         
41209         this.triggerEl = this.el.select('.input-group-addon', true).first();
41210         
41211         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41212         
41213         var _this = this;
41214         
41215         (function(){
41216             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41217             _this.list.setWidth(lw);
41218         }).defer(100);
41219         
41220         this.list.on('mouseover', this.onViewOver, this);
41221         this.list.on('mousemove', this.onViewMove, this);
41222         this.list.on('scroll', this.onViewScroll, this);
41223         
41224         if(!this.tpl){
41225             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41226         }
41227         
41228         this.view = new Roo.View(this.list, this.tpl, {
41229             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41230         });
41231         
41232         this.view.on('click', this.onViewClick, this);
41233         
41234         this.store.on('beforeload', this.onBeforeLoad, this);
41235         this.store.on('load', this.onLoad, this);
41236         this.store.on('loadexception', this.onLoadException, this);
41237         
41238         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41239             "up" : function(e){
41240                 this.inKeyMode = true;
41241                 this.selectPrev();
41242             },
41243
41244             "down" : function(e){
41245                 if(!this.isExpanded()){
41246                     this.onTriggerClick();
41247                 }else{
41248                     this.inKeyMode = true;
41249                     this.selectNext();
41250                 }
41251             },
41252
41253             "enter" : function(e){
41254                 this.collapse();
41255                 
41256                 if(this.fireEvent("specialkey", this, e)){
41257                     this.onViewClick(false);
41258                 }
41259                 
41260                 return true;
41261             },
41262
41263             "esc" : function(e){
41264                 this.collapse();
41265             },
41266
41267             "tab" : function(e){
41268                 this.collapse();
41269                 
41270                 if(this.fireEvent("specialkey", this, e)){
41271                     this.onViewClick(false);
41272                 }
41273                 
41274                 return true;
41275             },
41276
41277             scope : this,
41278
41279             doRelay : function(foo, bar, hname){
41280                 if(hname == 'down' || this.scope.isExpanded()){
41281                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41282                 }
41283                 return true;
41284             },
41285
41286             forceKeyDown: true
41287         });
41288         
41289         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41290         
41291     },
41292     
41293     initNumberEvent : function(e)
41294     {
41295         this.inputEl().on("keydown" , this.fireKey,  this);
41296         this.inputEl().on("focus", this.onFocus,  this);
41297         this.inputEl().on("blur", this.onBlur,  this);
41298         
41299         this.inputEl().relayEvent('keyup', this);
41300         
41301         if(this.indicator){
41302             this.indicator.addClass('invisible');
41303         }
41304  
41305         this.originalValue = this.getValue();
41306         
41307         if(this.validationEvent == 'keyup'){
41308             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41309             this.inputEl().on('keyup', this.filterValidation, this);
41310         }
41311         else if(this.validationEvent !== false){
41312             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41313         }
41314         
41315         if(this.selectOnFocus){
41316             this.on("focus", this.preFocus, this);
41317             
41318         }
41319         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41320             this.inputEl().on("keypress", this.filterKeys, this);
41321         } else {
41322             this.inputEl().relayEvent('keypress', this);
41323         }
41324         
41325         var allowed = "0123456789";
41326         
41327         if(this.allowDecimals){
41328             allowed += this.decimalSeparator;
41329         }
41330         
41331         if(this.allowNegative){
41332             allowed += "-";
41333         }
41334         
41335         if(this.thousandsDelimiter) {
41336             allowed += ",";
41337         }
41338         
41339         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41340         
41341         var keyPress = function(e){
41342             
41343             var k = e.getKey();
41344             
41345             var c = e.getCharCode();
41346             
41347             if(
41348                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41349                     allowed.indexOf(String.fromCharCode(c)) === -1
41350             ){
41351                 e.stopEvent();
41352                 return;
41353             }
41354             
41355             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41356                 return;
41357             }
41358             
41359             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41360                 e.stopEvent();
41361             }
41362         };
41363         
41364         this.inputEl().on("keypress", keyPress, this);
41365         
41366     },
41367     
41368     onTriggerClick : function(e)
41369     {   
41370         if(this.disabled){
41371             return;
41372         }
41373         
41374         this.page = 0;
41375         this.loadNext = false;
41376         
41377         if(this.isExpanded()){
41378             this.collapse();
41379             return;
41380         }
41381         
41382         this.hasFocus = true;
41383         
41384         if(this.triggerAction == 'all') {
41385             this.doQuery(this.allQuery, true);
41386             return;
41387         }
41388         
41389         this.doQuery(this.getRawValue());
41390     },
41391     
41392     getCurrency : function()
41393     {   
41394         var v = this.currencyEl().getValue();
41395         
41396         return v;
41397     },
41398     
41399     restrictHeight : function()
41400     {
41401         this.list.alignTo(this.currencyEl(), this.listAlign);
41402         this.list.alignTo(this.currencyEl(), this.listAlign);
41403     },
41404     
41405     onViewClick : function(view, doFocus, el, e)
41406     {
41407         var index = this.view.getSelectedIndexes()[0];
41408         
41409         var r = this.store.getAt(index);
41410         
41411         if(r){
41412             this.onSelect(r, index);
41413         }
41414     },
41415     
41416     onSelect : function(record, index){
41417         
41418         if(this.fireEvent('beforeselect', this, record, index) !== false){
41419         
41420             this.setFromCurrencyData(index > -1 ? record.data : false);
41421             
41422             this.collapse();
41423             
41424             this.fireEvent('select', this, record, index);
41425         }
41426     },
41427     
41428     setFromCurrencyData : function(o)
41429     {
41430         var currency = '';
41431         
41432         this.lastCurrency = o;
41433         
41434         if (this.currencyField) {
41435             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41436         } else {
41437             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41438         }
41439         
41440         this.lastSelectionText = currency;
41441         
41442         //setting default currency
41443         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41444             this.setCurrency(this.defaultCurrency);
41445             return;
41446         }
41447         
41448         this.setCurrency(currency);
41449     },
41450     
41451     setFromData : function(o)
41452     {
41453         var c = {};
41454         
41455         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41456         
41457         this.setFromCurrencyData(c);
41458         
41459         var value = '';
41460         
41461         if (this.name) {
41462             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41463         } else {
41464             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41465         }
41466         
41467         this.setValue(value);
41468         
41469     },
41470     
41471     setCurrency : function(v)
41472     {   
41473         this.currencyValue = v;
41474         
41475         if(this.rendered){
41476             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41477             this.validate();
41478         }
41479     },
41480     
41481     setValue : function(v)
41482     {
41483         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41484         
41485         this.value = v;
41486         
41487         if(this.rendered){
41488             
41489             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41490             
41491             this.inputEl().dom.value = (v == '') ? '' :
41492                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41493             
41494             if(!this.allowZero && v === '0') {
41495                 this.hiddenEl().dom.value = '';
41496                 this.inputEl().dom.value = '';
41497             }
41498             
41499             this.validate();
41500         }
41501     },
41502     
41503     getRawValue : function()
41504     {
41505         var v = this.inputEl().getValue();
41506         
41507         return v;
41508     },
41509     
41510     getValue : function()
41511     {
41512         return this.fixPrecision(this.parseValue(this.getRawValue()));
41513     },
41514     
41515     parseValue : function(value)
41516     {
41517         if(this.thousandsDelimiter) {
41518             value += "";
41519             r = new RegExp(",", "g");
41520             value = value.replace(r, "");
41521         }
41522         
41523         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41524         return isNaN(value) ? '' : value;
41525         
41526     },
41527     
41528     fixPrecision : function(value)
41529     {
41530         if(this.thousandsDelimiter) {
41531             value += "";
41532             r = new RegExp(",", "g");
41533             value = value.replace(r, "");
41534         }
41535         
41536         var nan = isNaN(value);
41537         
41538         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41539             return nan ? '' : value;
41540         }
41541         return parseFloat(value).toFixed(this.decimalPrecision);
41542     },
41543     
41544     decimalPrecisionFcn : function(v)
41545     {
41546         return Math.floor(v);
41547     },
41548     
41549     validateValue : function(value)
41550     {
41551         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41552             return false;
41553         }
41554         
41555         var num = this.parseValue(value);
41556         
41557         if(isNaN(num)){
41558             this.markInvalid(String.format(this.nanText, value));
41559             return false;
41560         }
41561         
41562         if(num < this.minValue){
41563             this.markInvalid(String.format(this.minText, this.minValue));
41564             return false;
41565         }
41566         
41567         if(num > this.maxValue){
41568             this.markInvalid(String.format(this.maxText, this.maxValue));
41569             return false;
41570         }
41571         
41572         return true;
41573     },
41574     
41575     validate : function()
41576     {
41577         if(this.disabled || this.allowBlank){
41578             this.markValid();
41579             return true;
41580         }
41581         
41582         var currency = this.getCurrency();
41583         
41584         if(this.validateValue(this.getRawValue()) && currency.length){
41585             this.markValid();
41586             return true;
41587         }
41588         
41589         this.markInvalid();
41590         return false;
41591     },
41592     
41593     getName: function()
41594     {
41595         return this.name;
41596     },
41597     
41598     beforeBlur : function()
41599     {
41600         if(!this.castInt){
41601             return;
41602         }
41603         
41604         var v = this.parseValue(this.getRawValue());
41605         
41606         if(v || v == 0){
41607             this.setValue(v);
41608         }
41609     },
41610     
41611     onBlur : function()
41612     {
41613         this.beforeBlur();
41614         
41615         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41616             //this.el.removeClass(this.focusClass);
41617         }
41618         
41619         this.hasFocus = false;
41620         
41621         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41622             this.validate();
41623         }
41624         
41625         var v = this.getValue();
41626         
41627         if(String(v) !== String(this.startValue)){
41628             this.fireEvent('change', this, v, this.startValue);
41629         }
41630         
41631         this.fireEvent("blur", this);
41632     },
41633     
41634     inputEl : function()
41635     {
41636         return this.el.select('.roo-money-amount-input', true).first();
41637     },
41638     
41639     currencyEl : function()
41640     {
41641         return this.el.select('.roo-money-currency-input', true).first();
41642     },
41643     
41644     hiddenEl : function()
41645     {
41646         return this.el.select('input.hidden-number-input',true).first();
41647     }
41648     
41649 });/**
41650  * @class Roo.bootstrap.BezierSignature
41651  * @extends Roo.bootstrap.Component
41652  * Bootstrap BezierSignature class
41653  * This script refer to:
41654  *    Title: Signature Pad
41655  *    Author: szimek
41656  *    Availability: https://github.com/szimek/signature_pad
41657  *
41658  * @constructor
41659  * Create a new BezierSignature
41660  * @param {Object} config The config object
41661  */
41662
41663 Roo.bootstrap.BezierSignature = function(config){
41664     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41665     this.addEvents({
41666         "resize" : true
41667     });
41668 };
41669
41670 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41671 {
41672      
41673     curve_data: [],
41674     
41675     is_empty: true,
41676     
41677     mouse_btn_down: true,
41678     
41679     /**
41680      * @cfg {int} canvas height
41681      */
41682     canvas_height: '200px',
41683     
41684     /**
41685      * @cfg {float|function} Radius of a single dot.
41686      */ 
41687     dot_size: false,
41688     
41689     /**
41690      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41691      */
41692     min_width: 0.5,
41693     
41694     /**
41695      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41696      */
41697     max_width: 2.5,
41698     
41699     /**
41700      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41701      */
41702     throttle: 16,
41703     
41704     /**
41705      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41706      */
41707     min_distance: 5,
41708     
41709     /**
41710      * @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.
41711      */
41712     bg_color: 'rgba(0, 0, 0, 0)',
41713     
41714     /**
41715      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41716      */
41717     dot_color: 'black',
41718     
41719     /**
41720      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41721      */ 
41722     velocity_filter_weight: 0.7,
41723     
41724     /**
41725      * @cfg {function} Callback when stroke begin. 
41726      */
41727     onBegin: false,
41728     
41729     /**
41730      * @cfg {function} Callback when stroke end.
41731      */
41732     onEnd: false,
41733     
41734     getAutoCreate : function()
41735     {
41736         var cls = 'roo-signature column';
41737         
41738         if(this.cls){
41739             cls += ' ' + this.cls;
41740         }
41741         
41742         var col_sizes = [
41743             'lg',
41744             'md',
41745             'sm',
41746             'xs'
41747         ];
41748         
41749         for(var i = 0; i < col_sizes.length; i++) {
41750             if(this[col_sizes[i]]) {
41751                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41752             }
41753         }
41754         
41755         var cfg = {
41756             tag: 'div',
41757             cls: cls,
41758             cn: [
41759                 {
41760                     tag: 'div',
41761                     cls: 'roo-signature-body',
41762                     cn: [
41763                         {
41764                             tag: 'canvas',
41765                             cls: 'roo-signature-body-canvas',
41766                             height: this.canvas_height,
41767                             width: this.canvas_width
41768                         }
41769                     ]
41770                 },
41771                 {
41772                     tag: 'input',
41773                     type: 'file',
41774                     style: 'display: none'
41775                 }
41776             ]
41777         };
41778         
41779         return cfg;
41780     },
41781     
41782     initEvents: function() 
41783     {
41784         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41785         
41786         var canvas = this.canvasEl();
41787         
41788         // mouse && touch event swapping...
41789         canvas.dom.style.touchAction = 'none';
41790         canvas.dom.style.msTouchAction = 'none';
41791         
41792         this.mouse_btn_down = false;
41793         canvas.on('mousedown', this._handleMouseDown, this);
41794         canvas.on('mousemove', this._handleMouseMove, this);
41795         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41796         
41797         if (window.PointerEvent) {
41798             canvas.on('pointerdown', this._handleMouseDown, this);
41799             canvas.on('pointermove', this._handleMouseMove, this);
41800             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41801         }
41802         
41803         if ('ontouchstart' in window) {
41804             canvas.on('touchstart', this._handleTouchStart, this);
41805             canvas.on('touchmove', this._handleTouchMove, this);
41806             canvas.on('touchend', this._handleTouchEnd, this);
41807         }
41808         
41809         Roo.EventManager.onWindowResize(this.resize, this, true);
41810         
41811         // file input event
41812         this.fileEl().on('change', this.uploadImage, this);
41813         
41814         this.clear();
41815         
41816         this.resize();
41817     },
41818     
41819     resize: function(){
41820         
41821         var canvas = this.canvasEl().dom;
41822         var ctx = this.canvasElCtx();
41823         var img_data = false;
41824         
41825         if(canvas.width > 0) {
41826             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41827         }
41828         // setting canvas width will clean img data
41829         canvas.width = 0;
41830         
41831         var style = window.getComputedStyle ? 
41832             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41833             
41834         var padding_left = parseInt(style.paddingLeft) || 0;
41835         var padding_right = parseInt(style.paddingRight) || 0;
41836         
41837         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41838         
41839         if(img_data) {
41840             ctx.putImageData(img_data, 0, 0);
41841         }
41842     },
41843     
41844     _handleMouseDown: function(e)
41845     {
41846         if (e.browserEvent.which === 1) {
41847             this.mouse_btn_down = true;
41848             this.strokeBegin(e);
41849         }
41850     },
41851     
41852     _handleMouseMove: function (e)
41853     {
41854         if (this.mouse_btn_down) {
41855             this.strokeMoveUpdate(e);
41856         }
41857     },
41858     
41859     _handleMouseUp: function (e)
41860     {
41861         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41862             this.mouse_btn_down = false;
41863             this.strokeEnd(e);
41864         }
41865     },
41866     
41867     _handleTouchStart: function (e) {
41868         
41869         e.preventDefault();
41870         if (e.browserEvent.targetTouches.length === 1) {
41871             // var touch = e.browserEvent.changedTouches[0];
41872             // this.strokeBegin(touch);
41873             
41874              this.strokeBegin(e); // assume e catching the correct xy...
41875         }
41876     },
41877     
41878     _handleTouchMove: function (e) {
41879         e.preventDefault();
41880         // var touch = event.targetTouches[0];
41881         // _this._strokeMoveUpdate(touch);
41882         this.strokeMoveUpdate(e);
41883     },
41884     
41885     _handleTouchEnd: function (e) {
41886         var wasCanvasTouched = e.target === this.canvasEl().dom;
41887         if (wasCanvasTouched) {
41888             e.preventDefault();
41889             // var touch = event.changedTouches[0];
41890             // _this._strokeEnd(touch);
41891             this.strokeEnd(e);
41892         }
41893     },
41894     
41895     reset: function () {
41896         this._lastPoints = [];
41897         this._lastVelocity = 0;
41898         this._lastWidth = (this.min_width + this.max_width) / 2;
41899         this.canvasElCtx().fillStyle = this.dot_color;
41900     },
41901     
41902     strokeMoveUpdate: function(e)
41903     {
41904         this.strokeUpdate(e);
41905         
41906         if (this.throttle) {
41907             this.throttleStroke(this.strokeUpdate, this.throttle);
41908         }
41909         else {
41910             this.strokeUpdate(e);
41911         }
41912     },
41913     
41914     strokeBegin: function(e)
41915     {
41916         var newPointGroup = {
41917             color: this.dot_color,
41918             points: []
41919         };
41920         
41921         if (typeof this.onBegin === 'function') {
41922             this.onBegin(e);
41923         }
41924         
41925         this.curve_data.push(newPointGroup);
41926         this.reset();
41927         this.strokeUpdate(e);
41928     },
41929     
41930     strokeUpdate: function(e)
41931     {
41932         var rect = this.canvasEl().dom.getBoundingClientRect();
41933         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41934         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41935         var lastPoints = lastPointGroup.points;
41936         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41937         var isLastPointTooClose = lastPoint
41938             ? point.distanceTo(lastPoint) <= this.min_distance
41939             : false;
41940         var color = lastPointGroup.color;
41941         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41942             var curve = this.addPoint(point);
41943             if (!lastPoint) {
41944                 this.drawDot({color: color, point: point});
41945             }
41946             else if (curve) {
41947                 this.drawCurve({color: color, curve: curve});
41948             }
41949             lastPoints.push({
41950                 time: point.time,
41951                 x: point.x,
41952                 y: point.y
41953             });
41954         }
41955     },
41956     
41957     strokeEnd: function(e)
41958     {
41959         this.strokeUpdate(e);
41960         if (typeof this.onEnd === 'function') {
41961             this.onEnd(e);
41962         }
41963     },
41964     
41965     addPoint:  function (point) {
41966         var _lastPoints = this._lastPoints;
41967         _lastPoints.push(point);
41968         if (_lastPoints.length > 2) {
41969             if (_lastPoints.length === 3) {
41970                 _lastPoints.unshift(_lastPoints[0]);
41971             }
41972             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41973             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41974             _lastPoints.shift();
41975             return curve;
41976         }
41977         return null;
41978     },
41979     
41980     calculateCurveWidths: function (startPoint, endPoint) {
41981         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41982             (1 - this.velocity_filter_weight) * this._lastVelocity;
41983
41984         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41985         var widths = {
41986             end: newWidth,
41987             start: this._lastWidth
41988         };
41989         
41990         this._lastVelocity = velocity;
41991         this._lastWidth = newWidth;
41992         return widths;
41993     },
41994     
41995     drawDot: function (_a) {
41996         var color = _a.color, point = _a.point;
41997         var ctx = this.canvasElCtx();
41998         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41999         ctx.beginPath();
42000         this.drawCurveSegment(point.x, point.y, width);
42001         ctx.closePath();
42002         ctx.fillStyle = color;
42003         ctx.fill();
42004     },
42005     
42006     drawCurve: function (_a) {
42007         var color = _a.color, curve = _a.curve;
42008         var ctx = this.canvasElCtx();
42009         var widthDelta = curve.endWidth - curve.startWidth;
42010         var drawSteps = Math.floor(curve.length()) * 2;
42011         ctx.beginPath();
42012         ctx.fillStyle = color;
42013         for (var i = 0; i < drawSteps; i += 1) {
42014         var t = i / drawSteps;
42015         var tt = t * t;
42016         var ttt = tt * t;
42017         var u = 1 - t;
42018         var uu = u * u;
42019         var uuu = uu * u;
42020         var x = uuu * curve.startPoint.x;
42021         x += 3 * uu * t * curve.control1.x;
42022         x += 3 * u * tt * curve.control2.x;
42023         x += ttt * curve.endPoint.x;
42024         var y = uuu * curve.startPoint.y;
42025         y += 3 * uu * t * curve.control1.y;
42026         y += 3 * u * tt * curve.control2.y;
42027         y += ttt * curve.endPoint.y;
42028         var width = curve.startWidth + ttt * widthDelta;
42029         this.drawCurveSegment(x, y, width);
42030         }
42031         ctx.closePath();
42032         ctx.fill();
42033     },
42034     
42035     drawCurveSegment: function (x, y, width) {
42036         var ctx = this.canvasElCtx();
42037         ctx.moveTo(x, y);
42038         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42039         this.is_empty = false;
42040     },
42041     
42042     clear: function()
42043     {
42044         var ctx = this.canvasElCtx();
42045         var canvas = this.canvasEl().dom;
42046         ctx.fillStyle = this.bg_color;
42047         ctx.clearRect(0, 0, canvas.width, canvas.height);
42048         ctx.fillRect(0, 0, canvas.width, canvas.height);
42049         this.curve_data = [];
42050         this.reset();
42051         this.is_empty = true;
42052     },
42053     
42054     fileEl: function()
42055     {
42056         return  this.el.select('input',true).first();
42057     },
42058     
42059     canvasEl: function()
42060     {
42061         return this.el.select('canvas',true).first();
42062     },
42063     
42064     canvasElCtx: function()
42065     {
42066         return this.el.select('canvas',true).first().dom.getContext('2d');
42067     },
42068     
42069     getImage: function(type)
42070     {
42071         if(this.is_empty) {
42072             return false;
42073         }
42074         
42075         // encryption ?
42076         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42077     },
42078     
42079     drawFromImage: function(img_src)
42080     {
42081         var img = new Image();
42082         
42083         img.onload = function(){
42084             this.canvasElCtx().drawImage(img, 0, 0);
42085         }.bind(this);
42086         
42087         img.src = img_src;
42088         
42089         this.is_empty = false;
42090     },
42091     
42092     selectImage: function()
42093     {
42094         this.fileEl().dom.click();
42095     },
42096     
42097     uploadImage: function(e)
42098     {
42099         var reader = new FileReader();
42100         
42101         reader.onload = function(e){
42102             var img = new Image();
42103             img.onload = function(){
42104                 this.reset();
42105                 this.canvasElCtx().drawImage(img, 0, 0);
42106             }.bind(this);
42107             img.src = e.target.result;
42108         }.bind(this);
42109         
42110         reader.readAsDataURL(e.target.files[0]);
42111     },
42112     
42113     // Bezier Point Constructor
42114     Point: (function () {
42115         function Point(x, y, time) {
42116             this.x = x;
42117             this.y = y;
42118             this.time = time || Date.now();
42119         }
42120         Point.prototype.distanceTo = function (start) {
42121             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42122         };
42123         Point.prototype.equals = function (other) {
42124             return this.x === other.x && this.y === other.y && this.time === other.time;
42125         };
42126         Point.prototype.velocityFrom = function (start) {
42127             return this.time !== start.time
42128             ? this.distanceTo(start) / (this.time - start.time)
42129             : 0;
42130         };
42131         return Point;
42132     }()),
42133     
42134     
42135     // Bezier Constructor
42136     Bezier: (function () {
42137         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42138             this.startPoint = startPoint;
42139             this.control2 = control2;
42140             this.control1 = control1;
42141             this.endPoint = endPoint;
42142             this.startWidth = startWidth;
42143             this.endWidth = endWidth;
42144         }
42145         Bezier.fromPoints = function (points, widths, scope) {
42146             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42147             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42148             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42149         };
42150         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42151             var dx1 = s1.x - s2.x;
42152             var dy1 = s1.y - s2.y;
42153             var dx2 = s2.x - s3.x;
42154             var dy2 = s2.y - s3.y;
42155             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42156             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42157             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42158             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42159             var dxm = m1.x - m2.x;
42160             var dym = m1.y - m2.y;
42161             var k = l2 / (l1 + l2);
42162             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42163             var tx = s2.x - cm.x;
42164             var ty = s2.y - cm.y;
42165             return {
42166                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42167                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42168             };
42169         };
42170         Bezier.prototype.length = function () {
42171             var steps = 10;
42172             var length = 0;
42173             var px;
42174             var py;
42175             for (var i = 0; i <= steps; i += 1) {
42176                 var t = i / steps;
42177                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42178                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42179                 if (i > 0) {
42180                     var xdiff = cx - px;
42181                     var ydiff = cy - py;
42182                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42183                 }
42184                 px = cx;
42185                 py = cy;
42186             }
42187             return length;
42188         };
42189         Bezier.prototype.point = function (t, start, c1, c2, end) {
42190             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42191             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42192             + (3.0 * c2 * (1.0 - t) * t * t)
42193             + (end * t * t * t);
42194         };
42195         return Bezier;
42196     }()),
42197     
42198     throttleStroke: function(fn, wait) {
42199       if (wait === void 0) { wait = 250; }
42200       var previous = 0;
42201       var timeout = null;
42202       var result;
42203       var storedContext;
42204       var storedArgs;
42205       var later = function () {
42206           previous = Date.now();
42207           timeout = null;
42208           result = fn.apply(storedContext, storedArgs);
42209           if (!timeout) {
42210               storedContext = null;
42211               storedArgs = [];
42212           }
42213       };
42214       return function wrapper() {
42215           var args = [];
42216           for (var _i = 0; _i < arguments.length; _i++) {
42217               args[_i] = arguments[_i];
42218           }
42219           var now = Date.now();
42220           var remaining = wait - (now - previous);
42221           storedContext = this;
42222           storedArgs = args;
42223           if (remaining <= 0 || remaining > wait) {
42224               if (timeout) {
42225                   clearTimeout(timeout);
42226                   timeout = null;
42227               }
42228               previous = now;
42229               result = fn.apply(storedContext, storedArgs);
42230               if (!timeout) {
42231                   storedContext = null;
42232                   storedArgs = [];
42233               }
42234           }
42235           else if (!timeout) {
42236               timeout = window.setTimeout(later, remaining);
42237           }
42238           return result;
42239       };
42240   }
42241   
42242 });
42243
42244  
42245
42246