Roo/bootstrap/ComboBox.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
593  * @cfg {String} badge text for badge
594  * @cfg {String} theme (default|glow)  
595  * @cfg {Boolean} inverse dark themed version
596  * @cfg {Boolean} toggle is it a slidy toggle button
597  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598  * @cfg {String} ontext text for on slidy toggle state
599  * @cfg {String} offtext text for off slidy toggle state
600  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
601  * @cfg {Boolean} removeClass remove the standard class..
602  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
603  * 
604  * @constructor
605  * Create a new button
606  * @param {Object} config The config object
607  */
608
609
610 Roo.bootstrap.Button = function(config){
611     Roo.bootstrap.Button.superclass.constructor.call(this, config);
612     this.weightClass = ["btn-default btn-outline-secondary", 
613                        "btn-primary", 
614                        "btn-success", 
615                        "btn-info", 
616                        "btn-warning",
617                        "btn-danger",
618                        "btn-link"
619                       ],  
620     this.addEvents({
621         // raw events
622         /**
623          * @event click
624          * When a butotn is pressed
625          * @param {Roo.bootstrap.Button} btn
626          * @param {Roo.EventObject} e
627          */
628         "click" : true,
629          /**
630          * @event toggle
631          * After the button has been toggles
632          * @param {Roo.bootstrap.Button} btn
633          * @param {Roo.EventObject} e
634          * @param {boolean} pressed (also available as button.pressed)
635          */
636         "toggle" : true
637     });
638 };
639
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
641     html: false,
642     active: false,
643     weight: '',
644     badge_weight: '',
645     outline : false,
646     size: '',
647     tag: 'button',
648     href: '',
649     disabled: false,
650     isClose: false,
651     glyphicon: '',
652     badge: '',
653     theme: 'default',
654     inverse: false,
655     
656     toggle: false,
657     ontext: 'ON',
658     offtext: 'OFF',
659     defaulton: true,
660     preventDefault: true,
661     removeClass: false,
662     name: false,
663     target: false,
664      
665     pressed : null,
666      
667     
668     getAutoCreate : function(){
669         
670         var cfg = {
671             tag : 'button',
672             cls : 'roo-button',
673             html: ''
674         };
675         
676         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
677             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
678             this.tag = 'button';
679         } else {
680             cfg.tag = this.tag;
681         }
682         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
683         
684         if (this.toggle == true) {
685             cfg={
686                 tag: 'div',
687                 cls: 'slider-frame roo-button',
688                 cn: [
689                     {
690                         tag: 'span',
691                         'data-on-text':'ON',
692                         'data-off-text':'OFF',
693                         cls: 'slider-button',
694                         html: this.offtext
695                     }
696                 ]
697             };
698             
699             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
700                 cfg.cls += ' '+this.weight;
701             }
702             
703             return cfg;
704         }
705         
706         if (this.isClose) {
707             cfg.cls += ' close';
708             
709             cfg["aria-hidden"] = true;
710             
711             cfg.html = "&times;";
712             
713             return cfg;
714         }
715         
716          
717         if (this.theme==='default') {
718             cfg.cls = 'btn roo-button';
719             
720             //if (this.parentType != 'Navbar') {
721             this.weight = this.weight.length ?  this.weight : 'default';
722             //}
723             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
724                 
725                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
726                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
727                 cfg.cls += ' btn-' + outline + weight;
728                 if (this.weight == 'default') {
729                     // BC
730                     cfg.cls += ' btn-' + this.weight;
731                 }
732             }
733         } else if (this.theme==='glow') {
734             
735             cfg.tag = 'a';
736             cfg.cls = 'btn-glow roo-button';
737             
738             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
739                 
740                 cfg.cls += ' ' + this.weight;
741             }
742         }
743    
744         
745         if (this.inverse) {
746             this.cls += ' inverse';
747         }
748         
749         
750         if (this.active || this.pressed === true) {
751             cfg.cls += ' active';
752         }
753         
754         if (this.disabled) {
755             cfg.disabled = 'disabled';
756         }
757         
758         if (this.items) {
759             Roo.log('changing to ul' );
760             cfg.tag = 'ul';
761             this.glyphicon = 'caret';
762         }
763         
764         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
765          
766         //gsRoo.log(this.parentType);
767         if (this.parentType === 'Navbar' && !this.parent().bar) {
768             Roo.log('changing to li?');
769             
770             cfg.tag = 'li';
771             
772             cfg.cls = '';
773             cfg.cn =  [{
774                 tag : 'a',
775                 cls : 'roo-button',
776                 html : this.html,
777                 href : this.href || '#'
778             }];
779             if (this.menu) {
780                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
781                 cfg.cls += ' dropdown';
782             }   
783             
784             delete cfg.html;
785             
786         }
787         
788        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
789         
790         if (this.glyphicon) {
791             cfg.html = ' ' + cfg.html;
792             
793             cfg.cn = [
794                 {
795                     tag: 'span',
796                     cls: 'glyphicon glyphicon-' + this.glyphicon
797                 }
798             ];
799         }
800         
801         if (this.badge) {
802             cfg.html += ' ';
803             
804             cfg.tag = 'a';
805             
806 //            cfg.cls='btn roo-button';
807             
808             cfg.href=this.href;
809             
810             var value = cfg.html;
811             
812             if(this.glyphicon){
813                 value = {
814                             tag: 'span',
815                             cls: 'glyphicon glyphicon-' + this.glyphicon,
816                             html: this.html
817                         };
818                 
819             }
820             var bw = this.badge_weight.length ? this.badge_weight :
821                 (this.weight.length ? this.weight : 'secondary');
822             bw = bw == 'default' ? 'secondary' : bw;
823             
824             cfg.cn = [
825                 value,
826                 {
827                     tag: 'span',
828                     cls: 'badge badge-' + bw,
829                     html: this.badge
830                 }
831             ];
832             
833             cfg.html='';
834         }
835         
836         if (this.menu) {
837             cfg.cls += ' dropdown';
838             cfg.html = typeof(cfg.html) != 'undefined' ?
839                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
840         }
841         
842         if (cfg.tag !== 'a' && this.href !== '') {
843             throw "Tag must be a to set href.";
844         } else if (this.href.length > 0) {
845             cfg.href = this.href;
846         }
847         
848         if(this.removeClass){
849             cfg.cls = '';
850         }
851         
852         if(this.target){
853             cfg.target = this.target;
854         }
855         
856         return cfg;
857     },
858     initEvents: function() {
859        // Roo.log('init events?');
860 //        Roo.log(this.el.dom);
861         // add the menu...
862         
863         if (typeof (this.menu) != 'undefined') {
864             this.menu.parentType = this.xtype;
865             this.menu.triggerEl = this.el;
866             this.addxtype(Roo.apply({}, this.menu));
867         }
868
869
870        if (this.el.hasClass('roo-button')) {
871             this.el.on('click', this.onClick, this);
872        } else {
873             this.el.select('.roo-button').on('click', this.onClick, this);
874        }
875        
876        if(this.removeClass){
877            this.el.on('click', this.onClick, this);
878        }
879        
880        this.el.enableDisplayMode();
881         
882     },
883     onClick : function(e)
884     {
885         if (this.disabled) {
886             return;
887         }
888         
889         Roo.log('button on click ');
890         if(this.preventDefault){
891             e.preventDefault();
892         }
893         
894         if (this.pressed === true || this.pressed === false) {
895             this.toggleActive(e);
896         }
897         
898         
899         this.fireEvent('click', this, e);
900     },
901     
902     /**
903      * Enables this button
904      */
905     enable : function()
906     {
907         this.disabled = false;
908         this.el.removeClass('disabled');
909     },
910     
911     /**
912      * Disable this button
913      */
914     disable : function()
915     {
916         this.disabled = true;
917         this.el.addClass('disabled');
918     },
919      /**
920      * sets the active state on/off, 
921      * @param {Boolean} state (optional) Force a particular state
922      */
923     setActive : function(v) {
924         
925         this.el[v ? 'addClass' : 'removeClass']('active');
926         this.pressed = v;
927     },
928      /**
929      * toggles the current active state 
930      */
931     toggleActive : function(e)
932     {
933         this.setActive(!this.pressed);
934         this.fireEvent('toggle', this, e, !this.pressed);
935     },
936      /**
937      * get the current active state
938      * @return {boolean} true if it's active
939      */
940     isActive : function()
941     {
942         return this.el.hasClass('active');
943     },
944     /**
945      * set the text of the first selected button
946      */
947     setText : function(str)
948     {
949         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
950     },
951     /**
952      * get the text of the first selected button
953      */
954     getText : function()
955     {
956         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
957     },
958     
959     setWeight : function(str)
960     {
961         this.el.removeClass(this.weightClass);
962         this.weight = str;
963         var outline = this.outline ? 'outline-' : '';
964         if (str == 'default') {
965             this.el.addClass('btn-default btn-outline-secondary');        
966             return;
967         }
968         this.el.addClass('btn-' + outline + str);        
969     }
970     
971     
972 });
973
974  /*
975  * - LGPL
976  *
977  * column
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Column
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Column class
985  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
986  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
987  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
988  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
989  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
990  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
991  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
992  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
993  *
994  * 
995  * @cfg {Boolean} hidden (true|false) hide the element
996  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
997  * @cfg {String} fa (ban|check|...) font awesome icon
998  * @cfg {Number} fasize (1|2|....) font awsome size
999
1000  * @cfg {String} icon (info-sign|check|...) glyphicon name
1001
1002  * @cfg {String} html content of column.
1003  * 
1004  * @constructor
1005  * Create a new Column
1006  * @param {Object} config The config object
1007  */
1008
1009 Roo.bootstrap.Column = function(config){
1010     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1011 };
1012
1013 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1014     
1015     xs: false,
1016     sm: false,
1017     md: false,
1018     lg: false,
1019     xsoff: false,
1020     smoff: false,
1021     mdoff: false,
1022     lgoff: false,
1023     html: '',
1024     offset: 0,
1025     alert: false,
1026     fa: false,
1027     icon : false,
1028     hidden : false,
1029     fasize : 1,
1030     
1031     getAutoCreate : function(){
1032         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1033         
1034         cfg = {
1035             tag: 'div',
1036             cls: 'column'
1037         };
1038         
1039         var settings=this;
1040         ['xs','sm','md','lg'].map(function(size){
1041             //Roo.log( size + ':' + settings[size]);
1042             
1043             if (settings[size+'off'] !== false) {
1044                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1045             }
1046             
1047             if (settings[size] === false) {
1048                 return;
1049             }
1050             
1051             if (!settings[size]) { // 0 = hidden
1052                 cfg.cls += ' hidden-' + size;
1053                 return;
1054             }
1055             cfg.cls += ' col-' + size + '-' + settings[size];
1056             
1057         });
1058         
1059         if (this.hidden) {
1060             cfg.cls += ' hidden';
1061         }
1062         
1063         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1064             cfg.cls +=' alert alert-' + this.alert;
1065         }
1066         
1067         
1068         if (this.html.length) {
1069             cfg.html = this.html;
1070         }
1071         if (this.fa) {
1072             var fasize = '';
1073             if (this.fasize > 1) {
1074                 fasize = ' fa-' + this.fasize + 'x';
1075             }
1076             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1077             
1078             
1079         }
1080         if (this.icon) {
1081             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1082         }
1083         
1084         return cfg;
1085     }
1086    
1087 });
1088
1089  
1090
1091  /*
1092  * - LGPL
1093  *
1094  * page container.
1095  * 
1096  */
1097
1098
1099 /**
1100  * @class Roo.bootstrap.Container
1101  * @extends Roo.bootstrap.Component
1102  * Bootstrap Container class
1103  * @cfg {Boolean} jumbotron is it a jumbotron element
1104  * @cfg {String} html content of element
1105  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1106  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1107  * @cfg {String} header content of header (for panel)
1108  * @cfg {String} footer content of footer (for panel)
1109  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1110  * @cfg {String} tag (header|aside|section) type of HTML tag.
1111  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1112  * @cfg {String} fa font awesome icon
1113  * @cfg {String} icon (info-sign|check|...) glyphicon name
1114  * @cfg {Boolean} hidden (true|false) hide the element
1115  * @cfg {Boolean} expandable (true|false) default false
1116  * @cfg {Boolean} expanded (true|false) default true
1117  * @cfg {String} rheader contet on the right of header
1118  * @cfg {Boolean} clickable (true|false) default false
1119
1120  *     
1121  * @constructor
1122  * Create a new Container
1123  * @param {Object} config The config object
1124  */
1125
1126 Roo.bootstrap.Container = function(config){
1127     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1128     
1129     this.addEvents({
1130         // raw events
1131          /**
1132          * @event expand
1133          * After the panel has been expand
1134          * 
1135          * @param {Roo.bootstrap.Container} this
1136          */
1137         "expand" : true,
1138         /**
1139          * @event collapse
1140          * After the panel has been collapsed
1141          * 
1142          * @param {Roo.bootstrap.Container} this
1143          */
1144         "collapse" : true,
1145         /**
1146          * @event click
1147          * When a element is chick
1148          * @param {Roo.bootstrap.Container} this
1149          * @param {Roo.EventObject} e
1150          */
1151         "click" : true
1152     });
1153 };
1154
1155 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1156     
1157     jumbotron : false,
1158     well: '',
1159     panel : '',
1160     header: '',
1161     footer : '',
1162     sticky: '',
1163     tag : false,
1164     alert : false,
1165     fa: false,
1166     icon : false,
1167     expandable : false,
1168     rheader : '',
1169     expanded : true,
1170     clickable: false,
1171   
1172      
1173     getChildContainer : function() {
1174         
1175         if(!this.el){
1176             return false;
1177         }
1178         
1179         if (this.panel.length) {
1180             return this.el.select('.panel-body',true).first();
1181         }
1182         
1183         return this.el;
1184     },
1185     
1186     
1187     getAutoCreate : function(){
1188         
1189         var cfg = {
1190             tag : this.tag || 'div',
1191             html : '',
1192             cls : ''
1193         };
1194         if (this.jumbotron) {
1195             cfg.cls = 'jumbotron';
1196         }
1197         
1198         
1199         
1200         // - this is applied by the parent..
1201         //if (this.cls) {
1202         //    cfg.cls = this.cls + '';
1203         //}
1204         
1205         if (this.sticky.length) {
1206             
1207             var bd = Roo.get(document.body);
1208             if (!bd.hasClass('bootstrap-sticky')) {
1209                 bd.addClass('bootstrap-sticky');
1210                 Roo.select('html',true).setStyle('height', '100%');
1211             }
1212              
1213             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1214         }
1215         
1216         
1217         if (this.well.length) {
1218             switch (this.well) {
1219                 case 'lg':
1220                 case 'sm':
1221                     cfg.cls +=' well well-' +this.well;
1222                     break;
1223                 default:
1224                     cfg.cls +=' well';
1225                     break;
1226             }
1227         }
1228         
1229         if (this.hidden) {
1230             cfg.cls += ' hidden';
1231         }
1232         
1233         
1234         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1235             cfg.cls +=' alert alert-' + this.alert;
1236         }
1237         
1238         var body = cfg;
1239         
1240         if (this.panel.length) {
1241             cfg.cls += ' panel panel-' + this.panel;
1242             cfg.cn = [];
1243             if (this.header.length) {
1244                 
1245                 var h = [];
1246                 
1247                 if(this.expandable){
1248                     
1249                     cfg.cls = cfg.cls + ' expandable';
1250                     
1251                     h.push({
1252                         tag: 'i',
1253                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1254                     });
1255                     
1256                 }
1257                 
1258                 h.push(
1259                     {
1260                         tag: 'span',
1261                         cls : 'panel-title',
1262                         html : (this.expandable ? '&nbsp;' : '') + this.header
1263                     },
1264                     {
1265                         tag: 'span',
1266                         cls: 'panel-header-right',
1267                         html: this.rheader
1268                     }
1269                 );
1270                 
1271                 cfg.cn.push({
1272                     cls : 'panel-heading',
1273                     style : this.expandable ? 'cursor: pointer' : '',
1274                     cn : h
1275                 });
1276                 
1277             }
1278             
1279             body = false;
1280             cfg.cn.push({
1281                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1282                 html : this.html
1283             });
1284             
1285             
1286             if (this.footer.length) {
1287                 cfg.cn.push({
1288                     cls : 'panel-footer',
1289                     html : this.footer
1290                     
1291                 });
1292             }
1293             
1294         }
1295         
1296         if (body) {
1297             body.html = this.html || cfg.html;
1298             // prefix with the icons..
1299             if (this.fa) {
1300                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1301             }
1302             if (this.icon) {
1303                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1304             }
1305             
1306             
1307         }
1308         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1309             cfg.cls =  'container';
1310         }
1311         
1312         return cfg;
1313     },
1314     
1315     initEvents: function() 
1316     {
1317         if(this.expandable){
1318             var headerEl = this.headerEl();
1319         
1320             if(headerEl){
1321                 headerEl.on('click', this.onToggleClick, this);
1322             }
1323         }
1324         
1325         if(this.clickable){
1326             this.el.on('click', this.onClick, this);
1327         }
1328         
1329     },
1330     
1331     onToggleClick : function()
1332     {
1333         var headerEl = this.headerEl();
1334         
1335         if(!headerEl){
1336             return;
1337         }
1338         
1339         if(this.expanded){
1340             this.collapse();
1341             return;
1342         }
1343         
1344         this.expand();
1345     },
1346     
1347     expand : function()
1348     {
1349         if(this.fireEvent('expand', this)) {
1350             
1351             this.expanded = true;
1352             
1353             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1354             
1355             this.el.select('.panel-body',true).first().removeClass('hide');
1356             
1357             var toggleEl = this.toggleEl();
1358
1359             if(!toggleEl){
1360                 return;
1361             }
1362
1363             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1364         }
1365         
1366     },
1367     
1368     collapse : function()
1369     {
1370         if(this.fireEvent('collapse', this)) {
1371             
1372             this.expanded = false;
1373             
1374             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1375             this.el.select('.panel-body',true).first().addClass('hide');
1376         
1377             var toggleEl = this.toggleEl();
1378
1379             if(!toggleEl){
1380                 return;
1381             }
1382
1383             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1384         }
1385     },
1386     
1387     toggleEl : function()
1388     {
1389         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1390             return;
1391         }
1392         
1393         return this.el.select('.panel-heading .fa',true).first();
1394     },
1395     
1396     headerEl : function()
1397     {
1398         if(!this.el || !this.panel.length || !this.header.length){
1399             return;
1400         }
1401         
1402         return this.el.select('.panel-heading',true).first()
1403     },
1404     
1405     bodyEl : function()
1406     {
1407         if(!this.el || !this.panel.length){
1408             return;
1409         }
1410         
1411         return this.el.select('.panel-body',true).first()
1412     },
1413     
1414     titleEl : function()
1415     {
1416         if(!this.el || !this.panel.length || !this.header.length){
1417             return;
1418         }
1419         
1420         return this.el.select('.panel-title',true).first();
1421     },
1422     
1423     setTitle : function(v)
1424     {
1425         var titleEl = this.titleEl();
1426         
1427         if(!titleEl){
1428             return;
1429         }
1430         
1431         titleEl.dom.innerHTML = v;
1432     },
1433     
1434     getTitle : function()
1435     {
1436         
1437         var titleEl = this.titleEl();
1438         
1439         if(!titleEl){
1440             return '';
1441         }
1442         
1443         return titleEl.dom.innerHTML;
1444     },
1445     
1446     setRightTitle : function(v)
1447     {
1448         var t = this.el.select('.panel-header-right',true).first();
1449         
1450         if(!t){
1451             return;
1452         }
1453         
1454         t.dom.innerHTML = v;
1455     },
1456     
1457     onClick : function(e)
1458     {
1459         e.preventDefault();
1460         
1461         this.fireEvent('click', this, e);
1462     }
1463 });
1464
1465  /*
1466  * - LGPL
1467  *
1468  * image
1469  * 
1470  */
1471
1472
1473 /**
1474  * @class Roo.bootstrap.Img
1475  * @extends Roo.bootstrap.Component
1476  * Bootstrap Img class
1477  * @cfg {Boolean} imgResponsive false | true
1478  * @cfg {String} border rounded | circle | thumbnail
1479  * @cfg {String} src image source
1480  * @cfg {String} alt image alternative text
1481  * @cfg {String} href a tag href
1482  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1483  * @cfg {String} xsUrl xs image source
1484  * @cfg {String} smUrl sm image source
1485  * @cfg {String} mdUrl md image source
1486  * @cfg {String} lgUrl lg image source
1487  * 
1488  * @constructor
1489  * Create a new Input
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Img = function(config){
1494     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1495     
1496     this.addEvents({
1497         // img events
1498         /**
1499          * @event click
1500          * The img click event for the img.
1501          * @param {Roo.EventObject} e
1502          */
1503         "click" : true
1504     });
1505 };
1506
1507 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1508     
1509     imgResponsive: true,
1510     border: '',
1511     src: 'about:blank',
1512     href: false,
1513     target: false,
1514     xsUrl: '',
1515     smUrl: '',
1516     mdUrl: '',
1517     lgUrl: '',
1518
1519     getAutoCreate : function()
1520     {   
1521         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1522             return this.createSingleImg();
1523         }
1524         
1525         var cfg = {
1526             tag: 'div',
1527             cls: 'roo-image-responsive-group',
1528             cn: []
1529         };
1530         var _this = this;
1531         
1532         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1533             
1534             if(!_this[size + 'Url']){
1535                 return;
1536             }
1537             
1538             var img = {
1539                 tag: 'img',
1540                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1541                 html: _this.html || cfg.html,
1542                 src: _this[size + 'Url']
1543             };
1544             
1545             img.cls += ' roo-image-responsive-' + size;
1546             
1547             var s = ['xs', 'sm', 'md', 'lg'];
1548             
1549             s.splice(s.indexOf(size), 1);
1550             
1551             Roo.each(s, function(ss){
1552                 img.cls += ' hidden-' + ss;
1553             });
1554             
1555             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1556                 cfg.cls += ' img-' + _this.border;
1557             }
1558             
1559             if(_this.alt){
1560                 cfg.alt = _this.alt;
1561             }
1562             
1563             if(_this.href){
1564                 var a = {
1565                     tag: 'a',
1566                     href: _this.href,
1567                     cn: [
1568                         img
1569                     ]
1570                 };
1571
1572                 if(this.target){
1573                     a.target = _this.target;
1574                 }
1575             }
1576             
1577             cfg.cn.push((_this.href) ? a : img);
1578             
1579         });
1580         
1581         return cfg;
1582     },
1583     
1584     createSingleImg : function()
1585     {
1586         var cfg = {
1587             tag: 'img',
1588             cls: (this.imgResponsive) ? 'img-responsive' : '',
1589             html : null,
1590             src : 'about:blank'  // just incase src get's set to undefined?!?
1591         };
1592         
1593         cfg.html = this.html || cfg.html;
1594         
1595         cfg.src = this.src || cfg.src;
1596         
1597         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1598             cfg.cls += ' img-' + this.border;
1599         }
1600         
1601         if(this.alt){
1602             cfg.alt = this.alt;
1603         }
1604         
1605         if(this.href){
1606             var a = {
1607                 tag: 'a',
1608                 href: this.href,
1609                 cn: [
1610                     cfg
1611                 ]
1612             };
1613             
1614             if(this.target){
1615                 a.target = this.target;
1616             }
1617             
1618         }
1619         
1620         return (this.href) ? a : cfg;
1621     },
1622     
1623     initEvents: function() 
1624     {
1625         if(!this.href){
1626             this.el.on('click', this.onClick, this);
1627         }
1628         
1629     },
1630     
1631     onClick : function(e)
1632     {
1633         Roo.log('img onclick');
1634         this.fireEvent('click', this, e);
1635     },
1636     /**
1637      * Sets the url of the image - used to update it
1638      * @param {String} url the url of the image
1639      */
1640     
1641     setSrc : function(url)
1642     {
1643         this.src =  url;
1644         
1645         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1646             this.el.dom.src =  url;
1647             return;
1648         }
1649         
1650         this.el.select('img', true).first().dom.src =  url;
1651     }
1652     
1653     
1654    
1655 });
1656
1657  /*
1658  * - LGPL
1659  *
1660  * image
1661  * 
1662  */
1663
1664
1665 /**
1666  * @class Roo.bootstrap.Link
1667  * @extends Roo.bootstrap.Component
1668  * Bootstrap Link Class
1669  * @cfg {String} alt image alternative text
1670  * @cfg {String} href a tag href
1671  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1672  * @cfg {String} html the content of the link.
1673  * @cfg {String} anchor name for the anchor link
1674  * @cfg {String} fa - favicon
1675
1676  * @cfg {Boolean} preventDefault (true | false) default false
1677
1678  * 
1679  * @constructor
1680  * Create a new Input
1681  * @param {Object} config The config object
1682  */
1683
1684 Roo.bootstrap.Link = function(config){
1685     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1686     
1687     this.addEvents({
1688         // img events
1689         /**
1690          * @event click
1691          * The img click event for the img.
1692          * @param {Roo.EventObject} e
1693          */
1694         "click" : true
1695     });
1696 };
1697
1698 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1699     
1700     href: false,
1701     target: false,
1702     preventDefault: false,
1703     anchor : false,
1704     alt : false,
1705     fa: false,
1706
1707
1708     getAutoCreate : function()
1709     {
1710         var html = this.html || '';
1711         
1712         if (this.fa !== false) {
1713             html = '<i class="fa fa-' + this.fa + '"></i>';
1714         }
1715         var cfg = {
1716             tag: 'a'
1717         };
1718         // anchor's do not require html/href...
1719         if (this.anchor === false) {
1720             cfg.html = html;
1721             cfg.href = this.href || '#';
1722         } else {
1723             cfg.name = this.anchor;
1724             if (this.html !== false || this.fa !== false) {
1725                 cfg.html = html;
1726             }
1727             if (this.href !== false) {
1728                 cfg.href = this.href;
1729             }
1730         }
1731         
1732         if(this.alt !== false){
1733             cfg.alt = this.alt;
1734         }
1735         
1736         
1737         if(this.target !== false) {
1738             cfg.target = this.target;
1739         }
1740         
1741         return cfg;
1742     },
1743     
1744     initEvents: function() {
1745         
1746         if(!this.href || this.preventDefault){
1747             this.el.on('click', this.onClick, this);
1748         }
1749     },
1750     
1751     onClick : function(e)
1752     {
1753         if(this.preventDefault){
1754             e.preventDefault();
1755         }
1756         //Roo.log('img onclick');
1757         this.fireEvent('click', this, e);
1758     }
1759    
1760 });
1761
1762  /*
1763  * - LGPL
1764  *
1765  * header
1766  * 
1767  */
1768
1769 /**
1770  * @class Roo.bootstrap.Header
1771  * @extends Roo.bootstrap.Component
1772  * Bootstrap Header class
1773  * @cfg {String} html content of header
1774  * @cfg {Number} level (1|2|3|4|5|6) default 1
1775  * 
1776  * @constructor
1777  * Create a new Header
1778  * @param {Object} config The config object
1779  */
1780
1781
1782 Roo.bootstrap.Header  = function(config){
1783     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1784 };
1785
1786 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1787     
1788     //href : false,
1789     html : false,
1790     level : 1,
1791     
1792     
1793     
1794     getAutoCreate : function(){
1795         
1796         
1797         
1798         var cfg = {
1799             tag: 'h' + (1 *this.level),
1800             html: this.html || ''
1801         } ;
1802         
1803         return cfg;
1804     }
1805    
1806 });
1807
1808  
1809
1810  /*
1811  * Based on:
1812  * Ext JS Library 1.1.1
1813  * Copyright(c) 2006-2007, Ext JS, LLC.
1814  *
1815  * Originally Released Under LGPL - original licence link has changed is not relivant.
1816  *
1817  * Fork - LGPL
1818  * <script type="text/javascript">
1819  */
1820  
1821 /**
1822  * @class Roo.bootstrap.MenuMgr
1823  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1824  * @singleton
1825  */
1826 Roo.bootstrap.MenuMgr = function(){
1827    var menus, active, groups = {}, attached = false, lastShow = new Date();
1828
1829    // private - called when first menu is created
1830    function init(){
1831        menus = {};
1832        active = new Roo.util.MixedCollection();
1833        Roo.get(document).addKeyListener(27, function(){
1834            if(active.length > 0){
1835                hideAll();
1836            }
1837        });
1838    }
1839
1840    // private
1841    function hideAll(){
1842        if(active && active.length > 0){
1843            var c = active.clone();
1844            c.each(function(m){
1845                m.hide();
1846            });
1847        }
1848    }
1849
1850    // private
1851    function onHide(m){
1852        active.remove(m);
1853        if(active.length < 1){
1854            Roo.get(document).un("mouseup", onMouseDown);
1855             
1856            attached = false;
1857        }
1858    }
1859
1860    // private
1861    function onShow(m){
1862        var last = active.last();
1863        lastShow = new Date();
1864        active.add(m);
1865        if(!attached){
1866           Roo.get(document).on("mouseup", onMouseDown);
1867            
1868            attached = true;
1869        }
1870        if(m.parentMenu){
1871           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1872           m.parentMenu.activeChild = m;
1873        }else if(last && last.isVisible()){
1874           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1875        }
1876    }
1877
1878    // private
1879    function onBeforeHide(m){
1880        if(m.activeChild){
1881            m.activeChild.hide();
1882        }
1883        if(m.autoHideTimer){
1884            clearTimeout(m.autoHideTimer);
1885            delete m.autoHideTimer;
1886        }
1887    }
1888
1889    // private
1890    function onBeforeShow(m){
1891        var pm = m.parentMenu;
1892        if(!pm && !m.allowOtherMenus){
1893            hideAll();
1894        }else if(pm && pm.activeChild && active != m){
1895            pm.activeChild.hide();
1896        }
1897    }
1898
1899    // private this should really trigger on mouseup..
1900    function onMouseDown(e){
1901         Roo.log("on Mouse Up");
1902         
1903         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1904             Roo.log("MenuManager hideAll");
1905             hideAll();
1906             e.stopEvent();
1907         }
1908         
1909         
1910    }
1911
1912    // private
1913    function onBeforeCheck(mi, state){
1914        if(state){
1915            var g = groups[mi.group];
1916            for(var i = 0, l = g.length; i < l; i++){
1917                if(g[i] != mi){
1918                    g[i].setChecked(false);
1919                }
1920            }
1921        }
1922    }
1923
1924    return {
1925
1926        /**
1927         * Hides all menus that are currently visible
1928         */
1929        hideAll : function(){
1930             hideAll();  
1931        },
1932
1933        // private
1934        register : function(menu){
1935            if(!menus){
1936                init();
1937            }
1938            menus[menu.id] = menu;
1939            menu.on("beforehide", onBeforeHide);
1940            menu.on("hide", onHide);
1941            menu.on("beforeshow", onBeforeShow);
1942            menu.on("show", onShow);
1943            var g = menu.group;
1944            if(g && menu.events["checkchange"]){
1945                if(!groups[g]){
1946                    groups[g] = [];
1947                }
1948                groups[g].push(menu);
1949                menu.on("checkchange", onCheck);
1950            }
1951        },
1952
1953         /**
1954          * Returns a {@link Roo.menu.Menu} object
1955          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1956          * be used to generate and return a new Menu instance.
1957          */
1958        get : function(menu){
1959            if(typeof menu == "string"){ // menu id
1960                return menus[menu];
1961            }else if(menu.events){  // menu instance
1962                return menu;
1963            }
1964            /*else if(typeof menu.length == 'number'){ // array of menu items?
1965                return new Roo.bootstrap.Menu({items:menu});
1966            }else{ // otherwise, must be a config
1967                return new Roo.bootstrap.Menu(menu);
1968            }
1969            */
1970            return false;
1971        },
1972
1973        // private
1974        unregister : function(menu){
1975            delete menus[menu.id];
1976            menu.un("beforehide", onBeforeHide);
1977            menu.un("hide", onHide);
1978            menu.un("beforeshow", onBeforeShow);
1979            menu.un("show", onShow);
1980            var g = menu.group;
1981            if(g && menu.events["checkchange"]){
1982                groups[g].remove(menu);
1983                menu.un("checkchange", onCheck);
1984            }
1985        },
1986
1987        // private
1988        registerCheckable : function(menuItem){
1989            var g = menuItem.group;
1990            if(g){
1991                if(!groups[g]){
1992                    groups[g] = [];
1993                }
1994                groups[g].push(menuItem);
1995                menuItem.on("beforecheckchange", onBeforeCheck);
1996            }
1997        },
1998
1999        // private
2000        unregisterCheckable : function(menuItem){
2001            var g = menuItem.group;
2002            if(g){
2003                groups[g].remove(menuItem);
2004                menuItem.un("beforecheckchange", onBeforeCheck);
2005            }
2006        }
2007    };
2008 }();/*
2009  * - LGPL
2010  *
2011  * menu
2012  * 
2013  */
2014
2015 /**
2016  * @class Roo.bootstrap.Menu
2017  * @extends Roo.bootstrap.Component
2018  * Bootstrap Menu class - container for MenuItems
2019  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2020  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2021  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2022  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2023  * 
2024  * @constructor
2025  * Create a new Menu
2026  * @param {Object} config The config object
2027  */
2028
2029
2030 Roo.bootstrap.Menu = function(config){
2031     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2032     if (this.registerMenu && this.type != 'treeview')  {
2033         Roo.bootstrap.MenuMgr.register(this);
2034     }
2035     
2036     
2037     this.addEvents({
2038         /**
2039          * @event beforeshow
2040          * Fires before this menu is displayed
2041          * @param {Roo.menu.Menu} this
2042          */
2043         beforeshow : true,
2044         /**
2045          * @event beforehide
2046          * Fires before this menu is hidden
2047          * @param {Roo.menu.Menu} this
2048          */
2049         beforehide : true,
2050         /**
2051          * @event show
2052          * Fires after this menu is displayed
2053          * @param {Roo.menu.Menu} this
2054          */
2055         show : true,
2056         /**
2057          * @event hide
2058          * Fires after this menu is hidden
2059          * @param {Roo.menu.Menu} this
2060          */
2061         hide : true,
2062         /**
2063          * @event click
2064          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2065          * @param {Roo.menu.Menu} this
2066          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2067          * @param {Roo.EventObject} e
2068          */
2069         click : true,
2070         /**
2071          * @event mouseover
2072          * Fires when the mouse is hovering over this menu
2073          * @param {Roo.menu.Menu} this
2074          * @param {Roo.EventObject} e
2075          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2076          */
2077         mouseover : true,
2078         /**
2079          * @event mouseout
2080          * Fires when the mouse exits this menu
2081          * @param {Roo.menu.Menu} this
2082          * @param {Roo.EventObject} e
2083          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2084          */
2085         mouseout : true,
2086         /**
2087          * @event itemclick
2088          * Fires when a menu item contained in this menu is clicked
2089          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         itemclick: true
2093     });
2094     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2095 };
2096
2097 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2098     
2099    /// html : false,
2100     //align : '',
2101     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2102     type: false,
2103     /**
2104      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2105      */
2106     registerMenu : true,
2107     
2108     menuItems :false, // stores the menu items..
2109     
2110     hidden:true,
2111         
2112     parentMenu : false,
2113     
2114     stopEvent : true,
2115     
2116     isLink : false,
2117     
2118     getChildContainer : function() {
2119         return this.el;  
2120     },
2121     
2122     getAutoCreate : function(){
2123          
2124         //if (['right'].indexOf(this.align)!==-1) {
2125         //    cfg.cn[1].cls += ' pull-right'
2126         //}
2127         
2128         
2129         var cfg = {
2130             tag : 'ul',
2131             cls : 'dropdown-menu' ,
2132             style : 'z-index:1000'
2133             
2134         };
2135         
2136         if (this.type === 'submenu') {
2137             cfg.cls = 'submenu active';
2138         }
2139         if (this.type === 'treeview') {
2140             cfg.cls = 'treeview-menu';
2141         }
2142         
2143         return cfg;
2144     },
2145     initEvents : function() {
2146         
2147        // Roo.log("ADD event");
2148        // Roo.log(this.triggerEl.dom);
2149         
2150         this.triggerEl.on('click', this.onTriggerClick, this);
2151         
2152         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2153         
2154         
2155         if (this.triggerEl.hasClass('nav-item')) {
2156             // dropdown toggle on the 'a' in BS4?
2157             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2158         } else {
2159             this.triggerEl.addClass('dropdown-toggle');
2160         }
2161         if (Roo.isTouch) {
2162             this.el.on('touchstart'  , this.onTouch, this);
2163         }
2164         this.el.on('click' , this.onClick, this);
2165
2166         this.el.on("mouseover", this.onMouseOver, this);
2167         this.el.on("mouseout", this.onMouseOut, this);
2168         
2169     },
2170     
2171     findTargetItem : function(e)
2172     {
2173         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2174         if(!t){
2175             return false;
2176         }
2177         //Roo.log(t);         Roo.log(t.id);
2178         if(t && t.id){
2179             //Roo.log(this.menuitems);
2180             return this.menuitems.get(t.id);
2181             
2182             //return this.items.get(t.menuItemId);
2183         }
2184         
2185         return false;
2186     },
2187     
2188     onTouch : function(e) 
2189     {
2190         Roo.log("menu.onTouch");
2191         //e.stopEvent(); this make the user popdown broken
2192         this.onClick(e);
2193     },
2194     
2195     onClick : function(e)
2196     {
2197         Roo.log("menu.onClick");
2198         
2199         var t = this.findTargetItem(e);
2200         if(!t || t.isContainer){
2201             return;
2202         }
2203         Roo.log(e);
2204         /*
2205         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2206             if(t == this.activeItem && t.shouldDeactivate(e)){
2207                 this.activeItem.deactivate();
2208                 delete this.activeItem;
2209                 return;
2210             }
2211             if(t.canActivate){
2212                 this.setActiveItem(t, true);
2213             }
2214             return;
2215             
2216             
2217         }
2218         */
2219        
2220         Roo.log('pass click event');
2221         
2222         t.onClick(e);
2223         
2224         this.fireEvent("click", this, t, e);
2225         
2226         var _this = this;
2227         
2228         if(!t.href.length || t.href == '#'){
2229             (function() { _this.hide(); }).defer(100);
2230         }
2231         
2232     },
2233     
2234     onMouseOver : function(e){
2235         var t  = this.findTargetItem(e);
2236         //Roo.log(t);
2237         //if(t){
2238         //    if(t.canActivate && !t.disabled){
2239         //        this.setActiveItem(t, true);
2240         //    }
2241         //}
2242         
2243         this.fireEvent("mouseover", this, e, t);
2244     },
2245     isVisible : function(){
2246         return !this.hidden;
2247     },
2248      onMouseOut : function(e){
2249         var t  = this.findTargetItem(e);
2250         
2251         //if(t ){
2252         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2253         //        this.activeItem.deactivate();
2254         //        delete this.activeItem;
2255         //    }
2256         //}
2257         this.fireEvent("mouseout", this, e, t);
2258     },
2259     
2260     
2261     /**
2262      * Displays this menu relative to another element
2263      * @param {String/HTMLElement/Roo.Element} element The element to align to
2264      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2265      * the element (defaults to this.defaultAlign)
2266      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2267      */
2268     show : function(el, pos, parentMenu){
2269         this.parentMenu = parentMenu;
2270         if(!this.el){
2271             this.render();
2272         }
2273         this.fireEvent("beforeshow", this);
2274         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2275     },
2276      /**
2277      * Displays this menu at a specific xy position
2278      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2279      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2280      */
2281     showAt : function(xy, parentMenu, /* private: */_e){
2282         this.parentMenu = parentMenu;
2283         if(!this.el){
2284             this.render();
2285         }
2286         if(_e !== false){
2287             this.fireEvent("beforeshow", this);
2288             //xy = this.el.adjustForConstraints(xy);
2289         }
2290         
2291         //this.el.show();
2292         this.hideMenuItems();
2293         this.hidden = false;
2294         this.triggerEl.addClass('open');
2295         this.el.addClass('show');
2296         
2297         // reassign x when hitting right
2298         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2299             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2300         }
2301         
2302         // reassign y when hitting bottom
2303         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2304             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2305         }
2306         
2307         // but the list may align on trigger left or trigger top... should it be a properity?
2308         
2309         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2310             this.el.setXY(xy);
2311         }
2312         
2313         this.focus();
2314         this.fireEvent("show", this);
2315     },
2316     
2317     focus : function(){
2318         return;
2319         if(!this.hidden){
2320             this.doFocus.defer(50, this);
2321         }
2322     },
2323
2324     doFocus : function(){
2325         if(!this.hidden){
2326             this.focusEl.focus();
2327         }
2328     },
2329
2330     /**
2331      * Hides this menu and optionally all parent menus
2332      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2333      */
2334     hide : function(deep)
2335     {
2336         
2337         this.hideMenuItems();
2338         if(this.el && this.isVisible()){
2339             this.fireEvent("beforehide", this);
2340             if(this.activeItem){
2341                 this.activeItem.deactivate();
2342                 this.activeItem = null;
2343             }
2344             this.triggerEl.removeClass('open');;
2345             this.el.removeClass('show');
2346             this.hidden = true;
2347             this.fireEvent("hide", this);
2348         }
2349         if(deep === true && this.parentMenu){
2350             this.parentMenu.hide(true);
2351         }
2352     },
2353     
2354     onTriggerClick : function(e)
2355     {
2356         Roo.log('trigger click');
2357         
2358         var target = e.getTarget();
2359         
2360         Roo.log(target.nodeName.toLowerCase());
2361         
2362         if(target.nodeName.toLowerCase() === 'i'){
2363             e.preventDefault();
2364         }
2365         
2366     },
2367     
2368     onTriggerPress  : function(e)
2369     {
2370         Roo.log('trigger press');
2371         //Roo.log(e.getTarget());
2372        // Roo.log(this.triggerEl.dom);
2373        
2374         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2375         var pel = Roo.get(e.getTarget());
2376         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2377             Roo.log('is treeview or dropdown?');
2378             return;
2379         }
2380         
2381         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2382             return;
2383         }
2384         
2385         if (this.isVisible()) {
2386             Roo.log('hide');
2387             this.hide();
2388         } else {
2389             Roo.log('show');
2390             this.show(this.triggerEl, false, false);
2391         }
2392         
2393         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2394             e.stopEvent();
2395         }
2396         
2397     },
2398        
2399     
2400     hideMenuItems : function()
2401     {
2402         Roo.log("hide Menu Items");
2403         if (!this.el) { 
2404             return;
2405         }
2406         //$(backdrop).remove()
2407         this.el.select('.open',true).each(function(aa) {
2408             
2409             aa.removeClass('open');
2410           //var parent = getParent($(this))
2411           //var relatedTarget = { relatedTarget: this }
2412           
2413            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2414           //if (e.isDefaultPrevented()) return
2415            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2416         });
2417     },
2418     addxtypeChild : function (tree, cntr) {
2419         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2420           
2421         this.menuitems.add(comp);
2422         return comp;
2423
2424     },
2425     getEl : function()
2426     {
2427         Roo.log(this.el);
2428         return this.el;
2429     },
2430     
2431     clear : function()
2432     {
2433         this.getEl().dom.innerHTML = '';
2434         this.menuitems.clear();
2435     }
2436 });
2437
2438  
2439  /*
2440  * - LGPL
2441  *
2442  * menu item
2443  * 
2444  */
2445
2446
2447 /**
2448  * @class Roo.bootstrap.MenuItem
2449  * @extends Roo.bootstrap.Component
2450  * Bootstrap MenuItem class
2451  * @cfg {String} html the menu label
2452  * @cfg {String} href the link
2453  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2454  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2455  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2456  * @cfg {String} fa favicon to show on left of menu item.
2457  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2458  * 
2459  * 
2460  * @constructor
2461  * Create a new MenuItem
2462  * @param {Object} config The config object
2463  */
2464
2465
2466 Roo.bootstrap.MenuItem = function(config){
2467     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2468     this.addEvents({
2469         // raw events
2470         /**
2471          * @event click
2472          * The raw click event for the entire grid.
2473          * @param {Roo.bootstrap.MenuItem} this
2474          * @param {Roo.EventObject} e
2475          */
2476         "click" : true
2477     });
2478 };
2479
2480 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2481     
2482     href : false,
2483     html : false,
2484     preventDefault: false,
2485     isContainer : false,
2486     active : false,
2487     fa: false,
2488     
2489     getAutoCreate : function(){
2490         
2491         if(this.isContainer){
2492             return {
2493                 tag: 'li',
2494                 cls: 'dropdown-menu-item dropdown-item'
2495             };
2496         }
2497         var ctag = {
2498             tag: 'span',
2499             html: 'Link'
2500         };
2501         
2502         var anc = {
2503             tag : 'a',
2504             href : '#',
2505             cn : [  ]
2506         };
2507         
2508         if (this.fa !== false) {
2509             anc.cn.push({
2510                 tag : 'i',
2511                 cls : 'fa fa-' + this.fa
2512             });
2513         }
2514         
2515         anc.cn.push(ctag);
2516         
2517         
2518         var cfg= {
2519             tag: 'li',
2520             cls: 'dropdown-menu-item dropdown-item',
2521             cn: [ anc ]
2522         };
2523         if (this.parent().type == 'treeview') {
2524             cfg.cls = 'treeview-menu';
2525         }
2526         if (this.active) {
2527             cfg.cls += ' active';
2528         }
2529         
2530         
2531         
2532         anc.href = this.href || cfg.cn[0].href ;
2533         ctag.html = this.html || cfg.cn[0].html ;
2534         return cfg;
2535     },
2536     
2537     initEvents: function()
2538     {
2539         if (this.parent().type == 'treeview') {
2540             this.el.select('a').on('click', this.onClick, this);
2541         }
2542         
2543         if (this.menu) {
2544             this.menu.parentType = this.xtype;
2545             this.menu.triggerEl = this.el;
2546             this.menu = this.addxtype(Roo.apply({}, this.menu));
2547         }
2548         
2549     },
2550     onClick : function(e)
2551     {
2552         Roo.log('item on click ');
2553         
2554         if(this.preventDefault){
2555             e.preventDefault();
2556         }
2557         //this.parent().hideMenuItems();
2558         
2559         this.fireEvent('click', this, e);
2560     },
2561     getEl : function()
2562     {
2563         return this.el;
2564     } 
2565 });
2566
2567  
2568
2569  /*
2570  * - LGPL
2571  *
2572  * menu separator
2573  * 
2574  */
2575
2576
2577 /**
2578  * @class Roo.bootstrap.MenuSeparator
2579  * @extends Roo.bootstrap.Component
2580  * Bootstrap MenuSeparator class
2581  * 
2582  * @constructor
2583  * Create a new MenuItem
2584  * @param {Object} config The config object
2585  */
2586
2587
2588 Roo.bootstrap.MenuSeparator = function(config){
2589     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2590 };
2591
2592 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2593     
2594     getAutoCreate : function(){
2595         var cfg = {
2596             cls: 'divider',
2597             tag : 'li'
2598         };
2599         
2600         return cfg;
2601     }
2602    
2603 });
2604
2605  
2606
2607  
2608 /*
2609 * Licence: LGPL
2610 */
2611
2612 /**
2613  * @class Roo.bootstrap.Modal
2614  * @extends Roo.bootstrap.Component
2615  * Bootstrap Modal class
2616  * @cfg {String} title Title of dialog
2617  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2618  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2619  * @cfg {Boolean} specificTitle default false
2620  * @cfg {Array} buttons Array of buttons or standard button set..
2621  * @cfg {String} buttonPosition (left|right|center) default right
2622  * @cfg {Boolean} animate default true
2623  * @cfg {Boolean} allow_close default true
2624  * @cfg {Boolean} fitwindow default false
2625  * @cfg {String} size (sm|lg) default empty
2626  * @cfg {Number} max_width set the max width of modal
2627  *
2628  *
2629  * @constructor
2630  * Create a new Modal Dialog
2631  * @param {Object} config The config object
2632  */
2633
2634 Roo.bootstrap.Modal = function(config){
2635     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2636     this.addEvents({
2637         // raw events
2638         /**
2639          * @event btnclick
2640          * The raw btnclick event for the button
2641          * @param {Roo.EventObject} e
2642          */
2643         "btnclick" : true,
2644         /**
2645          * @event resize
2646          * Fire when dialog resize
2647          * @param {Roo.bootstrap.Modal} this
2648          * @param {Roo.EventObject} e
2649          */
2650         "resize" : true
2651     });
2652     this.buttons = this.buttons || [];
2653
2654     if (this.tmpl) {
2655         this.tmpl = Roo.factory(this.tmpl);
2656     }
2657
2658 };
2659
2660 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2661
2662     title : 'test dialog',
2663
2664     buttons : false,
2665
2666     // set on load...
2667
2668     html: false,
2669
2670     tmp: false,
2671
2672     specificTitle: false,
2673
2674     buttonPosition: 'right',
2675
2676     allow_close : true,
2677
2678     animate : true,
2679
2680     fitwindow: false,
2681     
2682      // private
2683     dialogEl: false,
2684     bodyEl:  false,
2685     footerEl:  false,
2686     titleEl:  false,
2687     closeEl:  false,
2688
2689     size: '',
2690     
2691     max_width: 0,
2692     
2693     max_height: 0,
2694     
2695     fit_content: false,
2696
2697     onRender : function(ct, position)
2698     {
2699         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2700
2701         if(!this.el){
2702             var cfg = Roo.apply({},  this.getAutoCreate());
2703             cfg.id = Roo.id();
2704             //if(!cfg.name){
2705             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2706             //}
2707             //if (!cfg.name.length) {
2708             //    delete cfg.name;
2709            // }
2710             if (this.cls) {
2711                 cfg.cls += ' ' + this.cls;
2712             }
2713             if (this.style) {
2714                 cfg.style = this.style;
2715             }
2716             this.el = Roo.get(document.body).createChild(cfg, position);
2717         }
2718         //var type = this.el.dom.type;
2719
2720
2721         if(this.tabIndex !== undefined){
2722             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2723         }
2724
2725         this.dialogEl = this.el.select('.modal-dialog',true).first();
2726         this.bodyEl = this.el.select('.modal-body',true).first();
2727         this.closeEl = this.el.select('.modal-header .close', true).first();
2728         this.headerEl = this.el.select('.modal-header',true).first();
2729         this.titleEl = this.el.select('.modal-title',true).first();
2730         this.footerEl = this.el.select('.modal-footer',true).first();
2731
2732         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2733         
2734         //this.el.addClass("x-dlg-modal");
2735
2736         if (this.buttons.length) {
2737             Roo.each(this.buttons, function(bb) {
2738                 var b = Roo.apply({}, bb);
2739                 b.xns = b.xns || Roo.bootstrap;
2740                 b.xtype = b.xtype || 'Button';
2741                 if (typeof(b.listeners) == 'undefined') {
2742                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2743                 }
2744
2745                 var btn = Roo.factory(b);
2746
2747                 btn.render(this.el.select('.modal-footer div').first());
2748
2749             },this);
2750         }
2751         // render the children.
2752         var nitems = [];
2753
2754         if(typeof(this.items) != 'undefined'){
2755             var items = this.items;
2756             delete this.items;
2757
2758             for(var i =0;i < items.length;i++) {
2759                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2760             }
2761         }
2762
2763         this.items = nitems;
2764
2765         // where are these used - they used to be body/close/footer
2766
2767
2768         this.initEvents();
2769         //this.el.addClass([this.fieldClass, this.cls]);
2770
2771     },
2772
2773     getAutoCreate : function()
2774     {
2775         var bdy = {
2776                 cls : 'modal-body',
2777                 html : this.html || ''
2778         };
2779
2780         var title = {
2781             tag: 'h4',
2782             cls : 'modal-title',
2783             html : this.title
2784         };
2785
2786         if(this.specificTitle){
2787             title = this.title;
2788
2789         };
2790
2791         var header = [];
2792         if (this.allow_close && Roo.bootstrap.version == 3) {
2793             header.push({
2794                 tag: 'button',
2795                 cls : 'close',
2796                 html : '&times'
2797             });
2798         }
2799
2800         header.push(title);
2801
2802         if (this.allow_close && Roo.bootstrap.version == 4) {
2803             header.push({
2804                 tag: 'button',
2805                 cls : 'close',
2806                 html : '&times'
2807             });
2808         }
2809         
2810         var size = '';
2811
2812         if(this.size.length){
2813             size = 'modal-' + this.size;
2814         }
2815
2816         var modal = {
2817             cls: "modal",
2818              cn : [
2819                 {
2820                     cls: "modal-dialog " + size,
2821                     cn : [
2822                         {
2823                             cls : "modal-content",
2824                             cn : [
2825                                 {
2826                                     cls : 'modal-header',
2827                                     cn : header
2828                                 },
2829                                 bdy,
2830                                 {
2831                                     cls : 'modal-footer',
2832                                     cn : [
2833                                         {
2834                                             tag: 'div',
2835                                             cls: 'btn-' + this.buttonPosition
2836                                         }
2837                                     ]
2838
2839                                 }
2840
2841
2842                             ]
2843
2844                         }
2845                     ]
2846
2847                 }
2848             ]
2849         };
2850
2851         if(this.animate){
2852             modal.cls += ' fade';
2853         }
2854
2855         return modal;
2856
2857     },
2858     getChildContainer : function() {
2859
2860          return this.bodyEl;
2861
2862     },
2863     getButtonContainer : function() {
2864          return this.el.select('.modal-footer div',true).first();
2865
2866     },
2867     initEvents : function()
2868     {
2869         if (this.allow_close) {
2870             this.closeEl.on('click', this.hide, this);
2871         }
2872         Roo.EventManager.onWindowResize(this.resize, this, true);
2873
2874
2875     },
2876
2877     resize : function()
2878     {
2879         this.maskEl.setSize(
2880             Roo.lib.Dom.getViewWidth(true),
2881             Roo.lib.Dom.getViewHeight(true)
2882         );
2883         
2884         if (this.fitwindow) {
2885             this.setSize(
2886                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2887                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2888             );
2889             return;
2890         }
2891         
2892         if(this.max_width !== 0) {
2893             
2894             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2895             
2896             if(this.height) {
2897                 this.setSize(w, this.height);
2898                 return;
2899             }
2900             
2901             if(this.max_height) {
2902                 this.setSize(w,Math.min(
2903                     this.max_height,
2904                     Roo.lib.Dom.getViewportHeight(true) - 60
2905                 ));
2906                 
2907                 return;
2908             }
2909             
2910             if(!this.fit_content) {
2911                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2912                 return;
2913             }
2914             
2915             this.setSize(w, Math.min(
2916                 60 +
2917                 this.headerEl.getHeight() + 
2918                 this.footerEl.getHeight() + 
2919                 this.getChildHeight(this.bodyEl.dom.childNodes),
2920                 Roo.lib.Dom.getViewportHeight(true) - 60)
2921             );
2922         }
2923         
2924     },
2925
2926     setSize : function(w,h)
2927     {
2928         if (!w && !h) {
2929             return;
2930         }
2931         
2932         this.resizeTo(w,h);
2933     },
2934
2935     show : function() {
2936
2937         if (!this.rendered) {
2938             this.render();
2939         }
2940
2941         //this.el.setStyle('display', 'block');
2942         this.el.removeClass('hideing');
2943         this.el.dom.style.display='block';
2944         
2945         Roo.get(document.body).addClass('modal-open');
2946  
2947         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2948             var _this = this;
2949             (function(){
2950                 this.el.addClass('show');
2951                 this.el.addClass('in');
2952             }).defer(50, this);
2953         }else{
2954             this.el.addClass('show');
2955             this.el.addClass('in');
2956         }
2957
2958         // not sure how we can show data in here..
2959         //if (this.tmpl) {
2960         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2961         //}
2962
2963         Roo.get(document.body).addClass("x-body-masked");
2964         
2965         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2966         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2967         this.maskEl.dom.style.display = 'block';
2968         this.maskEl.addClass('show');
2969         
2970         
2971         this.resize();
2972         
2973         this.fireEvent('show', this);
2974
2975         // set zindex here - otherwise it appears to be ignored...
2976         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2977
2978         (function () {
2979             this.items.forEach( function(e) {
2980                 e.layout ? e.layout() : false;
2981
2982             });
2983         }).defer(100,this);
2984
2985     },
2986     hide : function()
2987     {
2988         if(this.fireEvent("beforehide", this) !== false){
2989             
2990             this.maskEl.removeClass('show');
2991             
2992             this.maskEl.dom.style.display = '';
2993             Roo.get(document.body).removeClass("x-body-masked");
2994             this.el.removeClass('in');
2995             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2996
2997             if(this.animate){ // why
2998                 this.el.addClass('hideing');
2999                 this.el.removeClass('show');
3000                 (function(){
3001                     if (!this.el.hasClass('hideing')) {
3002                         return; // it's been shown again...
3003                     }
3004                     
3005                     this.el.dom.style.display='';
3006
3007                     Roo.get(document.body).removeClass('modal-open');
3008                     this.el.removeClass('hideing');
3009                 }).defer(150,this);
3010                 
3011             }else{
3012                 this.el.removeClass('show');
3013                 this.el.dom.style.display='';
3014                 Roo.get(document.body).removeClass('modal-open');
3015
3016             }
3017             this.fireEvent('hide', this);
3018         }
3019     },
3020     isVisible : function()
3021     {
3022         
3023         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3024         
3025     },
3026
3027     addButton : function(str, cb)
3028     {
3029
3030
3031         var b = Roo.apply({}, { html : str } );
3032         b.xns = b.xns || Roo.bootstrap;
3033         b.xtype = b.xtype || 'Button';
3034         if (typeof(b.listeners) == 'undefined') {
3035             b.listeners = { click : cb.createDelegate(this)  };
3036         }
3037
3038         var btn = Roo.factory(b);
3039
3040         btn.render(this.el.select('.modal-footer div').first());
3041
3042         return btn;
3043
3044     },
3045
3046     setDefaultButton : function(btn)
3047     {
3048         //this.el.select('.modal-footer').()
3049     },
3050     diff : false,
3051
3052     resizeTo: function(w,h)
3053     {
3054         // skip.. ?? why??
3055
3056         this.dialogEl.setWidth(w);
3057         if (this.diff === false) {
3058             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3059         }
3060
3061         this.bodyEl.setHeight(h - this.diff);
3062
3063         this.fireEvent('resize', this);
3064
3065     },
3066     setContentSize  : function(w, h)
3067     {
3068
3069     },
3070     onButtonClick: function(btn,e)
3071     {
3072         //Roo.log([a,b,c]);
3073         this.fireEvent('btnclick', btn.name, e);
3074     },
3075      /**
3076      * Set the title of the Dialog
3077      * @param {String} str new Title
3078      */
3079     setTitle: function(str) {
3080         this.titleEl.dom.innerHTML = str;
3081     },
3082     /**
3083      * Set the body of the Dialog
3084      * @param {String} str new Title
3085      */
3086     setBody: function(str) {
3087         this.bodyEl.dom.innerHTML = str;
3088     },
3089     /**
3090      * Set the body of the Dialog using the template
3091      * @param {Obj} data - apply this data to the template and replace the body contents.
3092      */
3093     applyBody: function(obj)
3094     {
3095         if (!this.tmpl) {
3096             Roo.log("Error - using apply Body without a template");
3097             //code
3098         }
3099         this.tmpl.overwrite(this.bodyEl, obj);
3100     },
3101     
3102     getChildHeight : function(child_nodes)
3103     {
3104         if(
3105             !child_nodes ||
3106             child_nodes.length == 0
3107         ) {
3108             return;
3109         }
3110         
3111         var child_height = 0;
3112         
3113         for(var i = 0; i < child_nodes.length; i++) {
3114             
3115             /*
3116             * for modal with tabs...
3117             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3118                 
3119                 var layout_childs = child_nodes[i].childNodes;
3120                 
3121                 for(var j = 0; j < layout_childs.length; j++) {
3122                     
3123                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3124                         
3125                         var layout_body_childs = layout_childs[j].childNodes;
3126                         
3127                         for(var k = 0; k < layout_body_childs.length; k++) {
3128                             
3129                             if(layout_body_childs[k].classList.contains('navbar')) {
3130                                 child_height += layout_body_childs[k].offsetHeight;
3131                                 continue;
3132                             }
3133                             
3134                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3135                                 
3136                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3137                                 
3138                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3139                                     
3140                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3141                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3142                                         continue;
3143                                     }
3144                                     
3145                                 }
3146                                 
3147                             }
3148                             
3149                         }
3150                     }
3151                 }
3152                 continue;
3153             }
3154             */
3155             
3156             child_height += child_nodes[i].offsetHeight;
3157             // Roo.log(child_nodes[i].offsetHeight);
3158         }
3159         
3160         return child_height;
3161     }
3162
3163 });
3164
3165
3166 Roo.apply(Roo.bootstrap.Modal,  {
3167     /**
3168          * Button config that displays a single OK button
3169          * @type Object
3170          */
3171         OK :  [{
3172             name : 'ok',
3173             weight : 'primary',
3174             html : 'OK'
3175         }],
3176         /**
3177          * Button config that displays Yes and No buttons
3178          * @type Object
3179          */
3180         YESNO : [
3181             {
3182                 name  : 'no',
3183                 html : 'No'
3184             },
3185             {
3186                 name  :'yes',
3187                 weight : 'primary',
3188                 html : 'Yes'
3189             }
3190         ],
3191
3192         /**
3193          * Button config that displays OK and Cancel buttons
3194          * @type Object
3195          */
3196         OKCANCEL : [
3197             {
3198                name : 'cancel',
3199                 html : 'Cancel'
3200             },
3201             {
3202                 name : 'ok',
3203                 weight : 'primary',
3204                 html : 'OK'
3205             }
3206         ],
3207         /**
3208          * Button config that displays Yes, No and Cancel buttons
3209          * @type Object
3210          */
3211         YESNOCANCEL : [
3212             {
3213                 name : 'yes',
3214                 weight : 'primary',
3215                 html : 'Yes'
3216             },
3217             {
3218                 name : 'no',
3219                 html : 'No'
3220             },
3221             {
3222                 name : 'cancel',
3223                 html : 'Cancel'
3224             }
3225         ],
3226         
3227         zIndex : 10001
3228 });
3229 /*
3230  * - LGPL
3231  *
3232  * messagebox - can be used as a replace
3233  * 
3234  */
3235 /**
3236  * @class Roo.MessageBox
3237  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3238  * Example usage:
3239  *<pre><code>
3240 // Basic alert:
3241 Roo.Msg.alert('Status', 'Changes saved successfully.');
3242
3243 // Prompt for user data:
3244 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3245     if (btn == 'ok'){
3246         // process text value...
3247     }
3248 });
3249
3250 // Show a dialog using config options:
3251 Roo.Msg.show({
3252    title:'Save Changes?',
3253    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3254    buttons: Roo.Msg.YESNOCANCEL,
3255    fn: processResult,
3256    animEl: 'elId'
3257 });
3258 </code></pre>
3259  * @singleton
3260  */
3261 Roo.bootstrap.MessageBox = function(){
3262     var dlg, opt, mask, waitTimer;
3263     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3264     var buttons, activeTextEl, bwidth;
3265
3266     
3267     // private
3268     var handleButton = function(button){
3269         dlg.hide();
3270         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3271     };
3272
3273     // private
3274     var handleHide = function(){
3275         if(opt && opt.cls){
3276             dlg.el.removeClass(opt.cls);
3277         }
3278         //if(waitTimer){
3279         //    Roo.TaskMgr.stop(waitTimer);
3280         //    waitTimer = null;
3281         //}
3282     };
3283
3284     // private
3285     var updateButtons = function(b){
3286         var width = 0;
3287         if(!b){
3288             buttons["ok"].hide();
3289             buttons["cancel"].hide();
3290             buttons["yes"].hide();
3291             buttons["no"].hide();
3292             //dlg.footer.dom.style.display = 'none';
3293             return width;
3294         }
3295         dlg.footerEl.dom.style.display = '';
3296         for(var k in buttons){
3297             if(typeof buttons[k] != "function"){
3298                 if(b[k]){
3299                     buttons[k].show();
3300                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3301                     width += buttons[k].el.getWidth()+15;
3302                 }else{
3303                     buttons[k].hide();
3304                 }
3305             }
3306         }
3307         return width;
3308     };
3309
3310     // private
3311     var handleEsc = function(d, k, e){
3312         if(opt && opt.closable !== false){
3313             dlg.hide();
3314         }
3315         if(e){
3316             e.stopEvent();
3317         }
3318     };
3319
3320     return {
3321         /**
3322          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3323          * @return {Roo.BasicDialog} The BasicDialog element
3324          */
3325         getDialog : function(){
3326            if(!dlg){
3327                 dlg = new Roo.bootstrap.Modal( {
3328                     //draggable: true,
3329                     //resizable:false,
3330                     //constraintoviewport:false,
3331                     //fixedcenter:true,
3332                     //collapsible : false,
3333                     //shim:true,
3334                     //modal: true,
3335                 //    width: 'auto',
3336                   //  height:100,
3337                     //buttonAlign:"center",
3338                     closeClick : function(){
3339                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3340                             handleButton("no");
3341                         }else{
3342                             handleButton("cancel");
3343                         }
3344                     }
3345                 });
3346                 dlg.render();
3347                 dlg.on("hide", handleHide);
3348                 mask = dlg.mask;
3349                 //dlg.addKeyListener(27, handleEsc);
3350                 buttons = {};
3351                 this.buttons = buttons;
3352                 var bt = this.buttonText;
3353                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3354                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3355                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3356                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3357                 //Roo.log(buttons);
3358                 bodyEl = dlg.bodyEl.createChild({
3359
3360                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3361                         '<textarea class="roo-mb-textarea"></textarea>' +
3362                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3363                 });
3364                 msgEl = bodyEl.dom.firstChild;
3365                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3366                 textboxEl.enableDisplayMode();
3367                 textboxEl.addKeyListener([10,13], function(){
3368                     if(dlg.isVisible() && opt && opt.buttons){
3369                         if(opt.buttons.ok){
3370                             handleButton("ok");
3371                         }else if(opt.buttons.yes){
3372                             handleButton("yes");
3373                         }
3374                     }
3375                 });
3376                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3377                 textareaEl.enableDisplayMode();
3378                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3379                 progressEl.enableDisplayMode();
3380                 
3381                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3382                 var pf = progressEl.dom.firstChild;
3383                 if (pf) {
3384                     pp = Roo.get(pf.firstChild);
3385                     pp.setHeight(pf.offsetHeight);
3386                 }
3387                 
3388             }
3389             return dlg;
3390         },
3391
3392         /**
3393          * Updates the message box body text
3394          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3395          * the XHTML-compliant non-breaking space character '&amp;#160;')
3396          * @return {Roo.MessageBox} This message box
3397          */
3398         updateText : function(text)
3399         {
3400             if(!dlg.isVisible() && !opt.width){
3401                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3402                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3403             }
3404             msgEl.innerHTML = text || '&#160;';
3405       
3406             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3407             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3408             var w = Math.max(
3409                     Math.min(opt.width || cw , this.maxWidth), 
3410                     Math.max(opt.minWidth || this.minWidth, bwidth)
3411             );
3412             if(opt.prompt){
3413                 activeTextEl.setWidth(w);
3414             }
3415             if(dlg.isVisible()){
3416                 dlg.fixedcenter = false;
3417             }
3418             // to big, make it scroll. = But as usual stupid IE does not support
3419             // !important..
3420             
3421             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3422                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3423                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3424             } else {
3425                 bodyEl.dom.style.height = '';
3426                 bodyEl.dom.style.overflowY = '';
3427             }
3428             if (cw > w) {
3429                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3430             } else {
3431                 bodyEl.dom.style.overflowX = '';
3432             }
3433             
3434             dlg.setContentSize(w, bodyEl.getHeight());
3435             if(dlg.isVisible()){
3436                 dlg.fixedcenter = true;
3437             }
3438             return this;
3439         },
3440
3441         /**
3442          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3443          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3444          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3445          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3446          * @return {Roo.MessageBox} This message box
3447          */
3448         updateProgress : function(value, text){
3449             if(text){
3450                 this.updateText(text);
3451             }
3452             
3453             if (pp) { // weird bug on my firefox - for some reason this is not defined
3454                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3455                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3456             }
3457             return this;
3458         },        
3459
3460         /**
3461          * Returns true if the message box is currently displayed
3462          * @return {Boolean} True if the message box is visible, else false
3463          */
3464         isVisible : function(){
3465             return dlg && dlg.isVisible();  
3466         },
3467
3468         /**
3469          * Hides the message box if it is displayed
3470          */
3471         hide : function(){
3472             if(this.isVisible()){
3473                 dlg.hide();
3474             }  
3475         },
3476
3477         /**
3478          * Displays a new message box, or reinitializes an existing message box, based on the config options
3479          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3480          * The following config object properties are supported:
3481          * <pre>
3482 Property    Type             Description
3483 ----------  ---------------  ------------------------------------------------------------------------------------
3484 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3485                                    closes (defaults to undefined)
3486 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3487                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3488 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3489                                    progress and wait dialogs will ignore this property and always hide the
3490                                    close button as they can only be closed programmatically.
3491 cls               String           A custom CSS class to apply to the message box element
3492 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3493                                    displayed (defaults to 75)
3494 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3495                                    function will be btn (the name of the button that was clicked, if applicable,
3496                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3497                                    Progress and wait dialogs will ignore this option since they do not respond to
3498                                    user actions and can only be closed programmatically, so any required function
3499                                    should be called by the same code after it closes the dialog.
3500 icon              String           A CSS class that provides a background image to be used as an icon for
3501                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3502 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3503 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3504 modal             Boolean          False to allow user interaction with the page while the message box is
3505                                    displayed (defaults to true)
3506 msg               String           A string that will replace the existing message box body text (defaults
3507                                    to the XHTML-compliant non-breaking space character '&#160;')
3508 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3509 progress          Boolean          True to display a progress bar (defaults to false)
3510 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3511 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3512 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3513 title             String           The title text
3514 value             String           The string value to set into the active textbox element if displayed
3515 wait              Boolean          True to display a progress bar (defaults to false)
3516 width             Number           The width of the dialog in pixels
3517 </pre>
3518          *
3519          * Example usage:
3520          * <pre><code>
3521 Roo.Msg.show({
3522    title: 'Address',
3523    msg: 'Please enter your address:',
3524    width: 300,
3525    buttons: Roo.MessageBox.OKCANCEL,
3526    multiline: true,
3527    fn: saveAddress,
3528    animEl: 'addAddressBtn'
3529 });
3530 </code></pre>
3531          * @param {Object} config Configuration options
3532          * @return {Roo.MessageBox} This message box
3533          */
3534         show : function(options)
3535         {
3536             
3537             // this causes nightmares if you show one dialog after another
3538             // especially on callbacks..
3539              
3540             if(this.isVisible()){
3541                 
3542                 this.hide();
3543                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3544                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3545                 Roo.log("New Dialog Message:" +  options.msg )
3546                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3547                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3548                 
3549             }
3550             var d = this.getDialog();
3551             opt = options;
3552             d.setTitle(opt.title || "&#160;");
3553             d.closeEl.setDisplayed(opt.closable !== false);
3554             activeTextEl = textboxEl;
3555             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3556             if(opt.prompt){
3557                 if(opt.multiline){
3558                     textboxEl.hide();
3559                     textareaEl.show();
3560                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3561                         opt.multiline : this.defaultTextHeight);
3562                     activeTextEl = textareaEl;
3563                 }else{
3564                     textboxEl.show();
3565                     textareaEl.hide();
3566                 }
3567             }else{
3568                 textboxEl.hide();
3569                 textareaEl.hide();
3570             }
3571             progressEl.setDisplayed(opt.progress === true);
3572             this.updateProgress(0);
3573             activeTextEl.dom.value = opt.value || "";
3574             if(opt.prompt){
3575                 dlg.setDefaultButton(activeTextEl);
3576             }else{
3577                 var bs = opt.buttons;
3578                 var db = null;
3579                 if(bs && bs.ok){
3580                     db = buttons["ok"];
3581                 }else if(bs && bs.yes){
3582                     db = buttons["yes"];
3583                 }
3584                 dlg.setDefaultButton(db);
3585             }
3586             bwidth = updateButtons(opt.buttons);
3587             this.updateText(opt.msg);
3588             if(opt.cls){
3589                 d.el.addClass(opt.cls);
3590             }
3591             d.proxyDrag = opt.proxyDrag === true;
3592             d.modal = opt.modal !== false;
3593             d.mask = opt.modal !== false ? mask : false;
3594             if(!d.isVisible()){
3595                 // force it to the end of the z-index stack so it gets a cursor in FF
3596                 document.body.appendChild(dlg.el.dom);
3597                 d.animateTarget = null;
3598                 d.show(options.animEl);
3599             }
3600             return this;
3601         },
3602
3603         /**
3604          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3605          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3606          * and closing the message box when the process is complete.
3607          * @param {String} title The title bar text
3608          * @param {String} msg The message box body text
3609          * @return {Roo.MessageBox} This message box
3610          */
3611         progress : function(title, msg){
3612             this.show({
3613                 title : title,
3614                 msg : msg,
3615                 buttons: false,
3616                 progress:true,
3617                 closable:false,
3618                 minWidth: this.minProgressWidth,
3619                 modal : true
3620             });
3621             return this;
3622         },
3623
3624         /**
3625          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3626          * If a callback function is passed it will be called after the user clicks the button, and the
3627          * id of the button that was clicked will be passed as the only parameter to the callback
3628          * (could also be the top-right close button).
3629          * @param {String} title The title bar text
3630          * @param {String} msg The message box body text
3631          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3632          * @param {Object} scope (optional) The scope of the callback function
3633          * @return {Roo.MessageBox} This message box
3634          */
3635         alert : function(title, msg, fn, scope)
3636         {
3637             this.show({
3638                 title : title,
3639                 msg : msg,
3640                 buttons: this.OK,
3641                 fn: fn,
3642                 closable : false,
3643                 scope : scope,
3644                 modal : true
3645             });
3646             return this;
3647         },
3648
3649         /**
3650          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3651          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3652          * You are responsible for closing the message box when the process is complete.
3653          * @param {String} msg The message box body text
3654          * @param {String} title (optional) The title bar text
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         wait : function(msg, title){
3658             this.show({
3659                 title : title,
3660                 msg : msg,
3661                 buttons: false,
3662                 closable:false,
3663                 progress:true,
3664                 modal:true,
3665                 width:300,
3666                 wait:true
3667             });
3668             waitTimer = Roo.TaskMgr.start({
3669                 run: function(i){
3670                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3671                 },
3672                 interval: 1000
3673             });
3674             return this;
3675         },
3676
3677         /**
3678          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3679          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3680          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3681          * @param {String} title The title bar text
3682          * @param {String} msg The message box body text
3683          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3684          * @param {Object} scope (optional) The scope of the callback function
3685          * @return {Roo.MessageBox} This message box
3686          */
3687         confirm : function(title, msg, fn, scope){
3688             this.show({
3689                 title : title,
3690                 msg : msg,
3691                 buttons: this.YESNO,
3692                 fn: fn,
3693                 scope : scope,
3694                 modal : true
3695             });
3696             return this;
3697         },
3698
3699         /**
3700          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3701          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3702          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3703          * (could also be the top-right close button) and the text that was entered will be passed as the two
3704          * parameters to the callback.
3705          * @param {String} title The title bar text
3706          * @param {String} msg The message box body text
3707          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3708          * @param {Object} scope (optional) The scope of the callback function
3709          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3710          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3711          * @return {Roo.MessageBox} This message box
3712          */
3713         prompt : function(title, msg, fn, scope, multiline){
3714             this.show({
3715                 title : title,
3716                 msg : msg,
3717                 buttons: this.OKCANCEL,
3718                 fn: fn,
3719                 minWidth:250,
3720                 scope : scope,
3721                 prompt:true,
3722                 multiline: multiline,
3723                 modal : true
3724             });
3725             return this;
3726         },
3727
3728         /**
3729          * Button config that displays a single OK button
3730          * @type Object
3731          */
3732         OK : {ok:true},
3733         /**
3734          * Button config that displays Yes and No buttons
3735          * @type Object
3736          */
3737         YESNO : {yes:true, no:true},
3738         /**
3739          * Button config that displays OK and Cancel buttons
3740          * @type Object
3741          */
3742         OKCANCEL : {ok:true, cancel:true},
3743         /**
3744          * Button config that displays Yes, No and Cancel buttons
3745          * @type Object
3746          */
3747         YESNOCANCEL : {yes:true, no:true, cancel:true},
3748
3749         /**
3750          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3751          * @type Number
3752          */
3753         defaultTextHeight : 75,
3754         /**
3755          * The maximum width in pixels of the message box (defaults to 600)
3756          * @type Number
3757          */
3758         maxWidth : 600,
3759         /**
3760          * The minimum width in pixels of the message box (defaults to 100)
3761          * @type Number
3762          */
3763         minWidth : 100,
3764         /**
3765          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3766          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3767          * @type Number
3768          */
3769         minProgressWidth : 250,
3770         /**
3771          * An object containing the default button text strings that can be overriden for localized language support.
3772          * Supported properties are: ok, cancel, yes and no.
3773          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3774          * @type Object
3775          */
3776         buttonText : {
3777             ok : "OK",
3778             cancel : "Cancel",
3779             yes : "Yes",
3780             no : "No"
3781         }
3782     };
3783 }();
3784
3785 /**
3786  * Shorthand for {@link Roo.MessageBox}
3787  */
3788 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3789 Roo.Msg = Roo.Msg || Roo.MessageBox;
3790 /*
3791  * - LGPL
3792  *
3793  * navbar
3794  * 
3795  */
3796
3797 /**
3798  * @class Roo.bootstrap.Navbar
3799  * @extends Roo.bootstrap.Component
3800  * Bootstrap Navbar class
3801
3802  * @constructor
3803  * Create a new Navbar
3804  * @param {Object} config The config object
3805  */
3806
3807
3808 Roo.bootstrap.Navbar = function(config){
3809     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3810     this.addEvents({
3811         // raw events
3812         /**
3813          * @event beforetoggle
3814          * Fire before toggle the menu
3815          * @param {Roo.EventObject} e
3816          */
3817         "beforetoggle" : true
3818     });
3819 };
3820
3821 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3822     
3823     
3824    
3825     // private
3826     navItems : false,
3827     loadMask : false,
3828     
3829     
3830     getAutoCreate : function(){
3831         
3832         
3833         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3834         
3835     },
3836     
3837     initEvents :function ()
3838     {
3839         //Roo.log(this.el.select('.navbar-toggle',true));
3840         this.el.select('.navbar-toggle',true).on('click', function() {
3841             if(this.fireEvent('beforetoggle', this) !== false){
3842                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3843             }
3844             
3845         }, this);
3846         
3847         var mark = {
3848             tag: "div",
3849             cls:"x-dlg-mask"
3850         };
3851         
3852         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3853         
3854         var size = this.el.getSize();
3855         this.maskEl.setSize(size.width, size.height);
3856         this.maskEl.enableDisplayMode("block");
3857         this.maskEl.hide();
3858         
3859         if(this.loadMask){
3860             this.maskEl.show();
3861         }
3862     },
3863     
3864     
3865     getChildContainer : function()
3866     {
3867         if (this.el.select('.collapse').getCount()) {
3868             return this.el.select('.collapse',true).first();
3869         }
3870         
3871         return this.el;
3872     },
3873     
3874     mask : function()
3875     {
3876         this.maskEl.show();
3877     },
3878     
3879     unmask : function()
3880     {
3881         this.maskEl.hide();
3882     } 
3883     
3884     
3885     
3886     
3887 });
3888
3889
3890
3891  
3892
3893  /*
3894  * - LGPL
3895  *
3896  * navbar
3897  * 
3898  */
3899
3900 /**
3901  * @class Roo.bootstrap.NavSimplebar
3902  * @extends Roo.bootstrap.Navbar
3903  * Bootstrap Sidebar class
3904  *
3905  * @cfg {Boolean} inverse is inverted color
3906  * 
3907  * @cfg {String} type (nav | pills | tabs)
3908  * @cfg {Boolean} arrangement stacked | justified
3909  * @cfg {String} align (left | right) alignment
3910  * 
3911  * @cfg {Boolean} main (true|false) main nav bar? default false
3912  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3913  * 
3914  * @cfg {String} tag (header|footer|nav|div) default is nav 
3915
3916  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3917  * 
3918  * 
3919  * @constructor
3920  * Create a new Sidebar
3921  * @param {Object} config The config object
3922  */
3923
3924
3925 Roo.bootstrap.NavSimplebar = function(config){
3926     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3927 };
3928
3929 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3930     
3931     inverse: false,
3932     
3933     type: false,
3934     arrangement: '',
3935     align : false,
3936     
3937     weight : 'light',
3938     
3939     main : false,
3940     
3941     
3942     tag : false,
3943     
3944     
3945     getAutoCreate : function(){
3946         
3947         
3948         var cfg = {
3949             tag : this.tag || 'div',
3950             cls : 'navbar navbar-expand-lg'
3951         };
3952         if (['light','white'].indexOf(this.weight) > -1) {
3953             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3954         }
3955         cfg.cls += ' bg-' + this.weight;
3956         
3957           
3958         
3959         cfg.cn = [
3960             {
3961                 cls: 'nav',
3962                 tag : 'ul'
3963             }
3964         ];
3965         
3966          
3967         this.type = this.type || 'nav';
3968         if (['tabs','pills'].indexOf(this.type)!==-1) {
3969             cfg.cn[0].cls += ' nav-' + this.type
3970         
3971         
3972         } else {
3973             if (this.type!=='nav') {
3974                 Roo.log('nav type must be nav/tabs/pills')
3975             }
3976             cfg.cn[0].cls += ' navbar-nav'
3977         }
3978         
3979         
3980         
3981         
3982         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3983             cfg.cn[0].cls += ' nav-' + this.arrangement;
3984         }
3985         
3986         
3987         if (this.align === 'right') {
3988             cfg.cn[0].cls += ' navbar-right';
3989         }
3990         
3991         if (this.inverse) {
3992             cfg.cls += ' navbar-inverse';
3993             
3994         }
3995         
3996         
3997         return cfg;
3998     
3999         
4000     }
4001     
4002     
4003     
4004 });
4005
4006
4007
4008  
4009
4010  
4011        /*
4012  * - LGPL
4013  *
4014  * navbar
4015  * navbar-fixed-top
4016  * navbar-expand-md  fixed-top 
4017  */
4018
4019 /**
4020  * @class Roo.bootstrap.NavHeaderbar
4021  * @extends Roo.bootstrap.NavSimplebar
4022  * Bootstrap Sidebar class
4023  *
4024  * @cfg {String} brand what is brand
4025  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4026  * @cfg {String} brand_href href of the brand
4027  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4028  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4029  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4030  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4031  * 
4032  * @constructor
4033  * Create a new Sidebar
4034  * @param {Object} config The config object
4035  */
4036
4037
4038 Roo.bootstrap.NavHeaderbar = function(config){
4039     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4040       
4041 };
4042
4043 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4044     
4045     position: '',
4046     brand: '',
4047     brand_href: false,
4048     srButton : true,
4049     autohide : false,
4050     desktopCenter : false,
4051    
4052     
4053     getAutoCreate : function(){
4054         
4055         var   cfg = {
4056             tag: this.nav || 'nav',
4057             cls: 'navbar navbar-expand-md',
4058             role: 'navigation',
4059             cn: []
4060         };
4061         
4062         var cn = cfg.cn;
4063         if (this.desktopCenter) {
4064             cn.push({cls : 'container', cn : []});
4065             cn = cn[0].cn;
4066         }
4067         
4068         if(this.srButton){
4069             cn.push({
4070                 tag: 'div',
4071                 cls: 'navbar-header',
4072                 cn: [
4073                     {
4074                         tag: 'button',
4075                         type: 'button',
4076                         cls: 'navbar-toggle navbar-toggler',
4077                         'data-toggle': 'collapse',
4078                         cn: [
4079                             {
4080                                 tag: 'span',
4081                                 cls: 'sr-only',
4082                                 html: 'Toggle navigation'
4083                             },
4084                             {
4085                                 tag: 'span',
4086                                 cls: 'icon-bar navbar-toggler-icon'
4087                             },
4088                             {
4089                                 tag: 'span',
4090                                 cls: 'icon-bar'
4091                             },
4092                             {
4093                                 tag: 'span',
4094                                 cls: 'icon-bar'
4095                             }
4096                         ]
4097                     }
4098                 ]
4099             });
4100         }
4101         
4102         cn.push({
4103             tag: 'div',
4104             cls: 'collapse navbar-collapse',
4105             cn : []
4106         });
4107         
4108         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4109         
4110         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4111             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4112             
4113             // tag can override this..
4114             
4115             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4116         }
4117         
4118         if (this.brand !== '') {
4119             cn[0].cn.push({
4120                 tag: 'a',
4121                 href: this.brand_href ? this.brand_href : '#',
4122                 cls: 'navbar-brand',
4123                 cn: [
4124                 this.brand
4125                 ]
4126             });
4127         }
4128         
4129         if(this.main){
4130             cfg.cls += ' main-nav';
4131         }
4132         
4133         
4134         return cfg;
4135
4136         
4137     },
4138     getHeaderChildContainer : function()
4139     {
4140         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4141             return this.el.select('.navbar-header',true).first();
4142         }
4143         
4144         return this.getChildContainer();
4145     },
4146     
4147     
4148     initEvents : function()
4149     {
4150         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4151         
4152         if (this.autohide) {
4153             
4154             var prevScroll = 0;
4155             var ft = this.el;
4156             
4157             Roo.get(document).on('scroll',function(e) {
4158                 var ns = Roo.get(document).getScroll().top;
4159                 var os = prevScroll;
4160                 prevScroll = ns;
4161                 
4162                 if(ns > os){
4163                     ft.removeClass('slideDown');
4164                     ft.addClass('slideUp');
4165                     return;
4166                 }
4167                 ft.removeClass('slideUp');
4168                 ft.addClass('slideDown');
4169                  
4170               
4171           },this);
4172         }
4173     }    
4174     
4175 });
4176
4177
4178
4179  
4180
4181  /*
4182  * - LGPL
4183  *
4184  * navbar
4185  * 
4186  */
4187
4188 /**
4189  * @class Roo.bootstrap.NavSidebar
4190  * @extends Roo.bootstrap.Navbar
4191  * Bootstrap Sidebar class
4192  * 
4193  * @constructor
4194  * Create a new Sidebar
4195  * @param {Object} config The config object
4196  */
4197
4198
4199 Roo.bootstrap.NavSidebar = function(config){
4200     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4201 };
4202
4203 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4204     
4205     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4206     
4207     getAutoCreate : function(){
4208         
4209         
4210         return  {
4211             tag: 'div',
4212             cls: 'sidebar sidebar-nav'
4213         };
4214     
4215         
4216     }
4217     
4218     
4219     
4220 });
4221
4222
4223
4224  
4225
4226  /*
4227  * - LGPL
4228  *
4229  * nav group
4230  * 
4231  */
4232
4233 /**
4234  * @class Roo.bootstrap.NavGroup
4235  * @extends Roo.bootstrap.Component
4236  * Bootstrap NavGroup class
4237  * @cfg {String} align (left|right)
4238  * @cfg {Boolean} inverse
4239  * @cfg {String} type (nav|pills|tab) default nav
4240  * @cfg {String} navId - reference Id for navbar.
4241
4242  * 
4243  * @constructor
4244  * Create a new nav group
4245  * @param {Object} config The config object
4246  */
4247
4248 Roo.bootstrap.NavGroup = function(config){
4249     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4250     this.navItems = [];
4251    
4252     Roo.bootstrap.NavGroup.register(this);
4253      this.addEvents({
4254         /**
4255              * @event changed
4256              * Fires when the active item changes
4257              * @param {Roo.bootstrap.NavGroup} this
4258              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4259              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4260          */
4261         'changed': true
4262      });
4263     
4264 };
4265
4266 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4267     
4268     align: '',
4269     inverse: false,
4270     form: false,
4271     type: 'nav',
4272     navId : '',
4273     // private
4274     
4275     navItems : false, 
4276     
4277     getAutoCreate : function()
4278     {
4279         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4280         
4281         cfg = {
4282             tag : 'ul',
4283             cls: 'nav' 
4284         };
4285         
4286         if (['tabs','pills'].indexOf(this.type)!==-1) {
4287             cfg.cls += ' nav-' + this.type
4288         } else {
4289             if (this.type!=='nav') {
4290                 Roo.log('nav type must be nav/tabs/pills')
4291             }
4292             cfg.cls += ' navbar-nav'
4293         }
4294         
4295         if (this.parent() && this.parent().sidebar) {
4296             cfg = {
4297                 tag: 'ul',
4298                 cls: 'dashboard-menu sidebar-menu'
4299             };
4300             
4301             return cfg;
4302         }
4303         
4304         if (this.form === true) {
4305             cfg = {
4306                 tag: 'form',
4307                 cls: 'navbar-form'
4308             };
4309             
4310             if (this.align === 'right') {
4311                 cfg.cls += ' navbar-right ml-md-auto';
4312             } else {
4313                 cfg.cls += ' navbar-left';
4314             }
4315         }
4316         
4317         if (this.align === 'right') {
4318             cfg.cls += ' navbar-right ml-md-auto';
4319         } else {
4320             cfg.cls += ' mr-auto';
4321         }
4322         
4323         if (this.inverse) {
4324             cfg.cls += ' navbar-inverse';
4325             
4326         }
4327         
4328         
4329         return cfg;
4330     },
4331     /**
4332     * sets the active Navigation item
4333     * @param {Roo.bootstrap.NavItem} the new current navitem
4334     */
4335     setActiveItem : function(item)
4336     {
4337         var prev = false;
4338         Roo.each(this.navItems, function(v){
4339             if (v == item) {
4340                 return ;
4341             }
4342             if (v.isActive()) {
4343                 v.setActive(false, true);
4344                 prev = v;
4345                 
4346             }
4347             
4348         });
4349
4350         item.setActive(true, true);
4351         this.fireEvent('changed', this, item, prev);
4352         
4353         
4354     },
4355     /**
4356     * gets the active Navigation item
4357     * @return {Roo.bootstrap.NavItem} the current navitem
4358     */
4359     getActive : function()
4360     {
4361         
4362         var prev = false;
4363         Roo.each(this.navItems, function(v){
4364             
4365             if (v.isActive()) {
4366                 prev = v;
4367                 
4368             }
4369             
4370         });
4371         return prev;
4372     },
4373     
4374     indexOfNav : function()
4375     {
4376         
4377         var prev = false;
4378         Roo.each(this.navItems, function(v,i){
4379             
4380             if (v.isActive()) {
4381                 prev = i;
4382                 
4383             }
4384             
4385         });
4386         return prev;
4387     },
4388     /**
4389     * adds a Navigation item
4390     * @param {Roo.bootstrap.NavItem} the navitem to add
4391     */
4392     addItem : function(cfg)
4393     {
4394         var cn = new Roo.bootstrap.NavItem(cfg);
4395         this.register(cn);
4396         cn.parentId = this.id;
4397         cn.onRender(this.el, null);
4398         return cn;
4399     },
4400     /**
4401     * register a Navigation item
4402     * @param {Roo.bootstrap.NavItem} the navitem to add
4403     */
4404     register : function(item)
4405     {
4406         this.navItems.push( item);
4407         item.navId = this.navId;
4408     
4409     },
4410     
4411     /**
4412     * clear all the Navigation item
4413     */
4414    
4415     clearAll : function()
4416     {
4417         this.navItems = [];
4418         this.el.dom.innerHTML = '';
4419     },
4420     
4421     getNavItem: function(tabId)
4422     {
4423         var ret = false;
4424         Roo.each(this.navItems, function(e) {
4425             if (e.tabId == tabId) {
4426                ret =  e;
4427                return false;
4428             }
4429             return true;
4430             
4431         });
4432         return ret;
4433     },
4434     
4435     setActiveNext : function()
4436     {
4437         var i = this.indexOfNav(this.getActive());
4438         if (i > this.navItems.length) {
4439             return;
4440         }
4441         this.setActiveItem(this.navItems[i+1]);
4442     },
4443     setActivePrev : function()
4444     {
4445         var i = this.indexOfNav(this.getActive());
4446         if (i  < 1) {
4447             return;
4448         }
4449         this.setActiveItem(this.navItems[i-1]);
4450     },
4451     clearWasActive : function(except) {
4452         Roo.each(this.navItems, function(e) {
4453             if (e.tabId != except.tabId && e.was_active) {
4454                e.was_active = false;
4455                return false;
4456             }
4457             return true;
4458             
4459         });
4460     },
4461     getWasActive : function ()
4462     {
4463         var r = false;
4464         Roo.each(this.navItems, function(e) {
4465             if (e.was_active) {
4466                r = e;
4467                return false;
4468             }
4469             return true;
4470             
4471         });
4472         return r;
4473     }
4474     
4475     
4476 });
4477
4478  
4479 Roo.apply(Roo.bootstrap.NavGroup, {
4480     
4481     groups: {},
4482      /**
4483     * register a Navigation Group
4484     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4485     */
4486     register : function(navgrp)
4487     {
4488         this.groups[navgrp.navId] = navgrp;
4489         
4490     },
4491     /**
4492     * fetch a Navigation Group based on the navigation ID
4493     * @param {string} the navgroup to add
4494     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4495     */
4496     get: function(navId) {
4497         if (typeof(this.groups[navId]) == 'undefined') {
4498             return false;
4499             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4500         }
4501         return this.groups[navId] ;
4502     }
4503     
4504     
4505     
4506 });
4507
4508  /*
4509  * - LGPL
4510  *
4511  * row
4512  * 
4513  */
4514
4515 /**
4516  * @class Roo.bootstrap.NavItem
4517  * @extends Roo.bootstrap.Component
4518  * Bootstrap Navbar.NavItem class
4519  * @cfg {String} href  link to
4520  * @cfg {String} html content of button
4521  * @cfg {String} badge text inside badge
4522  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4523  * @cfg {String} glyphicon name of glyphicon
4524  * @cfg {String} icon name of font awesome icon
4525  * @cfg {Boolean} active Is item active
4526  * @cfg {Boolean} disabled Is item disabled
4527  
4528  * @cfg {Boolean} preventDefault (true | false) default false
4529  * @cfg {String} tabId the tab that this item activates.
4530  * @cfg {String} tagtype (a|span) render as a href or span?
4531  * @cfg {Boolean} animateRef (true|false) link to element default false  
4532   
4533  * @constructor
4534  * Create a new Navbar Item
4535  * @param {Object} config The config object
4536  */
4537 Roo.bootstrap.NavItem = function(config){
4538     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4539     this.addEvents({
4540         // raw events
4541         /**
4542          * @event click
4543          * The raw click event for the entire grid.
4544          * @param {Roo.EventObject} e
4545          */
4546         "click" : true,
4547          /**
4548             * @event changed
4549             * Fires when the active item active state changes
4550             * @param {Roo.bootstrap.NavItem} this
4551             * @param {boolean} state the new state
4552              
4553          */
4554         'changed': true,
4555         /**
4556             * @event scrollto
4557             * Fires when scroll to element
4558             * @param {Roo.bootstrap.NavItem} this
4559             * @param {Object} options
4560             * @param {Roo.EventObject} e
4561              
4562          */
4563         'scrollto': true
4564     });
4565    
4566 };
4567
4568 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4569     
4570     href: false,
4571     html: '',
4572     badge: '',
4573     icon: false,
4574     glyphicon: false,
4575     active: false,
4576     preventDefault : false,
4577     tabId : false,
4578     tagtype : 'a',
4579     disabled : false,
4580     animateRef : false,
4581     was_active : false,
4582     
4583     getAutoCreate : function(){
4584          
4585         var cfg = {
4586             tag: 'li',
4587             cls: 'nav-item'
4588             
4589         };
4590         
4591         if (this.active) {
4592             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4593         }
4594         if (this.disabled) {
4595             cfg.cls += ' disabled';
4596         }
4597         
4598         if (this.href || this.html || this.glyphicon || this.icon) {
4599             cfg.cn = [
4600                 {
4601                     tag: this.tagtype,
4602                     href : this.href || "#",
4603                     html: this.html || ''
4604                 }
4605             ];
4606             if (this.tagtype == 'a') {
4607                 cfg.cn[0].cls = 'nav-link';
4608             }
4609             if (this.icon) {
4610                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4611             }
4612
4613             if(this.glyphicon) {
4614                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4615             }
4616             
4617             if (this.menu) {
4618                 
4619                 cfg.cn[0].html += " <span class='caret'></span>";
4620              
4621             }
4622             
4623             if (this.badge !== '') {
4624                  
4625                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4626             }
4627         }
4628         
4629         
4630         
4631         return cfg;
4632     },
4633     initEvents: function() 
4634     {
4635         if (typeof (this.menu) != 'undefined') {
4636             this.menu.parentType = this.xtype;
4637             this.menu.triggerEl = this.el;
4638             this.menu = this.addxtype(Roo.apply({}, this.menu));
4639         }
4640         
4641         this.el.select('a',true).on('click', this.onClick, this);
4642         
4643         if(this.tagtype == 'span'){
4644             this.el.select('span',true).on('click', this.onClick, this);
4645         }
4646        
4647         // at this point parent should be available..
4648         this.parent().register(this);
4649     },
4650     
4651     onClick : function(e)
4652     {
4653         if (e.getTarget('.dropdown-menu-item')) {
4654             // did you click on a menu itemm.... - then don't trigger onclick..
4655             return;
4656         }
4657         
4658         if(
4659                 this.preventDefault || 
4660                 this.href == '#' 
4661         ){
4662             Roo.log("NavItem - prevent Default?");
4663             e.preventDefault();
4664         }
4665         
4666         if (this.disabled) {
4667             return;
4668         }
4669         
4670         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4671         if (tg && tg.transition) {
4672             Roo.log("waiting for the transitionend");
4673             return;
4674         }
4675         
4676         
4677         
4678         //Roo.log("fire event clicked");
4679         if(this.fireEvent('click', this, e) === false){
4680             return;
4681         };
4682         
4683         if(this.tagtype == 'span'){
4684             return;
4685         }
4686         
4687         //Roo.log(this.href);
4688         var ael = this.el.select('a',true).first();
4689         //Roo.log(ael);
4690         
4691         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4692             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4693             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4694                 return; // ignore... - it's a 'hash' to another page.
4695             }
4696             Roo.log("NavItem - prevent Default?");
4697             e.preventDefault();
4698             this.scrollToElement(e);
4699         }
4700         
4701         
4702         var p =  this.parent();
4703    
4704         if (['tabs','pills'].indexOf(p.type)!==-1) {
4705             if (typeof(p.setActiveItem) !== 'undefined') {
4706                 p.setActiveItem(this);
4707             }
4708         }
4709         
4710         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4711         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4712             // remove the collapsed menu expand...
4713             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4714         }
4715     },
4716     
4717     isActive: function () {
4718         return this.active
4719     },
4720     setActive : function(state, fire, is_was_active)
4721     {
4722         if (this.active && !state && this.navId) {
4723             this.was_active = true;
4724             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4725             if (nv) {
4726                 nv.clearWasActive(this);
4727             }
4728             
4729         }
4730         this.active = state;
4731         
4732         if (!state ) {
4733             this.el.removeClass('active');
4734         } else if (!this.el.hasClass('active')) {
4735             this.el.addClass('active');
4736         }
4737         if (fire) {
4738             this.fireEvent('changed', this, state);
4739         }
4740         
4741         // show a panel if it's registered and related..
4742         
4743         if (!this.navId || !this.tabId || !state || is_was_active) {
4744             return;
4745         }
4746         
4747         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4748         if (!tg) {
4749             return;
4750         }
4751         var pan = tg.getPanelByName(this.tabId);
4752         if (!pan) {
4753             return;
4754         }
4755         // if we can not flip to new panel - go back to old nav highlight..
4756         if (false == tg.showPanel(pan)) {
4757             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4758             if (nv) {
4759                 var onav = nv.getWasActive();
4760                 if (onav) {
4761                     onav.setActive(true, false, true);
4762                 }
4763             }
4764             
4765         }
4766         
4767         
4768         
4769     },
4770      // this should not be here...
4771     setDisabled : function(state)
4772     {
4773         this.disabled = state;
4774         if (!state ) {
4775             this.el.removeClass('disabled');
4776         } else if (!this.el.hasClass('disabled')) {
4777             this.el.addClass('disabled');
4778         }
4779         
4780     },
4781     
4782     /**
4783      * Fetch the element to display the tooltip on.
4784      * @return {Roo.Element} defaults to this.el
4785      */
4786     tooltipEl : function()
4787     {
4788         return this.el.select('' + this.tagtype + '', true).first();
4789     },
4790     
4791     scrollToElement : function(e)
4792     {
4793         var c = document.body;
4794         
4795         /*
4796          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4797          */
4798         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4799             c = document.documentElement;
4800         }
4801         
4802         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4803         
4804         if(!target){
4805             return;
4806         }
4807
4808         var o = target.calcOffsetsTo(c);
4809         
4810         var options = {
4811             target : target,
4812             value : o[1]
4813         };
4814         
4815         this.fireEvent('scrollto', this, options, e);
4816         
4817         Roo.get(c).scrollTo('top', options.value, true);
4818         
4819         return;
4820     }
4821 });
4822  
4823
4824  /*
4825  * - LGPL
4826  *
4827  * sidebar item
4828  *
4829  *  li
4830  *    <span> icon </span>
4831  *    <span> text </span>
4832  *    <span>badge </span>
4833  */
4834
4835 /**
4836  * @class Roo.bootstrap.NavSidebarItem
4837  * @extends Roo.bootstrap.NavItem
4838  * Bootstrap Navbar.NavSidebarItem class
4839  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4840  * {Boolean} open is the menu open
4841  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4842  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4843  * {String} buttonSize (sm|md|lg)the extra classes for the button
4844  * {Boolean} showArrow show arrow next to the text (default true)
4845  * @constructor
4846  * Create a new Navbar Button
4847  * @param {Object} config The config object
4848  */
4849 Roo.bootstrap.NavSidebarItem = function(config){
4850     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4851     this.addEvents({
4852         // raw events
4853         /**
4854          * @event click
4855          * The raw click event for the entire grid.
4856          * @param {Roo.EventObject} e
4857          */
4858         "click" : true,
4859          /**
4860             * @event changed
4861             * Fires when the active item active state changes
4862             * @param {Roo.bootstrap.NavSidebarItem} this
4863             * @param {boolean} state the new state
4864              
4865          */
4866         'changed': true
4867     });
4868    
4869 };
4870
4871 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4872     
4873     badgeWeight : 'default',
4874     
4875     open: false,
4876     
4877     buttonView : false,
4878     
4879     buttonWeight : 'default',
4880     
4881     buttonSize : 'md',
4882     
4883     showArrow : true,
4884     
4885     getAutoCreate : function(){
4886         
4887         
4888         var a = {
4889                 tag: 'a',
4890                 href : this.href || '#',
4891                 cls: '',
4892                 html : '',
4893                 cn : []
4894         };
4895         
4896         if(this.buttonView){
4897             a = {
4898                 tag: 'button',
4899                 href : this.href || '#',
4900                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4901                 html : this.html,
4902                 cn : []
4903             };
4904         }
4905         
4906         var cfg = {
4907             tag: 'li',
4908             cls: '',
4909             cn: [ a ]
4910         };
4911         
4912         if (this.active) {
4913             cfg.cls += ' active';
4914         }
4915         
4916         if (this.disabled) {
4917             cfg.cls += ' disabled';
4918         }
4919         if (this.open) {
4920             cfg.cls += ' open x-open';
4921         }
4922         // left icon..
4923         if (this.glyphicon || this.icon) {
4924             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4925             a.cn.push({ tag : 'i', cls : c }) ;
4926         }
4927         
4928         if(!this.buttonView){
4929             var span = {
4930                 tag: 'span',
4931                 html : this.html || ''
4932             };
4933
4934             a.cn.push(span);
4935             
4936         }
4937         
4938         if (this.badge !== '') {
4939             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4940         }
4941         
4942         if (this.menu) {
4943             
4944             if(this.showArrow){
4945                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4946             }
4947             
4948             a.cls += ' dropdown-toggle treeview' ;
4949         }
4950         
4951         return cfg;
4952     },
4953     
4954     initEvents : function()
4955     { 
4956         if (typeof (this.menu) != 'undefined') {
4957             this.menu.parentType = this.xtype;
4958             this.menu.triggerEl = this.el;
4959             this.menu = this.addxtype(Roo.apply({}, this.menu));
4960         }
4961         
4962         this.el.on('click', this.onClick, this);
4963         
4964         if(this.badge !== ''){
4965             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4966         }
4967         
4968     },
4969     
4970     onClick : function(e)
4971     {
4972         if(this.disabled){
4973             e.preventDefault();
4974             return;
4975         }
4976         
4977         if(this.preventDefault){
4978             e.preventDefault();
4979         }
4980         
4981         this.fireEvent('click', this);
4982     },
4983     
4984     disable : function()
4985     {
4986         this.setDisabled(true);
4987     },
4988     
4989     enable : function()
4990     {
4991         this.setDisabled(false);
4992     },
4993     
4994     setDisabled : function(state)
4995     {
4996         if(this.disabled == state){
4997             return;
4998         }
4999         
5000         this.disabled = state;
5001         
5002         if (state) {
5003             this.el.addClass('disabled');
5004             return;
5005         }
5006         
5007         this.el.removeClass('disabled');
5008         
5009         return;
5010     },
5011     
5012     setActive : function(state)
5013     {
5014         if(this.active == state){
5015             return;
5016         }
5017         
5018         this.active = state;
5019         
5020         if (state) {
5021             this.el.addClass('active');
5022             return;
5023         }
5024         
5025         this.el.removeClass('active');
5026         
5027         return;
5028     },
5029     
5030     isActive: function () 
5031     {
5032         return this.active;
5033     },
5034     
5035     setBadge : function(str)
5036     {
5037         if(!this.badgeEl){
5038             return;
5039         }
5040         
5041         this.badgeEl.dom.innerHTML = str;
5042     }
5043     
5044    
5045      
5046  
5047 });
5048  
5049
5050  /*
5051  * - LGPL
5052  *
5053  * row
5054  * 
5055  */
5056
5057 /**
5058  * @class Roo.bootstrap.Row
5059  * @extends Roo.bootstrap.Component
5060  * Bootstrap Row class (contains columns...)
5061  * 
5062  * @constructor
5063  * Create a new Row
5064  * @param {Object} config The config object
5065  */
5066
5067 Roo.bootstrap.Row = function(config){
5068     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5069 };
5070
5071 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5072     
5073     getAutoCreate : function(){
5074        return {
5075             cls: 'row clearfix'
5076        };
5077     }
5078     
5079     
5080 });
5081
5082  
5083
5084  /*
5085  * - LGPL
5086  *
5087  * element
5088  * 
5089  */
5090
5091 /**
5092  * @class Roo.bootstrap.Element
5093  * @extends Roo.bootstrap.Component
5094  * Bootstrap Element class
5095  * @cfg {String} html contents of the element
5096  * @cfg {String} tag tag of the element
5097  * @cfg {String} cls class of the element
5098  * @cfg {Boolean} preventDefault (true|false) default false
5099  * @cfg {Boolean} clickable (true|false) default false
5100  * 
5101  * @constructor
5102  * Create a new Element
5103  * @param {Object} config The config object
5104  */
5105
5106 Roo.bootstrap.Element = function(config){
5107     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5108     
5109     this.addEvents({
5110         // raw events
5111         /**
5112          * @event click
5113          * When a element is chick
5114          * @param {Roo.bootstrap.Element} this
5115          * @param {Roo.EventObject} e
5116          */
5117         "click" : true
5118     });
5119 };
5120
5121 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5122     
5123     tag: 'div',
5124     cls: '',
5125     html: '',
5126     preventDefault: false, 
5127     clickable: false,
5128     
5129     getAutoCreate : function(){
5130         
5131         var cfg = {
5132             tag: this.tag,
5133             // cls: this.cls, double assign in parent class Component.js :: onRender
5134             html: this.html
5135         };
5136         
5137         return cfg;
5138     },
5139     
5140     initEvents: function() 
5141     {
5142         Roo.bootstrap.Element.superclass.initEvents.call(this);
5143         
5144         if(this.clickable){
5145             this.el.on('click', this.onClick, this);
5146         }
5147         
5148     },
5149     
5150     onClick : function(e)
5151     {
5152         if(this.preventDefault){
5153             e.preventDefault();
5154         }
5155         
5156         this.fireEvent('click', this, e);
5157     },
5158     
5159     getValue : function()
5160     {
5161         return this.el.dom.innerHTML;
5162     },
5163     
5164     setValue : function(value)
5165     {
5166         this.el.dom.innerHTML = value;
5167     }
5168    
5169 });
5170
5171  
5172
5173  /*
5174  * - LGPL
5175  *
5176  * pagination
5177  * 
5178  */
5179
5180 /**
5181  * @class Roo.bootstrap.Pagination
5182  * @extends Roo.bootstrap.Component
5183  * Bootstrap Pagination class
5184  * @cfg {String} size xs | sm | md | lg
5185  * @cfg {Boolean} inverse false | true
5186  * 
5187  * @constructor
5188  * Create a new Pagination
5189  * @param {Object} config The config object
5190  */
5191
5192 Roo.bootstrap.Pagination = function(config){
5193     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5194 };
5195
5196 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5197     
5198     cls: false,
5199     size: false,
5200     inverse: false,
5201     
5202     getAutoCreate : function(){
5203         var cfg = {
5204             tag: 'ul',
5205                 cls: 'pagination'
5206         };
5207         if (this.inverse) {
5208             cfg.cls += ' inverse';
5209         }
5210         if (this.html) {
5211             cfg.html=this.html;
5212         }
5213         if (this.cls) {
5214             cfg.cls += " " + this.cls;
5215         }
5216         return cfg;
5217     }
5218    
5219 });
5220
5221  
5222
5223  /*
5224  * - LGPL
5225  *
5226  * Pagination item
5227  * 
5228  */
5229
5230
5231 /**
5232  * @class Roo.bootstrap.PaginationItem
5233  * @extends Roo.bootstrap.Component
5234  * Bootstrap PaginationItem class
5235  * @cfg {String} html text
5236  * @cfg {String} href the link
5237  * @cfg {Boolean} preventDefault (true | false) default true
5238  * @cfg {Boolean} active (true | false) default false
5239  * @cfg {Boolean} disabled default false
5240  * 
5241  * 
5242  * @constructor
5243  * Create a new PaginationItem
5244  * @param {Object} config The config object
5245  */
5246
5247
5248 Roo.bootstrap.PaginationItem = function(config){
5249     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5250     this.addEvents({
5251         // raw events
5252         /**
5253          * @event click
5254          * The raw click event for the entire grid.
5255          * @param {Roo.EventObject} e
5256          */
5257         "click" : true
5258     });
5259 };
5260
5261 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5262     
5263     href : false,
5264     html : false,
5265     preventDefault: true,
5266     active : false,
5267     cls : false,
5268     disabled: false,
5269     
5270     getAutoCreate : function(){
5271         var cfg= {
5272             tag: 'li',
5273             cn: [
5274                 {
5275                     tag : 'a',
5276                     href : this.href ? this.href : '#',
5277                     html : this.html ? this.html : ''
5278                 }
5279             ]
5280         };
5281         
5282         if(this.cls){
5283             cfg.cls = this.cls;
5284         }
5285         
5286         if(this.disabled){
5287             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5288         }
5289         
5290         if(this.active){
5291             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5292         }
5293         
5294         return cfg;
5295     },
5296     
5297     initEvents: function() {
5298         
5299         this.el.on('click', this.onClick, this);
5300         
5301     },
5302     onClick : function(e)
5303     {
5304         Roo.log('PaginationItem on click ');
5305         if(this.preventDefault){
5306             e.preventDefault();
5307         }
5308         
5309         if(this.disabled){
5310             return;
5311         }
5312         
5313         this.fireEvent('click', this, e);
5314     }
5315    
5316 });
5317
5318  
5319
5320  /*
5321  * - LGPL
5322  *
5323  * slider
5324  * 
5325  */
5326
5327
5328 /**
5329  * @class Roo.bootstrap.Slider
5330  * @extends Roo.bootstrap.Component
5331  * Bootstrap Slider class
5332  *    
5333  * @constructor
5334  * Create a new Slider
5335  * @param {Object} config The config object
5336  */
5337
5338 Roo.bootstrap.Slider = function(config){
5339     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5340 };
5341
5342 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5343     
5344     getAutoCreate : function(){
5345         
5346         var cfg = {
5347             tag: 'div',
5348             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5349             cn: [
5350                 {
5351                     tag: 'a',
5352                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5353                 }
5354             ]
5355         };
5356         
5357         return cfg;
5358     }
5359    
5360 });
5361
5362  /*
5363  * Based on:
5364  * Ext JS Library 1.1.1
5365  * Copyright(c) 2006-2007, Ext JS, LLC.
5366  *
5367  * Originally Released Under LGPL - original licence link has changed is not relivant.
5368  *
5369  * Fork - LGPL
5370  * <script type="text/javascript">
5371  */
5372  
5373
5374 /**
5375  * @class Roo.grid.ColumnModel
5376  * @extends Roo.util.Observable
5377  * This is the default implementation of a ColumnModel used by the Grid. It defines
5378  * the columns in the grid.
5379  * <br>Usage:<br>
5380  <pre><code>
5381  var colModel = new Roo.grid.ColumnModel([
5382         {header: "Ticker", width: 60, sortable: true, locked: true},
5383         {header: "Company Name", width: 150, sortable: true},
5384         {header: "Market Cap.", width: 100, sortable: true},
5385         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5386         {header: "Employees", width: 100, sortable: true, resizable: false}
5387  ]);
5388  </code></pre>
5389  * <p>
5390  
5391  * The config options listed for this class are options which may appear in each
5392  * individual column definition.
5393  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5394  * @constructor
5395  * @param {Object} config An Array of column config objects. See this class's
5396  * config objects for details.
5397 */
5398 Roo.grid.ColumnModel = function(config){
5399         /**
5400      * The config passed into the constructor
5401      */
5402     this.config = config;
5403     this.lookup = {};
5404
5405     // if no id, create one
5406     // if the column does not have a dataIndex mapping,
5407     // map it to the order it is in the config
5408     for(var i = 0, len = config.length; i < len; i++){
5409         var c = config[i];
5410         if(typeof c.dataIndex == "undefined"){
5411             c.dataIndex = i;
5412         }
5413         if(typeof c.renderer == "string"){
5414             c.renderer = Roo.util.Format[c.renderer];
5415         }
5416         if(typeof c.id == "undefined"){
5417             c.id = Roo.id();
5418         }
5419         if(c.editor && c.editor.xtype){
5420             c.editor  = Roo.factory(c.editor, Roo.grid);
5421         }
5422         if(c.editor && c.editor.isFormField){
5423             c.editor = new Roo.grid.GridEditor(c.editor);
5424         }
5425         this.lookup[c.id] = c;
5426     }
5427
5428     /**
5429      * The width of columns which have no width specified (defaults to 100)
5430      * @type Number
5431      */
5432     this.defaultWidth = 100;
5433
5434     /**
5435      * Default sortable of columns which have no sortable specified (defaults to false)
5436      * @type Boolean
5437      */
5438     this.defaultSortable = false;
5439
5440     this.addEvents({
5441         /**
5442              * @event widthchange
5443              * Fires when the width of a column changes.
5444              * @param {ColumnModel} this
5445              * @param {Number} columnIndex The column index
5446              * @param {Number} newWidth The new width
5447              */
5448             "widthchange": true,
5449         /**
5450              * @event headerchange
5451              * Fires when the text of a header changes.
5452              * @param {ColumnModel} this
5453              * @param {Number} columnIndex The column index
5454              * @param {Number} newText The new header text
5455              */
5456             "headerchange": true,
5457         /**
5458              * @event hiddenchange
5459              * Fires when a column is hidden or "unhidden".
5460              * @param {ColumnModel} this
5461              * @param {Number} columnIndex The column index
5462              * @param {Boolean} hidden true if hidden, false otherwise
5463              */
5464             "hiddenchange": true,
5465             /**
5466          * @event columnmoved
5467          * Fires when a column is moved.
5468          * @param {ColumnModel} this
5469          * @param {Number} oldIndex
5470          * @param {Number} newIndex
5471          */
5472         "columnmoved" : true,
5473         /**
5474          * @event columlockchange
5475          * Fires when a column's locked state is changed
5476          * @param {ColumnModel} this
5477          * @param {Number} colIndex
5478          * @param {Boolean} locked true if locked
5479          */
5480         "columnlockchange" : true
5481     });
5482     Roo.grid.ColumnModel.superclass.constructor.call(this);
5483 };
5484 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5485     /**
5486      * @cfg {String} header The header text to display in the Grid view.
5487      */
5488     /**
5489      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5490      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5491      * specified, the column's index is used as an index into the Record's data Array.
5492      */
5493     /**
5494      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5495      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5496      */
5497     /**
5498      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5499      * Defaults to the value of the {@link #defaultSortable} property.
5500      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5501      */
5502     /**
5503      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5504      */
5505     /**
5506      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5507      */
5508     /**
5509      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5510      */
5511     /**
5512      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5513      */
5514     /**
5515      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5516      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5517      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5518      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5519      */
5520        /**
5521      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5522      */
5523     /**
5524      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5525      */
5526     /**
5527      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5528      */
5529     /**
5530      * @cfg {String} cursor (Optional)
5531      */
5532     /**
5533      * @cfg {String} tooltip (Optional)
5534      */
5535     /**
5536      * @cfg {Number} xs (Optional)
5537      */
5538     /**
5539      * @cfg {Number} sm (Optional)
5540      */
5541     /**
5542      * @cfg {Number} md (Optional)
5543      */
5544     /**
5545      * @cfg {Number} lg (Optional)
5546      */
5547     /**
5548      * Returns the id of the column at the specified index.
5549      * @param {Number} index The column index
5550      * @return {String} the id
5551      */
5552     getColumnId : function(index){
5553         return this.config[index].id;
5554     },
5555
5556     /**
5557      * Returns the column for a specified id.
5558      * @param {String} id The column id
5559      * @return {Object} the column
5560      */
5561     getColumnById : function(id){
5562         return this.lookup[id];
5563     },
5564
5565     
5566     /**
5567      * Returns the column for a specified dataIndex.
5568      * @param {String} dataIndex The column dataIndex
5569      * @return {Object|Boolean} the column or false if not found
5570      */
5571     getColumnByDataIndex: function(dataIndex){
5572         var index = this.findColumnIndex(dataIndex);
5573         return index > -1 ? this.config[index] : false;
5574     },
5575     
5576     /**
5577      * Returns the index for a specified column id.
5578      * @param {String} id The column id
5579      * @return {Number} the index, or -1 if not found
5580      */
5581     getIndexById : function(id){
5582         for(var i = 0, len = this.config.length; i < len; i++){
5583             if(this.config[i].id == id){
5584                 return i;
5585             }
5586         }
5587         return -1;
5588     },
5589     
5590     /**
5591      * Returns the index for a specified column dataIndex.
5592      * @param {String} dataIndex The column dataIndex
5593      * @return {Number} the index, or -1 if not found
5594      */
5595     
5596     findColumnIndex : function(dataIndex){
5597         for(var i = 0, len = this.config.length; i < len; i++){
5598             if(this.config[i].dataIndex == dataIndex){
5599                 return i;
5600             }
5601         }
5602         return -1;
5603     },
5604     
5605     
5606     moveColumn : function(oldIndex, newIndex){
5607         var c = this.config[oldIndex];
5608         this.config.splice(oldIndex, 1);
5609         this.config.splice(newIndex, 0, c);
5610         this.dataMap = null;
5611         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5612     },
5613
5614     isLocked : function(colIndex){
5615         return this.config[colIndex].locked === true;
5616     },
5617
5618     setLocked : function(colIndex, value, suppressEvent){
5619         if(this.isLocked(colIndex) == value){
5620             return;
5621         }
5622         this.config[colIndex].locked = value;
5623         if(!suppressEvent){
5624             this.fireEvent("columnlockchange", this, colIndex, value);
5625         }
5626     },
5627
5628     getTotalLockedWidth : function(){
5629         var totalWidth = 0;
5630         for(var i = 0; i < this.config.length; i++){
5631             if(this.isLocked(i) && !this.isHidden(i)){
5632                 this.totalWidth += this.getColumnWidth(i);
5633             }
5634         }
5635         return totalWidth;
5636     },
5637
5638     getLockedCount : function(){
5639         for(var i = 0, len = this.config.length; i < len; i++){
5640             if(!this.isLocked(i)){
5641                 return i;
5642             }
5643         }
5644         
5645         return this.config.length;
5646     },
5647
5648     /**
5649      * Returns the number of columns.
5650      * @return {Number}
5651      */
5652     getColumnCount : function(visibleOnly){
5653         if(visibleOnly === true){
5654             var c = 0;
5655             for(var i = 0, len = this.config.length; i < len; i++){
5656                 if(!this.isHidden(i)){
5657                     c++;
5658                 }
5659             }
5660             return c;
5661         }
5662         return this.config.length;
5663     },
5664
5665     /**
5666      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5667      * @param {Function} fn
5668      * @param {Object} scope (optional)
5669      * @return {Array} result
5670      */
5671     getColumnsBy : function(fn, scope){
5672         var r = [];
5673         for(var i = 0, len = this.config.length; i < len; i++){
5674             var c = this.config[i];
5675             if(fn.call(scope||this, c, i) === true){
5676                 r[r.length] = c;
5677             }
5678         }
5679         return r;
5680     },
5681
5682     /**
5683      * Returns true if the specified column is sortable.
5684      * @param {Number} col The column index
5685      * @return {Boolean}
5686      */
5687     isSortable : function(col){
5688         if(typeof this.config[col].sortable == "undefined"){
5689             return this.defaultSortable;
5690         }
5691         return this.config[col].sortable;
5692     },
5693
5694     /**
5695      * Returns the rendering (formatting) function defined for the column.
5696      * @param {Number} col The column index.
5697      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5698      */
5699     getRenderer : function(col){
5700         if(!this.config[col].renderer){
5701             return Roo.grid.ColumnModel.defaultRenderer;
5702         }
5703         return this.config[col].renderer;
5704     },
5705
5706     /**
5707      * Sets the rendering (formatting) function for a column.
5708      * @param {Number} col The column index
5709      * @param {Function} fn The function to use to process the cell's raw data
5710      * to return HTML markup for the grid view. The render function is called with
5711      * the following parameters:<ul>
5712      * <li>Data value.</li>
5713      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5714      * <li>css A CSS style string to apply to the table cell.</li>
5715      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5716      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5717      * <li>Row index</li>
5718      * <li>Column index</li>
5719      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5720      */
5721     setRenderer : function(col, fn){
5722         this.config[col].renderer = fn;
5723     },
5724
5725     /**
5726      * Returns the width for the specified column.
5727      * @param {Number} col The column index
5728      * @return {Number}
5729      */
5730     getColumnWidth : function(col){
5731         return this.config[col].width * 1 || this.defaultWidth;
5732     },
5733
5734     /**
5735      * Sets the width for a column.
5736      * @param {Number} col The column index
5737      * @param {Number} width The new width
5738      */
5739     setColumnWidth : function(col, width, suppressEvent){
5740         this.config[col].width = width;
5741         this.totalWidth = null;
5742         if(!suppressEvent){
5743              this.fireEvent("widthchange", this, col, width);
5744         }
5745     },
5746
5747     /**
5748      * Returns the total width of all columns.
5749      * @param {Boolean} includeHidden True to include hidden column widths
5750      * @return {Number}
5751      */
5752     getTotalWidth : function(includeHidden){
5753         if(!this.totalWidth){
5754             this.totalWidth = 0;
5755             for(var i = 0, len = this.config.length; i < len; i++){
5756                 if(includeHidden || !this.isHidden(i)){
5757                     this.totalWidth += this.getColumnWidth(i);
5758                 }
5759             }
5760         }
5761         return this.totalWidth;
5762     },
5763
5764     /**
5765      * Returns the header for the specified column.
5766      * @param {Number} col The column index
5767      * @return {String}
5768      */
5769     getColumnHeader : function(col){
5770         return this.config[col].header;
5771     },
5772
5773     /**
5774      * Sets the header for a column.
5775      * @param {Number} col The column index
5776      * @param {String} header The new header
5777      */
5778     setColumnHeader : function(col, header){
5779         this.config[col].header = header;
5780         this.fireEvent("headerchange", this, col, header);
5781     },
5782
5783     /**
5784      * Returns the tooltip for the specified column.
5785      * @param {Number} col The column index
5786      * @return {String}
5787      */
5788     getColumnTooltip : function(col){
5789             return this.config[col].tooltip;
5790     },
5791     /**
5792      * Sets the tooltip for a column.
5793      * @param {Number} col The column index
5794      * @param {String} tooltip The new tooltip
5795      */
5796     setColumnTooltip : function(col, tooltip){
5797             this.config[col].tooltip = tooltip;
5798     },
5799
5800     /**
5801      * Returns the dataIndex for the specified column.
5802      * @param {Number} col The column index
5803      * @return {Number}
5804      */
5805     getDataIndex : function(col){
5806         return this.config[col].dataIndex;
5807     },
5808
5809     /**
5810      * Sets the dataIndex for a column.
5811      * @param {Number} col The column index
5812      * @param {Number} dataIndex The new dataIndex
5813      */
5814     setDataIndex : function(col, dataIndex){
5815         this.config[col].dataIndex = dataIndex;
5816     },
5817
5818     
5819     
5820     /**
5821      * Returns true if the cell is editable.
5822      * @param {Number} colIndex The column index
5823      * @param {Number} rowIndex The row index - this is nto actually used..?
5824      * @return {Boolean}
5825      */
5826     isCellEditable : function(colIndex, rowIndex){
5827         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5828     },
5829
5830     /**
5831      * Returns the editor defined for the cell/column.
5832      * return false or null to disable editing.
5833      * @param {Number} colIndex The column index
5834      * @param {Number} rowIndex The row index
5835      * @return {Object}
5836      */
5837     getCellEditor : function(colIndex, rowIndex){
5838         return this.config[colIndex].editor;
5839     },
5840
5841     /**
5842      * Sets if a column is editable.
5843      * @param {Number} col The column index
5844      * @param {Boolean} editable True if the column is editable
5845      */
5846     setEditable : function(col, editable){
5847         this.config[col].editable = editable;
5848     },
5849
5850
5851     /**
5852      * Returns true if the column is hidden.
5853      * @param {Number} colIndex The column index
5854      * @return {Boolean}
5855      */
5856     isHidden : function(colIndex){
5857         return this.config[colIndex].hidden;
5858     },
5859
5860
5861     /**
5862      * Returns true if the column width cannot be changed
5863      */
5864     isFixed : function(colIndex){
5865         return this.config[colIndex].fixed;
5866     },
5867
5868     /**
5869      * Returns true if the column can be resized
5870      * @return {Boolean}
5871      */
5872     isResizable : function(colIndex){
5873         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5874     },
5875     /**
5876      * Sets if a column is hidden.
5877      * @param {Number} colIndex The column index
5878      * @param {Boolean} hidden True if the column is hidden
5879      */
5880     setHidden : function(colIndex, hidden){
5881         this.config[colIndex].hidden = hidden;
5882         this.totalWidth = null;
5883         this.fireEvent("hiddenchange", this, colIndex, hidden);
5884     },
5885
5886     /**
5887      * Sets the editor for a column.
5888      * @param {Number} col The column index
5889      * @param {Object} editor The editor object
5890      */
5891     setEditor : function(col, editor){
5892         this.config[col].editor = editor;
5893     }
5894 });
5895
5896 Roo.grid.ColumnModel.defaultRenderer = function(value)
5897 {
5898     if(typeof value == "object") {
5899         return value;
5900     }
5901         if(typeof value == "string" && value.length < 1){
5902             return "&#160;";
5903         }
5904     
5905         return String.format("{0}", value);
5906 };
5907
5908 // Alias for backwards compatibility
5909 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5910 /*
5911  * Based on:
5912  * Ext JS Library 1.1.1
5913  * Copyright(c) 2006-2007, Ext JS, LLC.
5914  *
5915  * Originally Released Under LGPL - original licence link has changed is not relivant.
5916  *
5917  * Fork - LGPL
5918  * <script type="text/javascript">
5919  */
5920  
5921 /**
5922  * @class Roo.LoadMask
5923  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5924  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5925  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5926  * element's UpdateManager load indicator and will be destroyed after the initial load.
5927  * @constructor
5928  * Create a new LoadMask
5929  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5930  * @param {Object} config The config object
5931  */
5932 Roo.LoadMask = function(el, config){
5933     this.el = Roo.get(el);
5934     Roo.apply(this, config);
5935     if(this.store){
5936         this.store.on('beforeload', this.onBeforeLoad, this);
5937         this.store.on('load', this.onLoad, this);
5938         this.store.on('loadexception', this.onLoadException, this);
5939         this.removeMask = false;
5940     }else{
5941         var um = this.el.getUpdateManager();
5942         um.showLoadIndicator = false; // disable the default indicator
5943         um.on('beforeupdate', this.onBeforeLoad, this);
5944         um.on('update', this.onLoad, this);
5945         um.on('failure', this.onLoad, this);
5946         this.removeMask = true;
5947     }
5948 };
5949
5950 Roo.LoadMask.prototype = {
5951     /**
5952      * @cfg {Boolean} removeMask
5953      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5954      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5955      */
5956     /**
5957      * @cfg {String} msg
5958      * The text to display in a centered loading message box (defaults to 'Loading...')
5959      */
5960     msg : 'Loading...',
5961     /**
5962      * @cfg {String} msgCls
5963      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5964      */
5965     msgCls : 'x-mask-loading',
5966
5967     /**
5968      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5969      * @type Boolean
5970      */
5971     disabled: false,
5972
5973     /**
5974      * Disables the mask to prevent it from being displayed
5975      */
5976     disable : function(){
5977        this.disabled = true;
5978     },
5979
5980     /**
5981      * Enables the mask so that it can be displayed
5982      */
5983     enable : function(){
5984         this.disabled = false;
5985     },
5986     
5987     onLoadException : function()
5988     {
5989         Roo.log(arguments);
5990         
5991         if (typeof(arguments[3]) != 'undefined') {
5992             Roo.MessageBox.alert("Error loading",arguments[3]);
5993         } 
5994         /*
5995         try {
5996             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5997                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5998             }   
5999         } catch(e) {
6000             
6001         }
6002         */
6003     
6004         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6005     },
6006     // private
6007     onLoad : function()
6008     {
6009         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6010     },
6011
6012     // private
6013     onBeforeLoad : function(){
6014         if(!this.disabled){
6015             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6016         }
6017     },
6018
6019     // private
6020     destroy : function(){
6021         if(this.store){
6022             this.store.un('beforeload', this.onBeforeLoad, this);
6023             this.store.un('load', this.onLoad, this);
6024             this.store.un('loadexception', this.onLoadException, this);
6025         }else{
6026             var um = this.el.getUpdateManager();
6027             um.un('beforeupdate', this.onBeforeLoad, this);
6028             um.un('update', this.onLoad, this);
6029             um.un('failure', this.onLoad, this);
6030         }
6031     }
6032 };/*
6033  * - LGPL
6034  *
6035  * table
6036  * 
6037  */
6038
6039 /**
6040  * @class Roo.bootstrap.Table
6041  * @extends Roo.bootstrap.Component
6042  * Bootstrap Table class
6043  * @cfg {String} cls table class
6044  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6045  * @cfg {String} bgcolor Specifies the background color for a table
6046  * @cfg {Number} border Specifies whether the table cells should have borders or not
6047  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6048  * @cfg {Number} cellspacing Specifies the space between cells
6049  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6050  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6051  * @cfg {String} sortable Specifies that the table should be sortable
6052  * @cfg {String} summary Specifies a summary of the content of a table
6053  * @cfg {Number} width Specifies the width of a table
6054  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6055  * 
6056  * @cfg {boolean} striped Should the rows be alternative striped
6057  * @cfg {boolean} bordered Add borders to the table
6058  * @cfg {boolean} hover Add hover highlighting
6059  * @cfg {boolean} condensed Format condensed
6060  * @cfg {boolean} responsive Format condensed
6061  * @cfg {Boolean} loadMask (true|false) default false
6062  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6063  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6064  * @cfg {Boolean} rowSelection (true|false) default false
6065  * @cfg {Boolean} cellSelection (true|false) default false
6066  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6067  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6068  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6069  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6070  
6071  * 
6072  * @constructor
6073  * Create a new Table
6074  * @param {Object} config The config object
6075  */
6076
6077 Roo.bootstrap.Table = function(config){
6078     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6079     
6080   
6081     
6082     // BC...
6083     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6084     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6085     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6086     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6087     
6088     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6089     if (this.sm) {
6090         this.sm.grid = this;
6091         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6092         this.sm = this.selModel;
6093         this.sm.xmodule = this.xmodule || false;
6094     }
6095     
6096     if (this.cm && typeof(this.cm.config) == 'undefined') {
6097         this.colModel = new Roo.grid.ColumnModel(this.cm);
6098         this.cm = this.colModel;
6099         this.cm.xmodule = this.xmodule || false;
6100     }
6101     if (this.store) {
6102         this.store= Roo.factory(this.store, Roo.data);
6103         this.ds = this.store;
6104         this.ds.xmodule = this.xmodule || false;
6105          
6106     }
6107     if (this.footer && this.store) {
6108         this.footer.dataSource = this.ds;
6109         this.footer = Roo.factory(this.footer);
6110     }
6111     
6112     /** @private */
6113     this.addEvents({
6114         /**
6115          * @event cellclick
6116          * Fires when a cell is clicked
6117          * @param {Roo.bootstrap.Table} this
6118          * @param {Roo.Element} el
6119          * @param {Number} rowIndex
6120          * @param {Number} columnIndex
6121          * @param {Roo.EventObject} e
6122          */
6123         "cellclick" : true,
6124         /**
6125          * @event celldblclick
6126          * Fires when a cell is double clicked
6127          * @param {Roo.bootstrap.Table} this
6128          * @param {Roo.Element} el
6129          * @param {Number} rowIndex
6130          * @param {Number} columnIndex
6131          * @param {Roo.EventObject} e
6132          */
6133         "celldblclick" : true,
6134         /**
6135          * @event rowclick
6136          * Fires when a row is clicked
6137          * @param {Roo.bootstrap.Table} this
6138          * @param {Roo.Element} el
6139          * @param {Number} rowIndex
6140          * @param {Roo.EventObject} e
6141          */
6142         "rowclick" : true,
6143         /**
6144          * @event rowdblclick
6145          * Fires when a row is double clicked
6146          * @param {Roo.bootstrap.Table} this
6147          * @param {Roo.Element} el
6148          * @param {Number} rowIndex
6149          * @param {Roo.EventObject} e
6150          */
6151         "rowdblclick" : true,
6152         /**
6153          * @event mouseover
6154          * Fires when a mouseover occur
6155          * @param {Roo.bootstrap.Table} this
6156          * @param {Roo.Element} el
6157          * @param {Number} rowIndex
6158          * @param {Number} columnIndex
6159          * @param {Roo.EventObject} e
6160          */
6161         "mouseover" : true,
6162         /**
6163          * @event mouseout
6164          * Fires when a mouseout occur
6165          * @param {Roo.bootstrap.Table} this
6166          * @param {Roo.Element} el
6167          * @param {Number} rowIndex
6168          * @param {Number} columnIndex
6169          * @param {Roo.EventObject} e
6170          */
6171         "mouseout" : true,
6172         /**
6173          * @event rowclass
6174          * Fires when a row is rendered, so you can change add a style to it.
6175          * @param {Roo.bootstrap.Table} this
6176          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6177          */
6178         'rowclass' : true,
6179           /**
6180          * @event rowsrendered
6181          * Fires when all the  rows have been rendered
6182          * @param {Roo.bootstrap.Table} this
6183          */
6184         'rowsrendered' : true,
6185         /**
6186          * @event contextmenu
6187          * The raw contextmenu event for the entire grid.
6188          * @param {Roo.EventObject} e
6189          */
6190         "contextmenu" : true,
6191         /**
6192          * @event rowcontextmenu
6193          * Fires when a row is right clicked
6194          * @param {Roo.bootstrap.Table} this
6195          * @param {Number} rowIndex
6196          * @param {Roo.EventObject} e
6197          */
6198         "rowcontextmenu" : true,
6199         /**
6200          * @event cellcontextmenu
6201          * Fires when a cell is right clicked
6202          * @param {Roo.bootstrap.Table} this
6203          * @param {Number} rowIndex
6204          * @param {Number} cellIndex
6205          * @param {Roo.EventObject} e
6206          */
6207          "cellcontextmenu" : true,
6208          /**
6209          * @event headercontextmenu
6210          * Fires when a header is right clicked
6211          * @param {Roo.bootstrap.Table} this
6212          * @param {Number} columnIndex
6213          * @param {Roo.EventObject} e
6214          */
6215         "headercontextmenu" : true
6216     });
6217 };
6218
6219 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6220     
6221     cls: false,
6222     align: false,
6223     bgcolor: false,
6224     border: false,
6225     cellpadding: false,
6226     cellspacing: false,
6227     frame: false,
6228     rules: false,
6229     sortable: false,
6230     summary: false,
6231     width: false,
6232     striped : false,
6233     scrollBody : false,
6234     bordered: false,
6235     hover:  false,
6236     condensed : false,
6237     responsive : false,
6238     sm : false,
6239     cm : false,
6240     store : false,
6241     loadMask : false,
6242     footerShow : true,
6243     headerShow : true,
6244   
6245     rowSelection : false,
6246     cellSelection : false,
6247     layout : false,
6248     
6249     // Roo.Element - the tbody
6250     mainBody: false,
6251     // Roo.Element - thead element
6252     mainHead: false,
6253     
6254     container: false, // used by gridpanel...
6255     
6256     lazyLoad : false,
6257     
6258     CSS : Roo.util.CSS,
6259     
6260     auto_hide_footer : false,
6261     
6262     getAutoCreate : function()
6263     {
6264         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6265         
6266         cfg = {
6267             tag: 'table',
6268             cls : 'table',
6269             cn : []
6270         };
6271         if (this.scrollBody) {
6272             cfg.cls += ' table-body-fixed';
6273         }    
6274         if (this.striped) {
6275             cfg.cls += ' table-striped';
6276         }
6277         
6278         if (this.hover) {
6279             cfg.cls += ' table-hover';
6280         }
6281         if (this.bordered) {
6282             cfg.cls += ' table-bordered';
6283         }
6284         if (this.condensed) {
6285             cfg.cls += ' table-condensed';
6286         }
6287         if (this.responsive) {
6288             cfg.cls += ' table-responsive';
6289         }
6290         
6291         if (this.cls) {
6292             cfg.cls+=  ' ' +this.cls;
6293         }
6294         
6295         // this lot should be simplifed...
6296         var _t = this;
6297         var cp = [
6298             'align',
6299             'bgcolor',
6300             'border',
6301             'cellpadding',
6302             'cellspacing',
6303             'frame',
6304             'rules',
6305             'sortable',
6306             'summary',
6307             'width'
6308         ].forEach(function(k) {
6309             if (_t[k]) {
6310                 cfg[k] = _t[k];
6311             }
6312         });
6313         
6314         
6315         if (this.layout) {
6316             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6317         }
6318         
6319         if(this.store || this.cm){
6320             if(this.headerShow){
6321                 cfg.cn.push(this.renderHeader());
6322             }
6323             
6324             cfg.cn.push(this.renderBody());
6325             
6326             if(this.footerShow){
6327                 cfg.cn.push(this.renderFooter());
6328             }
6329             // where does this come from?
6330             //cfg.cls+=  ' TableGrid';
6331         }
6332         
6333         return { cn : [ cfg ] };
6334     },
6335     
6336     initEvents : function()
6337     {   
6338         if(!this.store || !this.cm){
6339             return;
6340         }
6341         if (this.selModel) {
6342             this.selModel.initEvents();
6343         }
6344         
6345         
6346         //Roo.log('initEvents with ds!!!!');
6347         
6348         this.mainBody = this.el.select('tbody', true).first();
6349         this.mainHead = this.el.select('thead', true).first();
6350         this.mainFoot = this.el.select('tfoot', true).first();
6351         
6352         
6353         
6354         var _this = this;
6355         
6356         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6357             e.on('click', _this.sort, _this);
6358         });
6359         
6360         this.mainBody.on("click", this.onClick, this);
6361         this.mainBody.on("dblclick", this.onDblClick, this);
6362         
6363         // why is this done????? = it breaks dialogs??
6364         //this.parent().el.setStyle('position', 'relative');
6365         
6366         
6367         if (this.footer) {
6368             this.footer.parentId = this.id;
6369             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6370             
6371             if(this.lazyLoad){
6372                 this.el.select('tfoot tr td').first().addClass('hide');
6373             }
6374         } 
6375         
6376         if(this.loadMask) {
6377             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6378         }
6379         
6380         this.store.on('load', this.onLoad, this);
6381         this.store.on('beforeload', this.onBeforeLoad, this);
6382         this.store.on('update', this.onUpdate, this);
6383         this.store.on('add', this.onAdd, this);
6384         this.store.on("clear", this.clear, this);
6385         
6386         this.el.on("contextmenu", this.onContextMenu, this);
6387         
6388         this.mainBody.on('scroll', this.onBodyScroll, this);
6389         
6390         this.cm.on("headerchange", this.onHeaderChange, this);
6391         
6392         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6393         
6394     },
6395     
6396     onContextMenu : function(e, t)
6397     {
6398         this.processEvent("contextmenu", e);
6399     },
6400     
6401     processEvent : function(name, e)
6402     {
6403         if (name != 'touchstart' ) {
6404             this.fireEvent(name, e);    
6405         }
6406         
6407         var t = e.getTarget();
6408         
6409         var cell = Roo.get(t);
6410         
6411         if(!cell){
6412             return;
6413         }
6414         
6415         if(cell.findParent('tfoot', false, true)){
6416             return;
6417         }
6418         
6419         if(cell.findParent('thead', false, true)){
6420             
6421             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6422                 cell = Roo.get(t).findParent('th', false, true);
6423                 if (!cell) {
6424                     Roo.log("failed to find th in thead?");
6425                     Roo.log(e.getTarget());
6426                     return;
6427                 }
6428             }
6429             
6430             var cellIndex = cell.dom.cellIndex;
6431             
6432             var ename = name == 'touchstart' ? 'click' : name;
6433             this.fireEvent("header" + ename, this, cellIndex, e);
6434             
6435             return;
6436         }
6437         
6438         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6439             cell = Roo.get(t).findParent('td', false, true);
6440             if (!cell) {
6441                 Roo.log("failed to find th in tbody?");
6442                 Roo.log(e.getTarget());
6443                 return;
6444             }
6445         }
6446         
6447         var row = cell.findParent('tr', false, true);
6448         var cellIndex = cell.dom.cellIndex;
6449         var rowIndex = row.dom.rowIndex - 1;
6450         
6451         if(row !== false){
6452             
6453             this.fireEvent("row" + name, this, rowIndex, e);
6454             
6455             if(cell !== false){
6456             
6457                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6458             }
6459         }
6460         
6461     },
6462     
6463     onMouseover : function(e, el)
6464     {
6465         var cell = Roo.get(el);
6466         
6467         if(!cell){
6468             return;
6469         }
6470         
6471         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6472             cell = cell.findParent('td', false, true);
6473         }
6474         
6475         var row = cell.findParent('tr', false, true);
6476         var cellIndex = cell.dom.cellIndex;
6477         var rowIndex = row.dom.rowIndex - 1; // start from 0
6478         
6479         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6480         
6481     },
6482     
6483     onMouseout : function(e, el)
6484     {
6485         var cell = Roo.get(el);
6486         
6487         if(!cell){
6488             return;
6489         }
6490         
6491         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6492             cell = cell.findParent('td', false, true);
6493         }
6494         
6495         var row = cell.findParent('tr', false, true);
6496         var cellIndex = cell.dom.cellIndex;
6497         var rowIndex = row.dom.rowIndex - 1; // start from 0
6498         
6499         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6500         
6501     },
6502     
6503     onClick : function(e, el)
6504     {
6505         var cell = Roo.get(el);
6506         
6507         if(!cell || (!this.cellSelection && !this.rowSelection)){
6508             return;
6509         }
6510         
6511         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6512             cell = cell.findParent('td', false, true);
6513         }
6514         
6515         if(!cell || typeof(cell) == 'undefined'){
6516             return;
6517         }
6518         
6519         var row = cell.findParent('tr', false, true);
6520         
6521         if(!row || typeof(row) == 'undefined'){
6522             return;
6523         }
6524         
6525         var cellIndex = cell.dom.cellIndex;
6526         var rowIndex = this.getRowIndex(row);
6527         
6528         // why??? - should these not be based on SelectionModel?
6529         if(this.cellSelection){
6530             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6531         }
6532         
6533         if(this.rowSelection){
6534             this.fireEvent('rowclick', this, row, rowIndex, e);
6535         }
6536         
6537         
6538     },
6539         
6540     onDblClick : function(e,el)
6541     {
6542         var cell = Roo.get(el);
6543         
6544         if(!cell || (!this.cellSelection && !this.rowSelection)){
6545             return;
6546         }
6547         
6548         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6549             cell = cell.findParent('td', false, true);
6550         }
6551         
6552         if(!cell || typeof(cell) == 'undefined'){
6553             return;
6554         }
6555         
6556         var row = cell.findParent('tr', false, true);
6557         
6558         if(!row || typeof(row) == 'undefined'){
6559             return;
6560         }
6561         
6562         var cellIndex = cell.dom.cellIndex;
6563         var rowIndex = this.getRowIndex(row);
6564         
6565         if(this.cellSelection){
6566             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6567         }
6568         
6569         if(this.rowSelection){
6570             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6571         }
6572     },
6573     
6574     sort : function(e,el)
6575     {
6576         var col = Roo.get(el);
6577         
6578         if(!col.hasClass('sortable')){
6579             return;
6580         }
6581         
6582         var sort = col.attr('sort');
6583         var dir = 'ASC';
6584         
6585         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6586             dir = 'DESC';
6587         }
6588         
6589         this.store.sortInfo = {field : sort, direction : dir};
6590         
6591         if (this.footer) {
6592             Roo.log("calling footer first");
6593             this.footer.onClick('first');
6594         } else {
6595         
6596             this.store.load({ params : { start : 0 } });
6597         }
6598     },
6599     
6600     renderHeader : function()
6601     {
6602         var header = {
6603             tag: 'thead',
6604             cn : []
6605         };
6606         
6607         var cm = this.cm;
6608         this.totalWidth = 0;
6609         
6610         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6611             
6612             var config = cm.config[i];
6613             
6614             var c = {
6615                 tag: 'th',
6616                 cls : 'x-hcol-' + i,
6617                 style : '',
6618                 html: cm.getColumnHeader(i)
6619             };
6620             
6621             var hh = '';
6622             
6623             if(typeof(config.sortable) != 'undefined' && config.sortable){
6624                 c.cls = 'sortable';
6625                 c.html = '<i class="glyphicon"></i>' + c.html;
6626             }
6627             
6628             if(typeof(config.lgHeader) != 'undefined'){
6629                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6630             }
6631             
6632             if(typeof(config.mdHeader) != 'undefined'){
6633                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6634             }
6635             
6636             if(typeof(config.smHeader) != 'undefined'){
6637                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6638             }
6639             
6640             if(typeof(config.xsHeader) != 'undefined'){
6641                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6642             }
6643             
6644             if(hh.length){
6645                 c.html = hh;
6646             }
6647             
6648             if(typeof(config.tooltip) != 'undefined'){
6649                 c.tooltip = config.tooltip;
6650             }
6651             
6652             if(typeof(config.colspan) != 'undefined'){
6653                 c.colspan = config.colspan;
6654             }
6655             
6656             if(typeof(config.hidden) != 'undefined' && config.hidden){
6657                 c.style += ' display:none;';
6658             }
6659             
6660             if(typeof(config.dataIndex) != 'undefined'){
6661                 c.sort = config.dataIndex;
6662             }
6663             
6664            
6665             
6666             if(typeof(config.align) != 'undefined' && config.align.length){
6667                 c.style += ' text-align:' + config.align + ';';
6668             }
6669             
6670             if(typeof(config.width) != 'undefined'){
6671                 c.style += ' width:' + config.width + 'px;';
6672                 this.totalWidth += config.width;
6673             } else {
6674                 this.totalWidth += 100; // assume minimum of 100 per column?
6675             }
6676             
6677             if(typeof(config.cls) != 'undefined'){
6678                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6679             }
6680             
6681             ['xs','sm','md','lg'].map(function(size){
6682                 
6683                 if(typeof(config[size]) == 'undefined'){
6684                     return;
6685                 }
6686                 
6687                 if (!config[size]) { // 0 = hidden
6688                     c.cls += ' hidden-' + size;
6689                     return;
6690                 }
6691                 
6692                 c.cls += ' col-' + size + '-' + config[size];
6693
6694             });
6695             
6696             header.cn.push(c)
6697         }
6698         
6699         return header;
6700     },
6701     
6702     renderBody : function()
6703     {
6704         var body = {
6705             tag: 'tbody',
6706             cn : [
6707                 {
6708                     tag: 'tr',
6709                     cn : [
6710                         {
6711                             tag : 'td',
6712                             colspan :  this.cm.getColumnCount()
6713                         }
6714                     ]
6715                 }
6716             ]
6717         };
6718         
6719         return body;
6720     },
6721     
6722     renderFooter : function()
6723     {
6724         var footer = {
6725             tag: 'tfoot',
6726             cn : [
6727                 {
6728                     tag: 'tr',
6729                     cn : [
6730                         {
6731                             tag : 'td',
6732                             colspan :  this.cm.getColumnCount()
6733                         }
6734                     ]
6735                 }
6736             ]
6737         };
6738         
6739         return footer;
6740     },
6741     
6742     
6743     
6744     onLoad : function()
6745     {
6746 //        Roo.log('ds onload');
6747         this.clear();
6748         
6749         var _this = this;
6750         var cm = this.cm;
6751         var ds = this.store;
6752         
6753         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6754             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6755             if (_this.store.sortInfo) {
6756                     
6757                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6758                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6759                 }
6760                 
6761                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6762                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6763                 }
6764             }
6765         });
6766         
6767         var tbody =  this.mainBody;
6768               
6769         if(ds.getCount() > 0){
6770             ds.data.each(function(d,rowIndex){
6771                 var row =  this.renderRow(cm, ds, rowIndex);
6772                 
6773                 tbody.createChild(row);
6774                 
6775                 var _this = this;
6776                 
6777                 if(row.cellObjects.length){
6778                     Roo.each(row.cellObjects, function(r){
6779                         _this.renderCellObject(r);
6780                     })
6781                 }
6782                 
6783             }, this);
6784         }
6785         
6786         var tfoot = this.el.select('tfoot', true).first();
6787         
6788         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6789             
6790             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6791             
6792             var total = this.ds.getTotalCount();
6793             
6794             if(this.footer.pageSize < total){
6795                 this.mainFoot.show();
6796             }
6797         }
6798         
6799         Roo.each(this.el.select('tbody td', true).elements, function(e){
6800             e.on('mouseover', _this.onMouseover, _this);
6801         });
6802         
6803         Roo.each(this.el.select('tbody td', true).elements, function(e){
6804             e.on('mouseout', _this.onMouseout, _this);
6805         });
6806         this.fireEvent('rowsrendered', this);
6807         
6808         this.autoSize();
6809     },
6810     
6811     
6812     onUpdate : function(ds,record)
6813     {
6814         this.refreshRow(record);
6815         this.autoSize();
6816     },
6817     
6818     onRemove : function(ds, record, index, isUpdate){
6819         if(isUpdate !== true){
6820             this.fireEvent("beforerowremoved", this, index, record);
6821         }
6822         var bt = this.mainBody.dom;
6823         
6824         var rows = this.el.select('tbody > tr', true).elements;
6825         
6826         if(typeof(rows[index]) != 'undefined'){
6827             bt.removeChild(rows[index].dom);
6828         }
6829         
6830 //        if(bt.rows[index]){
6831 //            bt.removeChild(bt.rows[index]);
6832 //        }
6833         
6834         if(isUpdate !== true){
6835             //this.stripeRows(index);
6836             //this.syncRowHeights(index, index);
6837             //this.layout();
6838             this.fireEvent("rowremoved", this, index, record);
6839         }
6840     },
6841     
6842     onAdd : function(ds, records, rowIndex)
6843     {
6844         //Roo.log('on Add called');
6845         // - note this does not handle multiple adding very well..
6846         var bt = this.mainBody.dom;
6847         for (var i =0 ; i < records.length;i++) {
6848             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6849             //Roo.log(records[i]);
6850             //Roo.log(this.store.getAt(rowIndex+i));
6851             this.insertRow(this.store, rowIndex + i, false);
6852             return;
6853         }
6854         
6855     },
6856     
6857     
6858     refreshRow : function(record){
6859         var ds = this.store, index;
6860         if(typeof record == 'number'){
6861             index = record;
6862             record = ds.getAt(index);
6863         }else{
6864             index = ds.indexOf(record);
6865         }
6866         this.insertRow(ds, index, true);
6867         this.autoSize();
6868         this.onRemove(ds, record, index+1, true);
6869         this.autoSize();
6870         //this.syncRowHeights(index, index);
6871         //this.layout();
6872         this.fireEvent("rowupdated", this, index, record);
6873     },
6874     
6875     insertRow : function(dm, rowIndex, isUpdate){
6876         
6877         if(!isUpdate){
6878             this.fireEvent("beforerowsinserted", this, rowIndex);
6879         }
6880             //var s = this.getScrollState();
6881         var row = this.renderRow(this.cm, this.store, rowIndex);
6882         // insert before rowIndex..
6883         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6884         
6885         var _this = this;
6886                 
6887         if(row.cellObjects.length){
6888             Roo.each(row.cellObjects, function(r){
6889                 _this.renderCellObject(r);
6890             })
6891         }
6892             
6893         if(!isUpdate){
6894             this.fireEvent("rowsinserted", this, rowIndex);
6895             //this.syncRowHeights(firstRow, lastRow);
6896             //this.stripeRows(firstRow);
6897             //this.layout();
6898         }
6899         
6900     },
6901     
6902     
6903     getRowDom : function(rowIndex)
6904     {
6905         var rows = this.el.select('tbody > tr', true).elements;
6906         
6907         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6908         
6909     },
6910     // returns the object tree for a tr..
6911   
6912     
6913     renderRow : function(cm, ds, rowIndex) 
6914     {
6915         var d = ds.getAt(rowIndex);
6916         
6917         var row = {
6918             tag : 'tr',
6919             cls : 'x-row-' + rowIndex,
6920             cn : []
6921         };
6922             
6923         var cellObjects = [];
6924         
6925         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6926             var config = cm.config[i];
6927             
6928             var renderer = cm.getRenderer(i);
6929             var value = '';
6930             var id = false;
6931             
6932             if(typeof(renderer) !== 'undefined'){
6933                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6934             }
6935             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6936             // and are rendered into the cells after the row is rendered - using the id for the element.
6937             
6938             if(typeof(value) === 'object'){
6939                 id = Roo.id();
6940                 cellObjects.push({
6941                     container : id,
6942                     cfg : value 
6943                 })
6944             }
6945             
6946             var rowcfg = {
6947                 record: d,
6948                 rowIndex : rowIndex,
6949                 colIndex : i,
6950                 rowClass : ''
6951             };
6952
6953             this.fireEvent('rowclass', this, rowcfg);
6954             
6955             var td = {
6956                 tag: 'td',
6957                 cls : rowcfg.rowClass + ' x-col-' + i,
6958                 style: '',
6959                 html: (typeof(value) === 'object') ? '' : value
6960             };
6961             
6962             if (id) {
6963                 td.id = id;
6964             }
6965             
6966             if(typeof(config.colspan) != 'undefined'){
6967                 td.colspan = config.colspan;
6968             }
6969             
6970             if(typeof(config.hidden) != 'undefined' && config.hidden){
6971                 td.style += ' display:none;';
6972             }
6973             
6974             if(typeof(config.align) != 'undefined' && config.align.length){
6975                 td.style += ' text-align:' + config.align + ';';
6976             }
6977             if(typeof(config.valign) != 'undefined' && config.valign.length){
6978                 td.style += ' vertical-align:' + config.valign + ';';
6979             }
6980             
6981             if(typeof(config.width) != 'undefined'){
6982                 td.style += ' width:' +  config.width + 'px;';
6983             }
6984             
6985             if(typeof(config.cursor) != 'undefined'){
6986                 td.style += ' cursor:' +  config.cursor + ';';
6987             }
6988             
6989             if(typeof(config.cls) != 'undefined'){
6990                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6991             }
6992             
6993             ['xs','sm','md','lg'].map(function(size){
6994                 
6995                 if(typeof(config[size]) == 'undefined'){
6996                     return;
6997                 }
6998                 
6999                 if (!config[size]) { // 0 = hidden
7000                     td.cls += ' hidden-' + size;
7001                     return;
7002                 }
7003                 
7004                 td.cls += ' col-' + size + '-' + config[size];
7005
7006             });
7007             
7008             row.cn.push(td);
7009            
7010         }
7011         
7012         row.cellObjects = cellObjects;
7013         
7014         return row;
7015           
7016     },
7017     
7018     
7019     
7020     onBeforeLoad : function()
7021     {
7022         
7023     },
7024      /**
7025      * Remove all rows
7026      */
7027     clear : function()
7028     {
7029         this.el.select('tbody', true).first().dom.innerHTML = '';
7030     },
7031     /**
7032      * Show or hide a row.
7033      * @param {Number} rowIndex to show or hide
7034      * @param {Boolean} state hide
7035      */
7036     setRowVisibility : function(rowIndex, state)
7037     {
7038         var bt = this.mainBody.dom;
7039         
7040         var rows = this.el.select('tbody > tr', true).elements;
7041         
7042         if(typeof(rows[rowIndex]) == 'undefined'){
7043             return;
7044         }
7045         rows[rowIndex].dom.style.display = state ? '' : 'none';
7046     },
7047     
7048     
7049     getSelectionModel : function(){
7050         if(!this.selModel){
7051             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7052         }
7053         return this.selModel;
7054     },
7055     /*
7056      * Render the Roo.bootstrap object from renderder
7057      */
7058     renderCellObject : function(r)
7059     {
7060         var _this = this;
7061         
7062         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7063         
7064         var t = r.cfg.render(r.container);
7065         
7066         if(r.cfg.cn){
7067             Roo.each(r.cfg.cn, function(c){
7068                 var child = {
7069                     container: t.getChildContainer(),
7070                     cfg: c
7071                 };
7072                 _this.renderCellObject(child);
7073             })
7074         }
7075     },
7076     
7077     getRowIndex : function(row)
7078     {
7079         var rowIndex = -1;
7080         
7081         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7082             if(el != row){
7083                 return;
7084             }
7085             
7086             rowIndex = index;
7087         });
7088         
7089         return rowIndex;
7090     },
7091      /**
7092      * Returns the grid's underlying element = used by panel.Grid
7093      * @return {Element} The element
7094      */
7095     getGridEl : function(){
7096         return this.el;
7097     },
7098      /**
7099      * Forces a resize - used by panel.Grid
7100      * @return {Element} The element
7101      */
7102     autoSize : function()
7103     {
7104         //var ctr = Roo.get(this.container.dom.parentElement);
7105         var ctr = Roo.get(this.el.dom);
7106         
7107         var thd = this.getGridEl().select('thead',true).first();
7108         var tbd = this.getGridEl().select('tbody', true).first();
7109         var tfd = this.getGridEl().select('tfoot', true).first();
7110         
7111         var cw = ctr.getWidth();
7112         
7113         if (tbd) {
7114             
7115             tbd.setSize(ctr.getWidth(),
7116                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7117             );
7118             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7119             cw -= barsize;
7120         }
7121         cw = Math.max(cw, this.totalWidth);
7122         this.getGridEl().select('tr',true).setWidth(cw);
7123         // resize 'expandable coloumn?
7124         
7125         return; // we doe not have a view in this design..
7126         
7127     },
7128     onBodyScroll: function()
7129     {
7130         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7131         if(this.mainHead){
7132             this.mainHead.setStyle({
7133                 'position' : 'relative',
7134                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7135             });
7136         }
7137         
7138         if(this.lazyLoad){
7139             
7140             var scrollHeight = this.mainBody.dom.scrollHeight;
7141             
7142             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7143             
7144             var height = this.mainBody.getHeight();
7145             
7146             if(scrollHeight - height == scrollTop) {
7147                 
7148                 var total = this.ds.getTotalCount();
7149                 
7150                 if(this.footer.cursor + this.footer.pageSize < total){
7151                     
7152                     this.footer.ds.load({
7153                         params : {
7154                             start : this.footer.cursor + this.footer.pageSize,
7155                             limit : this.footer.pageSize
7156                         },
7157                         add : true
7158                     });
7159                 }
7160             }
7161             
7162         }
7163     },
7164     
7165     onHeaderChange : function()
7166     {
7167         var header = this.renderHeader();
7168         var table = this.el.select('table', true).first();
7169         
7170         this.mainHead.remove();
7171         this.mainHead = table.createChild(header, this.mainBody, false);
7172     },
7173     
7174     onHiddenChange : function(colModel, colIndex, hidden)
7175     {
7176         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7177         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7178         
7179         this.CSS.updateRule(thSelector, "display", "");
7180         this.CSS.updateRule(tdSelector, "display", "");
7181         
7182         if(hidden){
7183             this.CSS.updateRule(thSelector, "display", "none");
7184             this.CSS.updateRule(tdSelector, "display", "none");
7185         }
7186         
7187         this.onHeaderChange();
7188         this.onLoad();
7189     },
7190     
7191     setColumnWidth: function(col_index, width)
7192     {
7193         // width = "md-2 xs-2..."
7194         if(!this.colModel.config[col_index]) {
7195             return;
7196         }
7197         
7198         var w = width.split(" ");
7199         
7200         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7201         
7202         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7203         
7204         
7205         for(var j = 0; j < w.length; j++) {
7206             
7207             if(!w[j]) {
7208                 continue;
7209             }
7210             
7211             var size_cls = w[j].split("-");
7212             
7213             if(!Number.isInteger(size_cls[1] * 1)) {
7214                 continue;
7215             }
7216             
7217             if(!this.colModel.config[col_index][size_cls[0]]) {
7218                 continue;
7219             }
7220             
7221             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7222                 continue;
7223             }
7224             
7225             h_row[0].classList.replace(
7226                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7227                 "col-"+size_cls[0]+"-"+size_cls[1]
7228             );
7229             
7230             for(var i = 0; i < rows.length; i++) {
7231                 
7232                 var size_cls = w[j].split("-");
7233                 
7234                 if(!Number.isInteger(size_cls[1] * 1)) {
7235                     continue;
7236                 }
7237                 
7238                 if(!this.colModel.config[col_index][size_cls[0]]) {
7239                     continue;
7240                 }
7241                 
7242                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7243                     continue;
7244                 }
7245                 
7246                 rows[i].classList.replace(
7247                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7248                     "col-"+size_cls[0]+"-"+size_cls[1]
7249                 );
7250             }
7251             
7252             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7253         }
7254     }
7255 });
7256
7257  
7258
7259  /*
7260  * - LGPL
7261  *
7262  * table cell
7263  * 
7264  */
7265
7266 /**
7267  * @class Roo.bootstrap.TableCell
7268  * @extends Roo.bootstrap.Component
7269  * Bootstrap TableCell class
7270  * @cfg {String} html cell contain text
7271  * @cfg {String} cls cell class
7272  * @cfg {String} tag cell tag (td|th) default td
7273  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7274  * @cfg {String} align Aligns the content in a cell
7275  * @cfg {String} axis Categorizes cells
7276  * @cfg {String} bgcolor Specifies the background color of a cell
7277  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7278  * @cfg {Number} colspan Specifies the number of columns a cell should span
7279  * @cfg {String} headers Specifies one or more header cells a cell is related to
7280  * @cfg {Number} height Sets the height of a cell
7281  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7282  * @cfg {Number} rowspan Sets the number of rows a cell should span
7283  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7284  * @cfg {String} valign Vertical aligns the content in a cell
7285  * @cfg {Number} width Specifies the width of a cell
7286  * 
7287  * @constructor
7288  * Create a new TableCell
7289  * @param {Object} config The config object
7290  */
7291
7292 Roo.bootstrap.TableCell = function(config){
7293     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7294 };
7295
7296 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7297     
7298     html: false,
7299     cls: false,
7300     tag: false,
7301     abbr: false,
7302     align: false,
7303     axis: false,
7304     bgcolor: false,
7305     charoff: false,
7306     colspan: false,
7307     headers: false,
7308     height: false,
7309     nowrap: false,
7310     rowspan: false,
7311     scope: false,
7312     valign: false,
7313     width: false,
7314     
7315     
7316     getAutoCreate : function(){
7317         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7318         
7319         cfg = {
7320             tag: 'td'
7321         };
7322         
7323         if(this.tag){
7324             cfg.tag = this.tag;
7325         }
7326         
7327         if (this.html) {
7328             cfg.html=this.html
7329         }
7330         if (this.cls) {
7331             cfg.cls=this.cls
7332         }
7333         if (this.abbr) {
7334             cfg.abbr=this.abbr
7335         }
7336         if (this.align) {
7337             cfg.align=this.align
7338         }
7339         if (this.axis) {
7340             cfg.axis=this.axis
7341         }
7342         if (this.bgcolor) {
7343             cfg.bgcolor=this.bgcolor
7344         }
7345         if (this.charoff) {
7346             cfg.charoff=this.charoff
7347         }
7348         if (this.colspan) {
7349             cfg.colspan=this.colspan
7350         }
7351         if (this.headers) {
7352             cfg.headers=this.headers
7353         }
7354         if (this.height) {
7355             cfg.height=this.height
7356         }
7357         if (this.nowrap) {
7358             cfg.nowrap=this.nowrap
7359         }
7360         if (this.rowspan) {
7361             cfg.rowspan=this.rowspan
7362         }
7363         if (this.scope) {
7364             cfg.scope=this.scope
7365         }
7366         if (this.valign) {
7367             cfg.valign=this.valign
7368         }
7369         if (this.width) {
7370             cfg.width=this.width
7371         }
7372         
7373         
7374         return cfg;
7375     }
7376    
7377 });
7378
7379  
7380
7381  /*
7382  * - LGPL
7383  *
7384  * table row
7385  * 
7386  */
7387
7388 /**
7389  * @class Roo.bootstrap.TableRow
7390  * @extends Roo.bootstrap.Component
7391  * Bootstrap TableRow class
7392  * @cfg {String} cls row class
7393  * @cfg {String} align Aligns the content in a table row
7394  * @cfg {String} bgcolor Specifies a background color for a table row
7395  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7396  * @cfg {String} valign Vertical aligns the content in a table row
7397  * 
7398  * @constructor
7399  * Create a new TableRow
7400  * @param {Object} config The config object
7401  */
7402
7403 Roo.bootstrap.TableRow = function(config){
7404     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7405 };
7406
7407 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7408     
7409     cls: false,
7410     align: false,
7411     bgcolor: false,
7412     charoff: false,
7413     valign: false,
7414     
7415     getAutoCreate : function(){
7416         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7417         
7418         cfg = {
7419             tag: 'tr'
7420         };
7421             
7422         if(this.cls){
7423             cfg.cls = this.cls;
7424         }
7425         if(this.align){
7426             cfg.align = this.align;
7427         }
7428         if(this.bgcolor){
7429             cfg.bgcolor = this.bgcolor;
7430         }
7431         if(this.charoff){
7432             cfg.charoff = this.charoff;
7433         }
7434         if(this.valign){
7435             cfg.valign = this.valign;
7436         }
7437         
7438         return cfg;
7439     }
7440    
7441 });
7442
7443  
7444
7445  /*
7446  * - LGPL
7447  *
7448  * table body
7449  * 
7450  */
7451
7452 /**
7453  * @class Roo.bootstrap.TableBody
7454  * @extends Roo.bootstrap.Component
7455  * Bootstrap TableBody class
7456  * @cfg {String} cls element class
7457  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7458  * @cfg {String} align Aligns the content inside the element
7459  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7460  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7461  * 
7462  * @constructor
7463  * Create a new TableBody
7464  * @param {Object} config The config object
7465  */
7466
7467 Roo.bootstrap.TableBody = function(config){
7468     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7469 };
7470
7471 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7472     
7473     cls: false,
7474     tag: false,
7475     align: false,
7476     charoff: false,
7477     valign: false,
7478     
7479     getAutoCreate : function(){
7480         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7481         
7482         cfg = {
7483             tag: 'tbody'
7484         };
7485             
7486         if (this.cls) {
7487             cfg.cls=this.cls
7488         }
7489         if(this.tag){
7490             cfg.tag = this.tag;
7491         }
7492         
7493         if(this.align){
7494             cfg.align = this.align;
7495         }
7496         if(this.charoff){
7497             cfg.charoff = this.charoff;
7498         }
7499         if(this.valign){
7500             cfg.valign = this.valign;
7501         }
7502         
7503         return cfg;
7504     }
7505     
7506     
7507 //    initEvents : function()
7508 //    {
7509 //        
7510 //        if(!this.store){
7511 //            return;
7512 //        }
7513 //        
7514 //        this.store = Roo.factory(this.store, Roo.data);
7515 //        this.store.on('load', this.onLoad, this);
7516 //        
7517 //        this.store.load();
7518 //        
7519 //    },
7520 //    
7521 //    onLoad: function () 
7522 //    {   
7523 //        this.fireEvent('load', this);
7524 //    }
7525 //    
7526 //   
7527 });
7528
7529  
7530
7531  /*
7532  * Based on:
7533  * Ext JS Library 1.1.1
7534  * Copyright(c) 2006-2007, Ext JS, LLC.
7535  *
7536  * Originally Released Under LGPL - original licence link has changed is not relivant.
7537  *
7538  * Fork - LGPL
7539  * <script type="text/javascript">
7540  */
7541
7542 // as we use this in bootstrap.
7543 Roo.namespace('Roo.form');
7544  /**
7545  * @class Roo.form.Action
7546  * Internal Class used to handle form actions
7547  * @constructor
7548  * @param {Roo.form.BasicForm} el The form element or its id
7549  * @param {Object} config Configuration options
7550  */
7551
7552  
7553  
7554 // define the action interface
7555 Roo.form.Action = function(form, options){
7556     this.form = form;
7557     this.options = options || {};
7558 };
7559 /**
7560  * Client Validation Failed
7561  * @const 
7562  */
7563 Roo.form.Action.CLIENT_INVALID = 'client';
7564 /**
7565  * Server Validation Failed
7566  * @const 
7567  */
7568 Roo.form.Action.SERVER_INVALID = 'server';
7569  /**
7570  * Connect to Server Failed
7571  * @const 
7572  */
7573 Roo.form.Action.CONNECT_FAILURE = 'connect';
7574 /**
7575  * Reading Data from Server Failed
7576  * @const 
7577  */
7578 Roo.form.Action.LOAD_FAILURE = 'load';
7579
7580 Roo.form.Action.prototype = {
7581     type : 'default',
7582     failureType : undefined,
7583     response : undefined,
7584     result : undefined,
7585
7586     // interface method
7587     run : function(options){
7588
7589     },
7590
7591     // interface method
7592     success : function(response){
7593
7594     },
7595
7596     // interface method
7597     handleResponse : function(response){
7598
7599     },
7600
7601     // default connection failure
7602     failure : function(response){
7603         
7604         this.response = response;
7605         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7606         this.form.afterAction(this, false);
7607     },
7608
7609     processResponse : function(response){
7610         this.response = response;
7611         if(!response.responseText){
7612             return true;
7613         }
7614         this.result = this.handleResponse(response);
7615         return this.result;
7616     },
7617
7618     // utility functions used internally
7619     getUrl : function(appendParams){
7620         var url = this.options.url || this.form.url || this.form.el.dom.action;
7621         if(appendParams){
7622             var p = this.getParams();
7623             if(p){
7624                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7625             }
7626         }
7627         return url;
7628     },
7629
7630     getMethod : function(){
7631         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7632     },
7633
7634     getParams : function(){
7635         var bp = this.form.baseParams;
7636         var p = this.options.params;
7637         if(p){
7638             if(typeof p == "object"){
7639                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7640             }else if(typeof p == 'string' && bp){
7641                 p += '&' + Roo.urlEncode(bp);
7642             }
7643         }else if(bp){
7644             p = Roo.urlEncode(bp);
7645         }
7646         return p;
7647     },
7648
7649     createCallback : function(){
7650         return {
7651             success: this.success,
7652             failure: this.failure,
7653             scope: this,
7654             timeout: (this.form.timeout*1000),
7655             upload: this.form.fileUpload ? this.success : undefined
7656         };
7657     }
7658 };
7659
7660 Roo.form.Action.Submit = function(form, options){
7661     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7662 };
7663
7664 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7665     type : 'submit',
7666
7667     haveProgress : false,
7668     uploadComplete : false,
7669     
7670     // uploadProgress indicator.
7671     uploadProgress : function()
7672     {
7673         if (!this.form.progressUrl) {
7674             return;
7675         }
7676         
7677         if (!this.haveProgress) {
7678             Roo.MessageBox.progress("Uploading", "Uploading");
7679         }
7680         if (this.uploadComplete) {
7681            Roo.MessageBox.hide();
7682            return;
7683         }
7684         
7685         this.haveProgress = true;
7686    
7687         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7688         
7689         var c = new Roo.data.Connection();
7690         c.request({
7691             url : this.form.progressUrl,
7692             params: {
7693                 id : uid
7694             },
7695             method: 'GET',
7696             success : function(req){
7697                //console.log(data);
7698                 var rdata = false;
7699                 var edata;
7700                 try  {
7701                    rdata = Roo.decode(req.responseText)
7702                 } catch (e) {
7703                     Roo.log("Invalid data from server..");
7704                     Roo.log(edata);
7705                     return;
7706                 }
7707                 if (!rdata || !rdata.success) {
7708                     Roo.log(rdata);
7709                     Roo.MessageBox.alert(Roo.encode(rdata));
7710                     return;
7711                 }
7712                 var data = rdata.data;
7713                 
7714                 if (this.uploadComplete) {
7715                    Roo.MessageBox.hide();
7716                    return;
7717                 }
7718                    
7719                 if (data){
7720                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7721                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7722                     );
7723                 }
7724                 this.uploadProgress.defer(2000,this);
7725             },
7726        
7727             failure: function(data) {
7728                 Roo.log('progress url failed ');
7729                 Roo.log(data);
7730             },
7731             scope : this
7732         });
7733            
7734     },
7735     
7736     
7737     run : function()
7738     {
7739         // run get Values on the form, so it syncs any secondary forms.
7740         this.form.getValues();
7741         
7742         var o = this.options;
7743         var method = this.getMethod();
7744         var isPost = method == 'POST';
7745         if(o.clientValidation === false || this.form.isValid()){
7746             
7747             if (this.form.progressUrl) {
7748                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7749                     (new Date() * 1) + '' + Math.random());
7750                     
7751             } 
7752             
7753             
7754             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7755                 form:this.form.el.dom,
7756                 url:this.getUrl(!isPost),
7757                 method: method,
7758                 params:isPost ? this.getParams() : null,
7759                 isUpload: this.form.fileUpload
7760             }));
7761             
7762             this.uploadProgress();
7763
7764         }else if (o.clientValidation !== false){ // client validation failed
7765             this.failureType = Roo.form.Action.CLIENT_INVALID;
7766             this.form.afterAction(this, false);
7767         }
7768     },
7769
7770     success : function(response)
7771     {
7772         this.uploadComplete= true;
7773         if (this.haveProgress) {
7774             Roo.MessageBox.hide();
7775         }
7776         
7777         
7778         var result = this.processResponse(response);
7779         if(result === true || result.success){
7780             this.form.afterAction(this, true);
7781             return;
7782         }
7783         if(result.errors){
7784             this.form.markInvalid(result.errors);
7785             this.failureType = Roo.form.Action.SERVER_INVALID;
7786         }
7787         this.form.afterAction(this, false);
7788     },
7789     failure : function(response)
7790     {
7791         this.uploadComplete= true;
7792         if (this.haveProgress) {
7793             Roo.MessageBox.hide();
7794         }
7795         
7796         this.response = response;
7797         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7798         this.form.afterAction(this, false);
7799     },
7800     
7801     handleResponse : function(response){
7802         if(this.form.errorReader){
7803             var rs = this.form.errorReader.read(response);
7804             var errors = [];
7805             if(rs.records){
7806                 for(var i = 0, len = rs.records.length; i < len; i++) {
7807                     var r = rs.records[i];
7808                     errors[i] = r.data;
7809                 }
7810             }
7811             if(errors.length < 1){
7812                 errors = null;
7813             }
7814             return {
7815                 success : rs.success,
7816                 errors : errors
7817             };
7818         }
7819         var ret = false;
7820         try {
7821             ret = Roo.decode(response.responseText);
7822         } catch (e) {
7823             ret = {
7824                 success: false,
7825                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7826                 errors : []
7827             };
7828         }
7829         return ret;
7830         
7831     }
7832 });
7833
7834
7835 Roo.form.Action.Load = function(form, options){
7836     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7837     this.reader = this.form.reader;
7838 };
7839
7840 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7841     type : 'load',
7842
7843     run : function(){
7844         
7845         Roo.Ajax.request(Roo.apply(
7846                 this.createCallback(), {
7847                     method:this.getMethod(),
7848                     url:this.getUrl(false),
7849                     params:this.getParams()
7850         }));
7851     },
7852
7853     success : function(response){
7854         
7855         var result = this.processResponse(response);
7856         if(result === true || !result.success || !result.data){
7857             this.failureType = Roo.form.Action.LOAD_FAILURE;
7858             this.form.afterAction(this, false);
7859             return;
7860         }
7861         this.form.clearInvalid();
7862         this.form.setValues(result.data);
7863         this.form.afterAction(this, true);
7864     },
7865
7866     handleResponse : function(response){
7867         if(this.form.reader){
7868             var rs = this.form.reader.read(response);
7869             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7870             return {
7871                 success : rs.success,
7872                 data : data
7873             };
7874         }
7875         return Roo.decode(response.responseText);
7876     }
7877 });
7878
7879 Roo.form.Action.ACTION_TYPES = {
7880     'load' : Roo.form.Action.Load,
7881     'submit' : Roo.form.Action.Submit
7882 };/*
7883  * - LGPL
7884  *
7885  * form
7886  *
7887  */
7888
7889 /**
7890  * @class Roo.bootstrap.Form
7891  * @extends Roo.bootstrap.Component
7892  * Bootstrap Form class
7893  * @cfg {String} method  GET | POST (default POST)
7894  * @cfg {String} labelAlign top | left (default top)
7895  * @cfg {String} align left  | right - for navbars
7896  * @cfg {Boolean} loadMask load mask when submit (default true)
7897
7898  *
7899  * @constructor
7900  * Create a new Form
7901  * @param {Object} config The config object
7902  */
7903
7904
7905 Roo.bootstrap.Form = function(config){
7906     
7907     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7908     
7909     Roo.bootstrap.Form.popover.apply();
7910     
7911     this.addEvents({
7912         /**
7913          * @event clientvalidation
7914          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7915          * @param {Form} this
7916          * @param {Boolean} valid true if the form has passed client-side validation
7917          */
7918         clientvalidation: true,
7919         /**
7920          * @event beforeaction
7921          * Fires before any action is performed. Return false to cancel the action.
7922          * @param {Form} this
7923          * @param {Action} action The action to be performed
7924          */
7925         beforeaction: true,
7926         /**
7927          * @event actionfailed
7928          * Fires when an action fails.
7929          * @param {Form} this
7930          * @param {Action} action The action that failed
7931          */
7932         actionfailed : true,
7933         /**
7934          * @event actioncomplete
7935          * Fires when an action is completed.
7936          * @param {Form} this
7937          * @param {Action} action The action that completed
7938          */
7939         actioncomplete : true
7940     });
7941 };
7942
7943 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7944
7945      /**
7946      * @cfg {String} method
7947      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7948      */
7949     method : 'POST',
7950     /**
7951      * @cfg {String} url
7952      * The URL to use for form actions if one isn't supplied in the action options.
7953      */
7954     /**
7955      * @cfg {Boolean} fileUpload
7956      * Set to true if this form is a file upload.
7957      */
7958
7959     /**
7960      * @cfg {Object} baseParams
7961      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7962      */
7963
7964     /**
7965      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7966      */
7967     timeout: 30,
7968     /**
7969      * @cfg {Sting} align (left|right) for navbar forms
7970      */
7971     align : 'left',
7972
7973     // private
7974     activeAction : null,
7975
7976     /**
7977      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7978      * element by passing it or its id or mask the form itself by passing in true.
7979      * @type Mixed
7980      */
7981     waitMsgTarget : false,
7982
7983     loadMask : true,
7984     
7985     /**
7986      * @cfg {Boolean} errorMask (true|false) default false
7987      */
7988     errorMask : false,
7989     
7990     /**
7991      * @cfg {Number} maskOffset Default 100
7992      */
7993     maskOffset : 100,
7994     
7995     /**
7996      * @cfg {Boolean} maskBody
7997      */
7998     maskBody : false,
7999
8000     getAutoCreate : function(){
8001
8002         var cfg = {
8003             tag: 'form',
8004             method : this.method || 'POST',
8005             id : this.id || Roo.id(),
8006             cls : ''
8007         };
8008         if (this.parent().xtype.match(/^Nav/)) {
8009             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8010
8011         }
8012
8013         if (this.labelAlign == 'left' ) {
8014             cfg.cls += ' form-horizontal';
8015         }
8016
8017
8018         return cfg;
8019     },
8020     initEvents : function()
8021     {
8022         this.el.on('submit', this.onSubmit, this);
8023         // this was added as random key presses on the form where triggering form submit.
8024         this.el.on('keypress', function(e) {
8025             if (e.getCharCode() != 13) {
8026                 return true;
8027             }
8028             // we might need to allow it for textareas.. and some other items.
8029             // check e.getTarget().
8030
8031             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8032                 return true;
8033             }
8034
8035             Roo.log("keypress blocked");
8036
8037             e.preventDefault();
8038             return false;
8039         });
8040         
8041     },
8042     // private
8043     onSubmit : function(e){
8044         e.stopEvent();
8045     },
8046
8047      /**
8048      * Returns true if client-side validation on the form is successful.
8049      * @return Boolean
8050      */
8051     isValid : function(){
8052         var items = this.getItems();
8053         var valid = true;
8054         var target = false;
8055         
8056         items.each(function(f){
8057             
8058             if(f.validate()){
8059                 return;
8060             }
8061             
8062             Roo.log('invalid field: ' + f.name);
8063             
8064             valid = false;
8065
8066             if(!target && f.el.isVisible(true)){
8067                 target = f;
8068             }
8069            
8070         });
8071         
8072         if(this.errorMask && !valid){
8073             Roo.bootstrap.Form.popover.mask(this, target);
8074         }
8075         
8076         return valid;
8077     },
8078     
8079     /**
8080      * Returns true if any fields in this form have changed since their original load.
8081      * @return Boolean
8082      */
8083     isDirty : function(){
8084         var dirty = false;
8085         var items = this.getItems();
8086         items.each(function(f){
8087            if(f.isDirty()){
8088                dirty = true;
8089                return false;
8090            }
8091            return true;
8092         });
8093         return dirty;
8094     },
8095      /**
8096      * Performs a predefined action (submit or load) or custom actions you define on this form.
8097      * @param {String} actionName The name of the action type
8098      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8099      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8100      * accept other config options):
8101      * <pre>
8102 Property          Type             Description
8103 ----------------  ---------------  ----------------------------------------------------------------------------------
8104 url               String           The url for the action (defaults to the form's url)
8105 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8106 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8107 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8108                                    validate the form on the client (defaults to false)
8109      * </pre>
8110      * @return {BasicForm} this
8111      */
8112     doAction : function(action, options){
8113         if(typeof action == 'string'){
8114             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8115         }
8116         if(this.fireEvent('beforeaction', this, action) !== false){
8117             this.beforeAction(action);
8118             action.run.defer(100, action);
8119         }
8120         return this;
8121     },
8122
8123     // private
8124     beforeAction : function(action){
8125         var o = action.options;
8126         
8127         if(this.loadMask){
8128             
8129             if(this.maskBody){
8130                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8131             } else {
8132                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8133             }
8134         }
8135         // not really supported yet.. ??
8136
8137         //if(this.waitMsgTarget === true){
8138         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8139         //}else if(this.waitMsgTarget){
8140         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8141         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8142         //}else {
8143         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8144        // }
8145
8146     },
8147
8148     // private
8149     afterAction : function(action, success){
8150         this.activeAction = null;
8151         var o = action.options;
8152
8153         if(this.loadMask){
8154             
8155             if(this.maskBody){
8156                 Roo.get(document.body).unmask();
8157             } else {
8158                 this.el.unmask();
8159             }
8160         }
8161         
8162         //if(this.waitMsgTarget === true){
8163 //            this.el.unmask();
8164         //}else if(this.waitMsgTarget){
8165         //    this.waitMsgTarget.unmask();
8166         //}else{
8167         //    Roo.MessageBox.updateProgress(1);
8168         //    Roo.MessageBox.hide();
8169        // }
8170         //
8171         if(success){
8172             if(o.reset){
8173                 this.reset();
8174             }
8175             Roo.callback(o.success, o.scope, [this, action]);
8176             this.fireEvent('actioncomplete', this, action);
8177
8178         }else{
8179
8180             // failure condition..
8181             // we have a scenario where updates need confirming.
8182             // eg. if a locking scenario exists..
8183             // we look for { errors : { needs_confirm : true }} in the response.
8184             if (
8185                 (typeof(action.result) != 'undefined')  &&
8186                 (typeof(action.result.errors) != 'undefined')  &&
8187                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8188            ){
8189                 var _t = this;
8190                 Roo.log("not supported yet");
8191                  /*
8192
8193                 Roo.MessageBox.confirm(
8194                     "Change requires confirmation",
8195                     action.result.errorMsg,
8196                     function(r) {
8197                         if (r != 'yes') {
8198                             return;
8199                         }
8200                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8201                     }
8202
8203                 );
8204                 */
8205
8206
8207                 return;
8208             }
8209
8210             Roo.callback(o.failure, o.scope, [this, action]);
8211             // show an error message if no failed handler is set..
8212             if (!this.hasListener('actionfailed')) {
8213                 Roo.log("need to add dialog support");
8214                 /*
8215                 Roo.MessageBox.alert("Error",
8216                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8217                         action.result.errorMsg :
8218                         "Saving Failed, please check your entries or try again"
8219                 );
8220                 */
8221             }
8222
8223             this.fireEvent('actionfailed', this, action);
8224         }
8225
8226     },
8227     /**
8228      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8229      * @param {String} id The value to search for
8230      * @return Field
8231      */
8232     findField : function(id){
8233         var items = this.getItems();
8234         var field = items.get(id);
8235         if(!field){
8236              items.each(function(f){
8237                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8238                     field = f;
8239                     return false;
8240                 }
8241                 return true;
8242             });
8243         }
8244         return field || null;
8245     },
8246      /**
8247      * Mark fields in this form invalid in bulk.
8248      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8249      * @return {BasicForm} this
8250      */
8251     markInvalid : function(errors){
8252         if(errors instanceof Array){
8253             for(var i = 0, len = errors.length; i < len; i++){
8254                 var fieldError = errors[i];
8255                 var f = this.findField(fieldError.id);
8256                 if(f){
8257                     f.markInvalid(fieldError.msg);
8258                 }
8259             }
8260         }else{
8261             var field, id;
8262             for(id in errors){
8263                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8264                     field.markInvalid(errors[id]);
8265                 }
8266             }
8267         }
8268         //Roo.each(this.childForms || [], function (f) {
8269         //    f.markInvalid(errors);
8270         //});
8271
8272         return this;
8273     },
8274
8275     /**
8276      * Set values for fields in this form in bulk.
8277      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8278      * @return {BasicForm} this
8279      */
8280     setValues : function(values){
8281         if(values instanceof Array){ // array of objects
8282             for(var i = 0, len = values.length; i < len; i++){
8283                 var v = values[i];
8284                 var f = this.findField(v.id);
8285                 if(f){
8286                     f.setValue(v.value);
8287                     if(this.trackResetOnLoad){
8288                         f.originalValue = f.getValue();
8289                     }
8290                 }
8291             }
8292         }else{ // object hash
8293             var field, id;
8294             for(id in values){
8295                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8296
8297                     if (field.setFromData &&
8298                         field.valueField &&
8299                         field.displayField &&
8300                         // combos' with local stores can
8301                         // be queried via setValue()
8302                         // to set their value..
8303                         (field.store && !field.store.isLocal)
8304                         ) {
8305                         // it's a combo
8306                         var sd = { };
8307                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8308                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8309                         field.setFromData(sd);
8310
8311                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8312                         
8313                         field.setFromData(values);
8314                         
8315                     } else {
8316                         field.setValue(values[id]);
8317                     }
8318
8319
8320                     if(this.trackResetOnLoad){
8321                         field.originalValue = field.getValue();
8322                     }
8323                 }
8324             }
8325         }
8326
8327         //Roo.each(this.childForms || [], function (f) {
8328         //    f.setValues(values);
8329         //});
8330
8331         return this;
8332     },
8333
8334     /**
8335      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8336      * they are returned as an array.
8337      * @param {Boolean} asString
8338      * @return {Object}
8339      */
8340     getValues : function(asString){
8341         //if (this.childForms) {
8342             // copy values from the child forms
8343         //    Roo.each(this.childForms, function (f) {
8344         //        this.setValues(f.getValues());
8345         //    }, this);
8346         //}
8347
8348
8349
8350         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8351         if(asString === true){
8352             return fs;
8353         }
8354         return Roo.urlDecode(fs);
8355     },
8356
8357     /**
8358      * Returns the fields in this form as an object with key/value pairs.
8359      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8360      * @return {Object}
8361      */
8362     getFieldValues : function(with_hidden)
8363     {
8364         var items = this.getItems();
8365         var ret = {};
8366         items.each(function(f){
8367             
8368             if (!f.getName()) {
8369                 return;
8370             }
8371             
8372             var v = f.getValue();
8373             
8374             if (f.inputType =='radio') {
8375                 if (typeof(ret[f.getName()]) == 'undefined') {
8376                     ret[f.getName()] = ''; // empty..
8377                 }
8378
8379                 if (!f.el.dom.checked) {
8380                     return;
8381
8382                 }
8383                 v = f.el.dom.value;
8384
8385             }
8386             
8387             if(f.xtype == 'MoneyField'){
8388                 ret[f.currencyName] = f.getCurrency();
8389             }
8390
8391             // not sure if this supported any more..
8392             if ((typeof(v) == 'object') && f.getRawValue) {
8393                 v = f.getRawValue() ; // dates..
8394             }
8395             // combo boxes where name != hiddenName...
8396             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8397                 ret[f.name] = f.getRawValue();
8398             }
8399             ret[f.getName()] = v;
8400         });
8401
8402         return ret;
8403     },
8404
8405     /**
8406      * Clears all invalid messages in this form.
8407      * @return {BasicForm} this
8408      */
8409     clearInvalid : function(){
8410         var items = this.getItems();
8411
8412         items.each(function(f){
8413            f.clearInvalid();
8414         });
8415
8416         return this;
8417     },
8418
8419     /**
8420      * Resets this form.
8421      * @return {BasicForm} this
8422      */
8423     reset : function(){
8424         var items = this.getItems();
8425         items.each(function(f){
8426             f.reset();
8427         });
8428
8429         Roo.each(this.childForms || [], function (f) {
8430             f.reset();
8431         });
8432
8433
8434         return this;
8435     },
8436     
8437     getItems : function()
8438     {
8439         var r=new Roo.util.MixedCollection(false, function(o){
8440             return o.id || (o.id = Roo.id());
8441         });
8442         var iter = function(el) {
8443             if (el.inputEl) {
8444                 r.add(el);
8445             }
8446             if (!el.items) {
8447                 return;
8448             }
8449             Roo.each(el.items,function(e) {
8450                 iter(e);
8451             });
8452         };
8453
8454         iter(this);
8455         return r;
8456     },
8457     
8458     hideFields : function(items)
8459     {
8460         Roo.each(items, function(i){
8461             
8462             var f = this.findField(i);
8463             
8464             if(!f){
8465                 return;
8466             }
8467             
8468             f.hide();
8469             
8470         }, this);
8471     },
8472     
8473     showFields : function(items)
8474     {
8475         Roo.each(items, function(i){
8476             
8477             var f = this.findField(i);
8478             
8479             if(!f){
8480                 return;
8481             }
8482             
8483             f.show();
8484             
8485         }, this);
8486     }
8487
8488 });
8489
8490 Roo.apply(Roo.bootstrap.Form, {
8491     
8492     popover : {
8493         
8494         padding : 5,
8495         
8496         isApplied : false,
8497         
8498         isMasked : false,
8499         
8500         form : false,
8501         
8502         target : false,
8503         
8504         toolTip : false,
8505         
8506         intervalID : false,
8507         
8508         maskEl : false,
8509         
8510         apply : function()
8511         {
8512             if(this.isApplied){
8513                 return;
8514             }
8515             
8516             this.maskEl = {
8517                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8518                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8519                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8520                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8521             };
8522             
8523             this.maskEl.top.enableDisplayMode("block");
8524             this.maskEl.left.enableDisplayMode("block");
8525             this.maskEl.bottom.enableDisplayMode("block");
8526             this.maskEl.right.enableDisplayMode("block");
8527             
8528             this.toolTip = new Roo.bootstrap.Tooltip({
8529                 cls : 'roo-form-error-popover',
8530                 alignment : {
8531                     'left' : ['r-l', [-2,0], 'right'],
8532                     'right' : ['l-r', [2,0], 'left'],
8533                     'bottom' : ['tl-bl', [0,2], 'top'],
8534                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8535                 }
8536             });
8537             
8538             this.toolTip.render(Roo.get(document.body));
8539
8540             this.toolTip.el.enableDisplayMode("block");
8541             
8542             Roo.get(document.body).on('click', function(){
8543                 this.unmask();
8544             }, this);
8545             
8546             Roo.get(document.body).on('touchstart', function(){
8547                 this.unmask();
8548             }, this);
8549             
8550             this.isApplied = true
8551         },
8552         
8553         mask : function(form, target)
8554         {
8555             this.form = form;
8556             
8557             this.target = target;
8558             
8559             if(!this.form.errorMask || !target.el){
8560                 return;
8561             }
8562             
8563             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8564             
8565             Roo.log(scrollable);
8566             
8567             var ot = this.target.el.calcOffsetsTo(scrollable);
8568             
8569             var scrollTo = ot[1] - this.form.maskOffset;
8570             
8571             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8572             
8573             scrollable.scrollTo('top', scrollTo);
8574             
8575             var box = this.target.el.getBox();
8576             Roo.log(box);
8577             var zIndex = Roo.bootstrap.Modal.zIndex++;
8578
8579             
8580             this.maskEl.top.setStyle('position', 'absolute');
8581             this.maskEl.top.setStyle('z-index', zIndex);
8582             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8583             this.maskEl.top.setLeft(0);
8584             this.maskEl.top.setTop(0);
8585             this.maskEl.top.show();
8586             
8587             this.maskEl.left.setStyle('position', 'absolute');
8588             this.maskEl.left.setStyle('z-index', zIndex);
8589             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8590             this.maskEl.left.setLeft(0);
8591             this.maskEl.left.setTop(box.y - this.padding);
8592             this.maskEl.left.show();
8593
8594             this.maskEl.bottom.setStyle('position', 'absolute');
8595             this.maskEl.bottom.setStyle('z-index', zIndex);
8596             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8597             this.maskEl.bottom.setLeft(0);
8598             this.maskEl.bottom.setTop(box.bottom + this.padding);
8599             this.maskEl.bottom.show();
8600
8601             this.maskEl.right.setStyle('position', 'absolute');
8602             this.maskEl.right.setStyle('z-index', zIndex);
8603             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8604             this.maskEl.right.setLeft(box.right + this.padding);
8605             this.maskEl.right.setTop(box.y - this.padding);
8606             this.maskEl.right.show();
8607
8608             this.toolTip.bindEl = this.target.el;
8609
8610             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8611
8612             var tip = this.target.blankText;
8613
8614             if(this.target.getValue() !== '' ) {
8615                 
8616                 if (this.target.invalidText.length) {
8617                     tip = this.target.invalidText;
8618                 } else if (this.target.regexText.length){
8619                     tip = this.target.regexText;
8620                 }
8621             }
8622
8623             this.toolTip.show(tip);
8624
8625             this.intervalID = window.setInterval(function() {
8626                 Roo.bootstrap.Form.popover.unmask();
8627             }, 10000);
8628
8629             window.onwheel = function(){ return false;};
8630             
8631             (function(){ this.isMasked = true; }).defer(500, this);
8632             
8633         },
8634         
8635         unmask : function()
8636         {
8637             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8638                 return;
8639             }
8640             
8641             this.maskEl.top.setStyle('position', 'absolute');
8642             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8643             this.maskEl.top.hide();
8644
8645             this.maskEl.left.setStyle('position', 'absolute');
8646             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8647             this.maskEl.left.hide();
8648
8649             this.maskEl.bottom.setStyle('position', 'absolute');
8650             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8651             this.maskEl.bottom.hide();
8652
8653             this.maskEl.right.setStyle('position', 'absolute');
8654             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8655             this.maskEl.right.hide();
8656             
8657             this.toolTip.hide();
8658             
8659             this.toolTip.el.hide();
8660             
8661             window.onwheel = function(){ return true;};
8662             
8663             if(this.intervalID){
8664                 window.clearInterval(this.intervalID);
8665                 this.intervalID = false;
8666             }
8667             
8668             this.isMasked = false;
8669             
8670         }
8671         
8672     }
8673     
8674 });
8675
8676 /*
8677  * Based on:
8678  * Ext JS Library 1.1.1
8679  * Copyright(c) 2006-2007, Ext JS, LLC.
8680  *
8681  * Originally Released Under LGPL - original licence link has changed is not relivant.
8682  *
8683  * Fork - LGPL
8684  * <script type="text/javascript">
8685  */
8686 /**
8687  * @class Roo.form.VTypes
8688  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8689  * @singleton
8690  */
8691 Roo.form.VTypes = function(){
8692     // closure these in so they are only created once.
8693     var alpha = /^[a-zA-Z_]+$/;
8694     var alphanum = /^[a-zA-Z0-9_]+$/;
8695     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8696     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8697
8698     // All these messages and functions are configurable
8699     return {
8700         /**
8701          * The function used to validate email addresses
8702          * @param {String} value The email address
8703          */
8704         'email' : function(v){
8705             return email.test(v);
8706         },
8707         /**
8708          * The error text to display when the email validation function returns false
8709          * @type String
8710          */
8711         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8712         /**
8713          * The keystroke filter mask to be applied on email input
8714          * @type RegExp
8715          */
8716         'emailMask' : /[a-z0-9_\.\-@]/i,
8717
8718         /**
8719          * The function used to validate URLs
8720          * @param {String} value The URL
8721          */
8722         'url' : function(v){
8723             return url.test(v);
8724         },
8725         /**
8726          * The error text to display when the url validation function returns false
8727          * @type String
8728          */
8729         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8730         
8731         /**
8732          * The function used to validate alpha values
8733          * @param {String} value The value
8734          */
8735         'alpha' : function(v){
8736             return alpha.test(v);
8737         },
8738         /**
8739          * The error text to display when the alpha validation function returns false
8740          * @type String
8741          */
8742         'alphaText' : 'This field should only contain letters and _',
8743         /**
8744          * The keystroke filter mask to be applied on alpha input
8745          * @type RegExp
8746          */
8747         'alphaMask' : /[a-z_]/i,
8748
8749         /**
8750          * The function used to validate alphanumeric values
8751          * @param {String} value The value
8752          */
8753         'alphanum' : function(v){
8754             return alphanum.test(v);
8755         },
8756         /**
8757          * The error text to display when the alphanumeric validation function returns false
8758          * @type String
8759          */
8760         'alphanumText' : 'This field should only contain letters, numbers and _',
8761         /**
8762          * The keystroke filter mask to be applied on alphanumeric input
8763          * @type RegExp
8764          */
8765         'alphanumMask' : /[a-z0-9_]/i
8766     };
8767 }();/*
8768  * - LGPL
8769  *
8770  * Input
8771  * 
8772  */
8773
8774 /**
8775  * @class Roo.bootstrap.Input
8776  * @extends Roo.bootstrap.Component
8777  * Bootstrap Input class
8778  * @cfg {Boolean} disabled is it disabled
8779  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8780  * @cfg {String} name name of the input
8781  * @cfg {string} fieldLabel - the label associated
8782  * @cfg {string} placeholder - placeholder to put in text.
8783  * @cfg {string}  before - input group add on before
8784  * @cfg {string} after - input group add on after
8785  * @cfg {string} size - (lg|sm) or leave empty..
8786  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8787  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8788  * @cfg {Number} md colspan out of 12 for computer-sized screens
8789  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8790  * @cfg {string} value default value of the input
8791  * @cfg {Number} labelWidth set the width of label 
8792  * @cfg {Number} labellg set the width of label (1-12)
8793  * @cfg {Number} labelmd set the width of label (1-12)
8794  * @cfg {Number} labelsm set the width of label (1-12)
8795  * @cfg {Number} labelxs set the width of label (1-12)
8796  * @cfg {String} labelAlign (top|left)
8797  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8798  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8799  * @cfg {String} indicatorpos (left|right) default left
8800  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8801  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8802
8803  * @cfg {String} align (left|center|right) Default left
8804  * @cfg {Boolean} forceFeedback (true|false) Default false
8805  * 
8806  * @constructor
8807  * Create a new Input
8808  * @param {Object} config The config object
8809  */
8810
8811 Roo.bootstrap.Input = function(config){
8812     
8813     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8814     
8815     this.addEvents({
8816         /**
8817          * @event focus
8818          * Fires when this field receives input focus.
8819          * @param {Roo.form.Field} this
8820          */
8821         focus : true,
8822         /**
8823          * @event blur
8824          * Fires when this field loses input focus.
8825          * @param {Roo.form.Field} this
8826          */
8827         blur : true,
8828         /**
8829          * @event specialkey
8830          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8831          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8832          * @param {Roo.form.Field} this
8833          * @param {Roo.EventObject} e The event object
8834          */
8835         specialkey : true,
8836         /**
8837          * @event change
8838          * Fires just before the field blurs if the field value has changed.
8839          * @param {Roo.form.Field} this
8840          * @param {Mixed} newValue The new value
8841          * @param {Mixed} oldValue The original value
8842          */
8843         change : true,
8844         /**
8845          * @event invalid
8846          * Fires after the field has been marked as invalid.
8847          * @param {Roo.form.Field} this
8848          * @param {String} msg The validation message
8849          */
8850         invalid : true,
8851         /**
8852          * @event valid
8853          * Fires after the field has been validated with no errors.
8854          * @param {Roo.form.Field} this
8855          */
8856         valid : true,
8857          /**
8858          * @event keyup
8859          * Fires after the key up
8860          * @param {Roo.form.Field} this
8861          * @param {Roo.EventObject}  e The event Object
8862          */
8863         keyup : true
8864     });
8865 };
8866
8867 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8868      /**
8869      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8870       automatic validation (defaults to "keyup").
8871      */
8872     validationEvent : "keyup",
8873      /**
8874      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8875      */
8876     validateOnBlur : true,
8877     /**
8878      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8879      */
8880     validationDelay : 250,
8881      /**
8882      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8883      */
8884     focusClass : "x-form-focus",  // not needed???
8885     
8886        
8887     /**
8888      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8889      */
8890     invalidClass : "has-warning",
8891     
8892     /**
8893      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8894      */
8895     validClass : "has-success",
8896     
8897     /**
8898      * @cfg {Boolean} hasFeedback (true|false) default true
8899      */
8900     hasFeedback : true,
8901     
8902     /**
8903      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8904      */
8905     invalidFeedbackClass : "glyphicon-warning-sign",
8906     
8907     /**
8908      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8909      */
8910     validFeedbackClass : "glyphicon-ok",
8911     
8912     /**
8913      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8914      */
8915     selectOnFocus : false,
8916     
8917      /**
8918      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8919      */
8920     maskRe : null,
8921        /**
8922      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8923      */
8924     vtype : null,
8925     
8926       /**
8927      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8928      */
8929     disableKeyFilter : false,
8930     
8931        /**
8932      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8933      */
8934     disabled : false,
8935      /**
8936      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8937      */
8938     allowBlank : true,
8939     /**
8940      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8941      */
8942     blankText : "Please complete this mandatory field",
8943     
8944      /**
8945      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8946      */
8947     minLength : 0,
8948     /**
8949      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8950      */
8951     maxLength : Number.MAX_VALUE,
8952     /**
8953      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8954      */
8955     minLengthText : "The minimum length for this field is {0}",
8956     /**
8957      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8958      */
8959     maxLengthText : "The maximum length for this field is {0}",
8960   
8961     
8962     /**
8963      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8964      * If available, this function will be called only after the basic validators all return true, and will be passed the
8965      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8966      */
8967     validator : null,
8968     /**
8969      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8970      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8971      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8972      */
8973     regex : null,
8974     /**
8975      * @cfg {String} regexText -- Depricated - use Invalid Text
8976      */
8977     regexText : "",
8978     
8979     /**
8980      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8981      */
8982     invalidText : "",
8983     
8984     
8985     
8986     autocomplete: false,
8987     
8988     
8989     fieldLabel : '',
8990     inputType : 'text',
8991     
8992     name : false,
8993     placeholder: false,
8994     before : false,
8995     after : false,
8996     size : false,
8997     hasFocus : false,
8998     preventMark: false,
8999     isFormField : true,
9000     value : '',
9001     labelWidth : 2,
9002     labelAlign : false,
9003     readOnly : false,
9004     align : false,
9005     formatedValue : false,
9006     forceFeedback : false,
9007     
9008     indicatorpos : 'left',
9009     
9010     labellg : 0,
9011     labelmd : 0,
9012     labelsm : 0,
9013     labelxs : 0,
9014     
9015     capture : '',
9016     accept : '',
9017     
9018     parentLabelAlign : function()
9019     {
9020         var parent = this;
9021         while (parent.parent()) {
9022             parent = parent.parent();
9023             if (typeof(parent.labelAlign) !='undefined') {
9024                 return parent.labelAlign;
9025             }
9026         }
9027         return 'left';
9028         
9029     },
9030     
9031     getAutoCreate : function()
9032     {
9033         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9034         
9035         var id = Roo.id();
9036         
9037         var cfg = {};
9038         
9039         if(this.inputType != 'hidden'){
9040             cfg.cls = 'form-group' //input-group
9041         }
9042         
9043         var input =  {
9044             tag: 'input',
9045             id : id,
9046             type : this.inputType,
9047             value : this.value,
9048             cls : 'form-control',
9049             placeholder : this.placeholder || '',
9050             autocomplete : this.autocomplete || 'new-password'
9051         };
9052         
9053         if(this.capture.length){
9054             input.capture = this.capture;
9055         }
9056         
9057         if(this.accept.length){
9058             input.accept = this.accept + "/*";
9059         }
9060         
9061         if(this.align){
9062             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9063         }
9064         
9065         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9066             input.maxLength = this.maxLength;
9067         }
9068         
9069         if (this.disabled) {
9070             input.disabled=true;
9071         }
9072         
9073         if (this.readOnly) {
9074             input.readonly=true;
9075         }
9076         
9077         if (this.name) {
9078             input.name = this.name;
9079         }
9080         
9081         if (this.size) {
9082             input.cls += ' input-' + this.size;
9083         }
9084         
9085         var settings=this;
9086         ['xs','sm','md','lg'].map(function(size){
9087             if (settings[size]) {
9088                 cfg.cls += ' col-' + size + '-' + settings[size];
9089             }
9090         });
9091         
9092         var inputblock = input;
9093         
9094         var feedback = {
9095             tag: 'span',
9096             cls: 'glyphicon form-control-feedback'
9097         };
9098             
9099         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9100             
9101             inputblock = {
9102                 cls : 'has-feedback',
9103                 cn :  [
9104                     input,
9105                     feedback
9106                 ] 
9107             };  
9108         }
9109         
9110         if (this.before || this.after) {
9111             
9112             inputblock = {
9113                 cls : 'input-group',
9114                 cn :  [] 
9115             };
9116             
9117             if (this.before && typeof(this.before) == 'string') {
9118                 
9119                 inputblock.cn.push({
9120                     tag :'span',
9121                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9122                     html : this.before
9123                 });
9124             }
9125             if (this.before && typeof(this.before) == 'object') {
9126                 this.before = Roo.factory(this.before);
9127                 
9128                 inputblock.cn.push({
9129                     tag :'span',
9130                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9131                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9132                 });
9133             }
9134             
9135             inputblock.cn.push(input);
9136             
9137             if (this.after && typeof(this.after) == 'string') {
9138                 inputblock.cn.push({
9139                     tag :'span',
9140                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9141                     html : this.after
9142                 });
9143             }
9144             if (this.after && typeof(this.after) == 'object') {
9145                 this.after = Roo.factory(this.after);
9146                 
9147                 inputblock.cn.push({
9148                     tag :'span',
9149                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9150                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9151                 });
9152             }
9153             
9154             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9155                 inputblock.cls += ' has-feedback';
9156                 inputblock.cn.push(feedback);
9157             }
9158         };
9159         var indicator = {
9160             tag : 'i',
9161             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9162             tooltip : 'This field is required'
9163         };
9164         if (Roo.bootstrap.version == 4) {
9165             indicator = {
9166                 tag : 'i',
9167                 style : 'display-none'
9168             };
9169         }
9170         if (align ==='left' && this.fieldLabel.length) {
9171             
9172             cfg.cls += ' roo-form-group-label-left row';
9173             
9174             cfg.cn = [
9175                 indicator,
9176                 {
9177                     tag: 'label',
9178                     'for' :  id,
9179                     cls : 'control-label col-form-label',
9180                     html : this.fieldLabel
9181
9182                 },
9183                 {
9184                     cls : "", 
9185                     cn: [
9186                         inputblock
9187                     ]
9188                 }
9189             ];
9190             
9191             var labelCfg = cfg.cn[1];
9192             var contentCfg = cfg.cn[2];
9193             
9194             if(this.indicatorpos == 'right'){
9195                 cfg.cn = [
9196                     {
9197                         tag: 'label',
9198                         'for' :  id,
9199                         cls : 'control-label col-form-label',
9200                         cn : [
9201                             {
9202                                 tag : 'span',
9203                                 html : this.fieldLabel
9204                             },
9205                             indicator
9206                         ]
9207                     },
9208                     {
9209                         cls : "",
9210                         cn: [
9211                             inputblock
9212                         ]
9213                     }
9214
9215                 ];
9216                 
9217                 labelCfg = cfg.cn[0];
9218                 contentCfg = cfg.cn[1];
9219             
9220             }
9221             
9222             if(this.labelWidth > 12){
9223                 labelCfg.style = "width: " + this.labelWidth + 'px';
9224             }
9225             
9226             if(this.labelWidth < 13 && this.labelmd == 0){
9227                 this.labelmd = this.labelWidth;
9228             }
9229             
9230             if(this.labellg > 0){
9231                 labelCfg.cls += ' col-lg-' + this.labellg;
9232                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9233             }
9234             
9235             if(this.labelmd > 0){
9236                 labelCfg.cls += ' col-md-' + this.labelmd;
9237                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9238             }
9239             
9240             if(this.labelsm > 0){
9241                 labelCfg.cls += ' col-sm-' + this.labelsm;
9242                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9243             }
9244             
9245             if(this.labelxs > 0){
9246                 labelCfg.cls += ' col-xs-' + this.labelxs;
9247                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9248             }
9249             
9250             
9251         } else if ( this.fieldLabel.length) {
9252                 
9253             cfg.cn = [
9254                 {
9255                     tag : 'i',
9256                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9257                     tooltip : 'This field is required'
9258                 },
9259                 {
9260                     tag: 'label',
9261                    //cls : 'input-group-addon',
9262                     html : this.fieldLabel
9263
9264                 },
9265
9266                inputblock
9267
9268            ];
9269            
9270            if(this.indicatorpos == 'right'){
9271                 
9272                 cfg.cn = [
9273                     {
9274                         tag: 'label',
9275                        //cls : 'input-group-addon',
9276                         html : this.fieldLabel
9277
9278                     },
9279                     {
9280                         tag : 'i',
9281                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9282                         tooltip : 'This field is required'
9283                     },
9284
9285                    inputblock
9286
9287                ];
9288
9289             }
9290
9291         } else {
9292             
9293             cfg.cn = [
9294
9295                     inputblock
9296
9297             ];
9298                 
9299                 
9300         };
9301         
9302         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9303            cfg.cls += ' navbar-form';
9304         }
9305         
9306         if (this.parentType === 'NavGroup') {
9307            cfg.cls += ' navbar-form';
9308            cfg.tag = 'li';
9309         }
9310         
9311         return cfg;
9312         
9313     },
9314     /**
9315      * return the real input element.
9316      */
9317     inputEl: function ()
9318     {
9319         return this.el.select('input.form-control',true).first();
9320     },
9321     
9322     tooltipEl : function()
9323     {
9324         return this.inputEl();
9325     },
9326     
9327     indicatorEl : function()
9328     {
9329         if (Roo.bootstrap.version == 4) {
9330             return false; // not enabled in v4 yet.
9331         }
9332         
9333         var indicator = this.el.select('i.roo-required-indicator',true).first();
9334         
9335         if(!indicator){
9336             return false;
9337         }
9338         
9339         return indicator;
9340         
9341     },
9342     
9343     setDisabled : function(v)
9344     {
9345         var i  = this.inputEl().dom;
9346         if (!v) {
9347             i.removeAttribute('disabled');
9348             return;
9349             
9350         }
9351         i.setAttribute('disabled','true');
9352     },
9353     initEvents : function()
9354     {
9355           
9356         this.inputEl().on("keydown" , this.fireKey,  this);
9357         this.inputEl().on("focus", this.onFocus,  this);
9358         this.inputEl().on("blur", this.onBlur,  this);
9359         
9360         this.inputEl().relayEvent('keyup', this);
9361         
9362         this.indicator = this.indicatorEl();
9363         
9364         if(this.indicator){
9365             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9366         }
9367  
9368         // reference to original value for reset
9369         this.originalValue = this.getValue();
9370         //Roo.form.TextField.superclass.initEvents.call(this);
9371         if(this.validationEvent == 'keyup'){
9372             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9373             this.inputEl().on('keyup', this.filterValidation, this);
9374         }
9375         else if(this.validationEvent !== false){
9376             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9377         }
9378         
9379         if(this.selectOnFocus){
9380             this.on("focus", this.preFocus, this);
9381             
9382         }
9383         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9384             this.inputEl().on("keypress", this.filterKeys, this);
9385         } else {
9386             this.inputEl().relayEvent('keypress', this);
9387         }
9388        /* if(this.grow){
9389             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9390             this.el.on("click", this.autoSize,  this);
9391         }
9392         */
9393         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9394             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9395         }
9396         
9397         if (typeof(this.before) == 'object') {
9398             this.before.render(this.el.select('.roo-input-before',true).first());
9399         }
9400         if (typeof(this.after) == 'object') {
9401             this.after.render(this.el.select('.roo-input-after',true).first());
9402         }
9403         
9404         this.inputEl().on('change', this.onChange, this);
9405         
9406     },
9407     filterValidation : function(e){
9408         if(!e.isNavKeyPress()){
9409             this.validationTask.delay(this.validationDelay);
9410         }
9411     },
9412      /**
9413      * Validates the field value
9414      * @return {Boolean} True if the value is valid, else false
9415      */
9416     validate : function(){
9417         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9418         if(this.disabled || this.validateValue(this.getRawValue())){
9419             this.markValid();
9420             return true;
9421         }
9422         
9423         this.markInvalid();
9424         return false;
9425     },
9426     
9427     
9428     /**
9429      * Validates a value according to the field's validation rules and marks the field as invalid
9430      * if the validation fails
9431      * @param {Mixed} value The value to validate
9432      * @return {Boolean} True if the value is valid, else false
9433      */
9434     validateValue : function(value)
9435     {
9436         if(this.getVisibilityEl().hasClass('hidden')){
9437             return true;
9438         }
9439         
9440         if(value.length < 1)  { // if it's blank
9441             if(this.allowBlank){
9442                 return true;
9443             }
9444             return false;
9445         }
9446         
9447         if(value.length < this.minLength){
9448             return false;
9449         }
9450         if(value.length > this.maxLength){
9451             return false;
9452         }
9453         if(this.vtype){
9454             var vt = Roo.form.VTypes;
9455             if(!vt[this.vtype](value, this)){
9456                 return false;
9457             }
9458         }
9459         if(typeof this.validator == "function"){
9460             var msg = this.validator(value);
9461             if(msg !== true){
9462                 return false;
9463             }
9464             if (typeof(msg) == 'string') {
9465                 this.invalidText = msg;
9466             }
9467         }
9468         
9469         if(this.regex && !this.regex.test(value)){
9470             return false;
9471         }
9472         
9473         return true;
9474     },
9475     
9476      // private
9477     fireKey : function(e){
9478         //Roo.log('field ' + e.getKey());
9479         if(e.isNavKeyPress()){
9480             this.fireEvent("specialkey", this, e);
9481         }
9482     },
9483     focus : function (selectText){
9484         if(this.rendered){
9485             this.inputEl().focus();
9486             if(selectText === true){
9487                 this.inputEl().dom.select();
9488             }
9489         }
9490         return this;
9491     } ,
9492     
9493     onFocus : function(){
9494         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9495            // this.el.addClass(this.focusClass);
9496         }
9497         if(!this.hasFocus){
9498             this.hasFocus = true;
9499             this.startValue = this.getValue();
9500             this.fireEvent("focus", this);
9501         }
9502     },
9503     
9504     beforeBlur : Roo.emptyFn,
9505
9506     
9507     // private
9508     onBlur : function(){
9509         this.beforeBlur();
9510         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9511             //this.el.removeClass(this.focusClass);
9512         }
9513         this.hasFocus = false;
9514         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9515             this.validate();
9516         }
9517         var v = this.getValue();
9518         if(String(v) !== String(this.startValue)){
9519             this.fireEvent('change', this, v, this.startValue);
9520         }
9521         this.fireEvent("blur", this);
9522     },
9523     
9524     onChange : function(e)
9525     {
9526         var v = this.getValue();
9527         if(String(v) !== String(this.startValue)){
9528             this.fireEvent('change', this, v, this.startValue);
9529         }
9530         
9531     },
9532     
9533     /**
9534      * Resets the current field value to the originally loaded value and clears any validation messages
9535      */
9536     reset : function(){
9537         this.setValue(this.originalValue);
9538         this.validate();
9539     },
9540      /**
9541      * Returns the name of the field
9542      * @return {Mixed} name The name field
9543      */
9544     getName: function(){
9545         return this.name;
9546     },
9547      /**
9548      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9549      * @return {Mixed} value The field value
9550      */
9551     getValue : function(){
9552         
9553         var v = this.inputEl().getValue();
9554         
9555         return v;
9556     },
9557     /**
9558      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9559      * @return {Mixed} value The field value
9560      */
9561     getRawValue : function(){
9562         var v = this.inputEl().getValue();
9563         
9564         return v;
9565     },
9566     
9567     /**
9568      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9569      * @param {Mixed} value The value to set
9570      */
9571     setRawValue : function(v){
9572         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9573     },
9574     
9575     selectText : function(start, end){
9576         var v = this.getRawValue();
9577         if(v.length > 0){
9578             start = start === undefined ? 0 : start;
9579             end = end === undefined ? v.length : end;
9580             var d = this.inputEl().dom;
9581             if(d.setSelectionRange){
9582                 d.setSelectionRange(start, end);
9583             }else if(d.createTextRange){
9584                 var range = d.createTextRange();
9585                 range.moveStart("character", start);
9586                 range.moveEnd("character", v.length-end);
9587                 range.select();
9588             }
9589         }
9590     },
9591     
9592     /**
9593      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9594      * @param {Mixed} value The value to set
9595      */
9596     setValue : function(v){
9597         this.value = v;
9598         if(this.rendered){
9599             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9600             this.validate();
9601         }
9602     },
9603     
9604     /*
9605     processValue : function(value){
9606         if(this.stripCharsRe){
9607             var newValue = value.replace(this.stripCharsRe, '');
9608             if(newValue !== value){
9609                 this.setRawValue(newValue);
9610                 return newValue;
9611             }
9612         }
9613         return value;
9614     },
9615   */
9616     preFocus : function(){
9617         
9618         if(this.selectOnFocus){
9619             this.inputEl().dom.select();
9620         }
9621     },
9622     filterKeys : function(e){
9623         var k = e.getKey();
9624         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9625             return;
9626         }
9627         var c = e.getCharCode(), cc = String.fromCharCode(c);
9628         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9629             return;
9630         }
9631         if(!this.maskRe.test(cc)){
9632             e.stopEvent();
9633         }
9634     },
9635      /**
9636      * Clear any invalid styles/messages for this field
9637      */
9638     clearInvalid : function(){
9639         
9640         if(!this.el || this.preventMark){ // not rendered
9641             return;
9642         }
9643         
9644      
9645         this.el.removeClass(this.invalidClass);
9646         
9647         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9648             
9649             var feedback = this.el.select('.form-control-feedback', true).first();
9650             
9651             if(feedback){
9652                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9653             }
9654             
9655         }
9656         
9657         if(this.indicator){
9658             this.indicator.removeClass('visible');
9659             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9660         }
9661         
9662         this.fireEvent('valid', this);
9663     },
9664     
9665      /**
9666      * Mark this field as valid
9667      */
9668     markValid : function()
9669     {
9670         if(!this.el  || this.preventMark){ // not rendered...
9671             return;
9672         }
9673         
9674         this.el.removeClass([this.invalidClass, this.validClass]);
9675         
9676         var feedback = this.el.select('.form-control-feedback', true).first();
9677             
9678         if(feedback){
9679             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9680         }
9681         
9682         if(this.indicator){
9683             this.indicator.removeClass('visible');
9684             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9685         }
9686         
9687         if(this.disabled){
9688             return;
9689         }
9690         
9691         if(this.allowBlank && !this.getRawValue().length){
9692             return;
9693         }
9694         
9695         this.el.addClass(this.validClass);
9696         
9697         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9698             
9699             var feedback = this.el.select('.form-control-feedback', true).first();
9700             
9701             if(feedback){
9702                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9703                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9704             }
9705             
9706         }
9707         
9708         this.fireEvent('valid', this);
9709     },
9710     
9711      /**
9712      * Mark this field as invalid
9713      * @param {String} msg The validation message
9714      */
9715     markInvalid : function(msg)
9716     {
9717         if(!this.el  || this.preventMark){ // not rendered
9718             return;
9719         }
9720         
9721         this.el.removeClass([this.invalidClass, this.validClass]);
9722         
9723         var feedback = this.el.select('.form-control-feedback', true).first();
9724             
9725         if(feedback){
9726             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9727         }
9728
9729         if(this.disabled){
9730             return;
9731         }
9732         
9733         if(this.allowBlank && !this.getRawValue().length){
9734             return;
9735         }
9736         
9737         if(this.indicator){
9738             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9739             this.indicator.addClass('visible');
9740         }
9741         
9742         this.el.addClass(this.invalidClass);
9743         
9744         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9745             
9746             var feedback = this.el.select('.form-control-feedback', true).first();
9747             
9748             if(feedback){
9749                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9750                 
9751                 if(this.getValue().length || this.forceFeedback){
9752                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9753                 }
9754                 
9755             }
9756             
9757         }
9758         
9759         this.fireEvent('invalid', this, msg);
9760     },
9761     // private
9762     SafariOnKeyDown : function(event)
9763     {
9764         // this is a workaround for a password hang bug on chrome/ webkit.
9765         if (this.inputEl().dom.type != 'password') {
9766             return;
9767         }
9768         
9769         var isSelectAll = false;
9770         
9771         if(this.inputEl().dom.selectionEnd > 0){
9772             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9773         }
9774         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9775             event.preventDefault();
9776             this.setValue('');
9777             return;
9778         }
9779         
9780         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9781             
9782             event.preventDefault();
9783             // this is very hacky as keydown always get's upper case.
9784             //
9785             var cc = String.fromCharCode(event.getCharCode());
9786             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9787             
9788         }
9789     },
9790     adjustWidth : function(tag, w){
9791         tag = tag.toLowerCase();
9792         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9793             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9794                 if(tag == 'input'){
9795                     return w + 2;
9796                 }
9797                 if(tag == 'textarea'){
9798                     return w-2;
9799                 }
9800             }else if(Roo.isOpera){
9801                 if(tag == 'input'){
9802                     return w + 2;
9803                 }
9804                 if(tag == 'textarea'){
9805                     return w-2;
9806                 }
9807             }
9808         }
9809         return w;
9810     },
9811     
9812     setFieldLabel : function(v)
9813     {
9814         if(!this.rendered){
9815             return;
9816         }
9817         
9818         if(this.indicatorEl()){
9819             var ar = this.el.select('label > span',true);
9820             
9821             if (ar.elements.length) {
9822                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9823                 this.fieldLabel = v;
9824                 return;
9825             }
9826             
9827             var br = this.el.select('label',true);
9828             
9829             if(br.elements.length) {
9830                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9831                 this.fieldLabel = v;
9832                 return;
9833             }
9834             
9835             Roo.log('Cannot Found any of label > span || label in input');
9836             return;
9837         }
9838         
9839         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9840         this.fieldLabel = v;
9841         
9842         
9843     }
9844 });
9845
9846  
9847 /*
9848  * - LGPL
9849  *
9850  * Input
9851  * 
9852  */
9853
9854 /**
9855  * @class Roo.bootstrap.TextArea
9856  * @extends Roo.bootstrap.Input
9857  * Bootstrap TextArea class
9858  * @cfg {Number} cols Specifies the visible width of a text area
9859  * @cfg {Number} rows Specifies the visible number of lines in a text area
9860  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9861  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9862  * @cfg {string} html text
9863  * 
9864  * @constructor
9865  * Create a new TextArea
9866  * @param {Object} config The config object
9867  */
9868
9869 Roo.bootstrap.TextArea = function(config){
9870     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9871    
9872 };
9873
9874 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9875      
9876     cols : false,
9877     rows : 5,
9878     readOnly : false,
9879     warp : 'soft',
9880     resize : false,
9881     value: false,
9882     html: false,
9883     
9884     getAutoCreate : function(){
9885         
9886         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9887         
9888         var id = Roo.id();
9889         
9890         var cfg = {};
9891         
9892         if(this.inputType != 'hidden'){
9893             cfg.cls = 'form-group' //input-group
9894         }
9895         
9896         var input =  {
9897             tag: 'textarea',
9898             id : id,
9899             warp : this.warp,
9900             rows : this.rows,
9901             value : this.value || '',
9902             html: this.html || '',
9903             cls : 'form-control',
9904             placeholder : this.placeholder || '' 
9905             
9906         };
9907         
9908         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9909             input.maxLength = this.maxLength;
9910         }
9911         
9912         if(this.resize){
9913             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9914         }
9915         
9916         if(this.cols){
9917             input.cols = this.cols;
9918         }
9919         
9920         if (this.readOnly) {
9921             input.readonly = true;
9922         }
9923         
9924         if (this.name) {
9925             input.name = this.name;
9926         }
9927         
9928         if (this.size) {
9929             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9930         }
9931         
9932         var settings=this;
9933         ['xs','sm','md','lg'].map(function(size){
9934             if (settings[size]) {
9935                 cfg.cls += ' col-' + size + '-' + settings[size];
9936             }
9937         });
9938         
9939         var inputblock = input;
9940         
9941         if(this.hasFeedback && !this.allowBlank){
9942             
9943             var feedback = {
9944                 tag: 'span',
9945                 cls: 'glyphicon form-control-feedback'
9946             };
9947
9948             inputblock = {
9949                 cls : 'has-feedback',
9950                 cn :  [
9951                     input,
9952                     feedback
9953                 ] 
9954             };  
9955         }
9956         
9957         
9958         if (this.before || this.after) {
9959             
9960             inputblock = {
9961                 cls : 'input-group',
9962                 cn :  [] 
9963             };
9964             if (this.before) {
9965                 inputblock.cn.push({
9966                     tag :'span',
9967                     cls : 'input-group-addon',
9968                     html : this.before
9969                 });
9970             }
9971             
9972             inputblock.cn.push(input);
9973             
9974             if(this.hasFeedback && !this.allowBlank){
9975                 inputblock.cls += ' has-feedback';
9976                 inputblock.cn.push(feedback);
9977             }
9978             
9979             if (this.after) {
9980                 inputblock.cn.push({
9981                     tag :'span',
9982                     cls : 'input-group-addon',
9983                     html : this.after
9984                 });
9985             }
9986             
9987         }
9988         
9989         if (align ==='left' && this.fieldLabel.length) {
9990             cfg.cn = [
9991                 {
9992                     tag: 'label',
9993                     'for' :  id,
9994                     cls : 'control-label',
9995                     html : this.fieldLabel
9996                 },
9997                 {
9998                     cls : "",
9999                     cn: [
10000                         inputblock
10001                     ]
10002                 }
10003
10004             ];
10005             
10006             if(this.labelWidth > 12){
10007                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10008             }
10009
10010             if(this.labelWidth < 13 && this.labelmd == 0){
10011                 this.labelmd = this.labelWidth;
10012             }
10013
10014             if(this.labellg > 0){
10015                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10016                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10017             }
10018
10019             if(this.labelmd > 0){
10020                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10021                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10022             }
10023
10024             if(this.labelsm > 0){
10025                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10026                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10027             }
10028
10029             if(this.labelxs > 0){
10030                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10031                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10032             }
10033             
10034         } else if ( this.fieldLabel.length) {
10035             cfg.cn = [
10036
10037                {
10038                    tag: 'label',
10039                    //cls : 'input-group-addon',
10040                    html : this.fieldLabel
10041
10042                },
10043
10044                inputblock
10045
10046            ];
10047
10048         } else {
10049
10050             cfg.cn = [
10051
10052                 inputblock
10053
10054             ];
10055                 
10056         }
10057         
10058         if (this.disabled) {
10059             input.disabled=true;
10060         }
10061         
10062         return cfg;
10063         
10064     },
10065     /**
10066      * return the real textarea element.
10067      */
10068     inputEl: function ()
10069     {
10070         return this.el.select('textarea.form-control',true).first();
10071     },
10072     
10073     /**
10074      * Clear any invalid styles/messages for this field
10075      */
10076     clearInvalid : function()
10077     {
10078         
10079         if(!this.el || this.preventMark){ // not rendered
10080             return;
10081         }
10082         
10083         var label = this.el.select('label', true).first();
10084         var icon = this.el.select('i.fa-star', true).first();
10085         
10086         if(label && icon){
10087             icon.remove();
10088         }
10089         
10090         this.el.removeClass(this.invalidClass);
10091         
10092         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10093             
10094             var feedback = this.el.select('.form-control-feedback', true).first();
10095             
10096             if(feedback){
10097                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10098             }
10099             
10100         }
10101         
10102         this.fireEvent('valid', this);
10103     },
10104     
10105      /**
10106      * Mark this field as valid
10107      */
10108     markValid : function()
10109     {
10110         if(!this.el  || this.preventMark){ // not rendered
10111             return;
10112         }
10113         
10114         this.el.removeClass([this.invalidClass, this.validClass]);
10115         
10116         var feedback = this.el.select('.form-control-feedback', true).first();
10117             
10118         if(feedback){
10119             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10120         }
10121
10122         if(this.disabled || this.allowBlank){
10123             return;
10124         }
10125         
10126         var label = this.el.select('label', true).first();
10127         var icon = this.el.select('i.fa-star', true).first();
10128         
10129         if(label && icon){
10130             icon.remove();
10131         }
10132         
10133         this.el.addClass(this.validClass);
10134         
10135         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10136             
10137             var feedback = this.el.select('.form-control-feedback', true).first();
10138             
10139             if(feedback){
10140                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10141                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10142             }
10143             
10144         }
10145         
10146         this.fireEvent('valid', this);
10147     },
10148     
10149      /**
10150      * Mark this field as invalid
10151      * @param {String} msg The validation message
10152      */
10153     markInvalid : function(msg)
10154     {
10155         if(!this.el  || this.preventMark){ // not rendered
10156             return;
10157         }
10158         
10159         this.el.removeClass([this.invalidClass, this.validClass]);
10160         
10161         var feedback = this.el.select('.form-control-feedback', true).first();
10162             
10163         if(feedback){
10164             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10165         }
10166
10167         if(this.disabled || this.allowBlank){
10168             return;
10169         }
10170         
10171         var label = this.el.select('label', true).first();
10172         var icon = this.el.select('i.fa-star', true).first();
10173         
10174         if(!this.getValue().length && label && !icon){
10175             this.el.createChild({
10176                 tag : 'i',
10177                 cls : 'text-danger fa fa-lg fa-star',
10178                 tooltip : 'This field is required',
10179                 style : 'margin-right:5px;'
10180             }, label, true);
10181         }
10182
10183         this.el.addClass(this.invalidClass);
10184         
10185         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10186             
10187             var feedback = this.el.select('.form-control-feedback', true).first();
10188             
10189             if(feedback){
10190                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10191                 
10192                 if(this.getValue().length || this.forceFeedback){
10193                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10194                 }
10195                 
10196             }
10197             
10198         }
10199         
10200         this.fireEvent('invalid', this, msg);
10201     }
10202 });
10203
10204  
10205 /*
10206  * - LGPL
10207  *
10208  * trigger field - base class for combo..
10209  * 
10210  */
10211  
10212 /**
10213  * @class Roo.bootstrap.TriggerField
10214  * @extends Roo.bootstrap.Input
10215  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10216  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10217  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10218  * for which you can provide a custom implementation.  For example:
10219  * <pre><code>
10220 var trigger = new Roo.bootstrap.TriggerField();
10221 trigger.onTriggerClick = myTriggerFn;
10222 trigger.applyTo('my-field');
10223 </code></pre>
10224  *
10225  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10226  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10227  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10228  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10229  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10230
10231  * @constructor
10232  * Create a new TriggerField.
10233  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10234  * to the base TextField)
10235  */
10236 Roo.bootstrap.TriggerField = function(config){
10237     this.mimicing = false;
10238     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10239 };
10240
10241 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10242     /**
10243      * @cfg {String} triggerClass A CSS class to apply to the trigger
10244      */
10245      /**
10246      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10247      */
10248     hideTrigger:false,
10249
10250     /**
10251      * @cfg {Boolean} removable (true|false) special filter default false
10252      */
10253     removable : false,
10254     
10255     /** @cfg {Boolean} grow @hide */
10256     /** @cfg {Number} growMin @hide */
10257     /** @cfg {Number} growMax @hide */
10258
10259     /**
10260      * @hide 
10261      * @method
10262      */
10263     autoSize: Roo.emptyFn,
10264     // private
10265     monitorTab : true,
10266     // private
10267     deferHeight : true,
10268
10269     
10270     actionMode : 'wrap',
10271     
10272     caret : false,
10273     
10274     
10275     getAutoCreate : function(){
10276        
10277         var align = this.labelAlign || this.parentLabelAlign();
10278         
10279         var id = Roo.id();
10280         
10281         var cfg = {
10282             cls: 'form-group' //input-group
10283         };
10284         
10285         
10286         var input =  {
10287             tag: 'input',
10288             id : id,
10289             type : this.inputType,
10290             cls : 'form-control',
10291             autocomplete: 'new-password',
10292             placeholder : this.placeholder || '' 
10293             
10294         };
10295         if (this.name) {
10296             input.name = this.name;
10297         }
10298         if (this.size) {
10299             input.cls += ' input-' + this.size;
10300         }
10301         
10302         if (this.disabled) {
10303             input.disabled=true;
10304         }
10305         
10306         var inputblock = input;
10307         
10308         if(this.hasFeedback && !this.allowBlank){
10309             
10310             var feedback = {
10311                 tag: 'span',
10312                 cls: 'glyphicon form-control-feedback'
10313             };
10314             
10315             if(this.removable && !this.editable && !this.tickable){
10316                 inputblock = {
10317                     cls : 'has-feedback',
10318                     cn :  [
10319                         inputblock,
10320                         {
10321                             tag: 'button',
10322                             html : 'x',
10323                             cls : 'roo-combo-removable-btn close'
10324                         },
10325                         feedback
10326                     ] 
10327                 };
10328             } else {
10329                 inputblock = {
10330                     cls : 'has-feedback',
10331                     cn :  [
10332                         inputblock,
10333                         feedback
10334                     ] 
10335                 };
10336             }
10337
10338         } else {
10339             if(this.removable && !this.editable && !this.tickable){
10340                 inputblock = {
10341                     cls : 'roo-removable',
10342                     cn :  [
10343                         inputblock,
10344                         {
10345                             tag: 'button',
10346                             html : 'x',
10347                             cls : 'roo-combo-removable-btn close'
10348                         }
10349                     ] 
10350                 };
10351             }
10352         }
10353         
10354         if (this.before || this.after) {
10355             
10356             inputblock = {
10357                 cls : 'input-group',
10358                 cn :  [] 
10359             };
10360             if (this.before) {
10361                 inputblock.cn.push({
10362                     tag :'span',
10363                     cls : 'input-group-addon input-group-prepend input-group-text',
10364                     html : this.before
10365                 });
10366             }
10367             
10368             inputblock.cn.push(input);
10369             
10370             if(this.hasFeedback && !this.allowBlank){
10371                 inputblock.cls += ' has-feedback';
10372                 inputblock.cn.push(feedback);
10373             }
10374             
10375             if (this.after) {
10376                 inputblock.cn.push({
10377                     tag :'span',
10378                     cls : 'input-group-addon input-group-append input-group-text',
10379                     html : this.after
10380                 });
10381             }
10382             
10383         };
10384         
10385       
10386         
10387         var ibwrap = inputblock;
10388         
10389         if(this.multiple){
10390             ibwrap = {
10391                 tag: 'ul',
10392                 cls: 'roo-select2-choices',
10393                 cn:[
10394                     {
10395                         tag: 'li',
10396                         cls: 'roo-select2-search-field',
10397                         cn: [
10398
10399                             inputblock
10400                         ]
10401                     }
10402                 ]
10403             };
10404                 
10405         }
10406         
10407         var combobox = {
10408             cls: 'roo-select2-container input-group',
10409             cn: [
10410                  {
10411                     tag: 'input',
10412                     type : 'hidden',
10413                     cls: 'form-hidden-field'
10414                 },
10415                 ibwrap
10416             ]
10417         };
10418         
10419         if(!this.multiple && this.showToggleBtn){
10420             
10421             var caret = {
10422                         tag: 'span',
10423                         cls: 'caret'
10424              };
10425             if (this.caret != false) {
10426                 caret = {
10427                      tag: 'i',
10428                      cls: 'fa fa-' + this.caret
10429                 };
10430                 
10431             }
10432             
10433             combobox.cn.push({
10434                 tag :'span',
10435                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10436                 cn : [
10437                     caret,
10438                     {
10439                         tag: 'span',
10440                         cls: 'combobox-clear',
10441                         cn  : [
10442                             {
10443                                 tag : 'i',
10444                                 cls: 'icon-remove'
10445                             }
10446                         ]
10447                     }
10448                 ]
10449
10450             })
10451         }
10452         
10453         if(this.multiple){
10454             combobox.cls += ' roo-select2-container-multi';
10455         }
10456          var indicator = {
10457             tag : 'i',
10458             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10459             tooltip : 'This field is required'
10460         };
10461         if (Roo.bootstrap.version == 4) {
10462             indicator = {
10463                 tag : 'i',
10464                 style : 'display:none'
10465             };
10466         }
10467         
10468         
10469         if (align ==='left' && this.fieldLabel.length) {
10470             
10471             cfg.cls += ' roo-form-group-label-left row';
10472
10473             cfg.cn = [
10474                 indicator,
10475                 {
10476                     tag: 'label',
10477                     'for' :  id,
10478                     cls : 'control-label',
10479                     html : this.fieldLabel
10480
10481                 },
10482                 {
10483                     cls : "", 
10484                     cn: [
10485                         combobox
10486                     ]
10487                 }
10488
10489             ];
10490             
10491             var labelCfg = cfg.cn[1];
10492             var contentCfg = cfg.cn[2];
10493             
10494             if(this.indicatorpos == 'right'){
10495                 cfg.cn = [
10496                     {
10497                         tag: 'label',
10498                         'for' :  id,
10499                         cls : 'control-label',
10500                         cn : [
10501                             {
10502                                 tag : 'span',
10503                                 html : this.fieldLabel
10504                             },
10505                             indicator
10506                         ]
10507                     },
10508                     {
10509                         cls : "", 
10510                         cn: [
10511                             combobox
10512                         ]
10513                     }
10514
10515                 ];
10516                 
10517                 labelCfg = cfg.cn[0];
10518                 contentCfg = cfg.cn[1];
10519             }
10520             
10521             if(this.labelWidth > 12){
10522                 labelCfg.style = "width: " + this.labelWidth + 'px';
10523             }
10524             
10525             if(this.labelWidth < 13 && this.labelmd == 0){
10526                 this.labelmd = this.labelWidth;
10527             }
10528             
10529             if(this.labellg > 0){
10530                 labelCfg.cls += ' col-lg-' + this.labellg;
10531                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10532             }
10533             
10534             if(this.labelmd > 0){
10535                 labelCfg.cls += ' col-md-' + this.labelmd;
10536                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10537             }
10538             
10539             if(this.labelsm > 0){
10540                 labelCfg.cls += ' col-sm-' + this.labelsm;
10541                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10542             }
10543             
10544             if(this.labelxs > 0){
10545                 labelCfg.cls += ' col-xs-' + this.labelxs;
10546                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10547             }
10548             
10549         } else if ( this.fieldLabel.length) {
10550 //                Roo.log(" label");
10551             cfg.cn = [
10552                 indicator,
10553                {
10554                    tag: 'label',
10555                    //cls : 'input-group-addon',
10556                    html : this.fieldLabel
10557
10558                },
10559
10560                combobox
10561
10562             ];
10563             
10564             if(this.indicatorpos == 'right'){
10565                 
10566                 cfg.cn = [
10567                     {
10568                        tag: 'label',
10569                        cn : [
10570                            {
10571                                tag : 'span',
10572                                html : this.fieldLabel
10573                            },
10574                            indicator
10575                        ]
10576
10577                     },
10578                     combobox
10579
10580                 ];
10581
10582             }
10583
10584         } else {
10585             
10586 //                Roo.log(" no label && no align");
10587                 cfg = combobox
10588                      
10589                 
10590         }
10591         
10592         var settings=this;
10593         ['xs','sm','md','lg'].map(function(size){
10594             if (settings[size]) {
10595                 cfg.cls += ' col-' + size + '-' + settings[size];
10596             }
10597         });
10598         
10599         return cfg;
10600         
10601     },
10602     
10603     
10604     
10605     // private
10606     onResize : function(w, h){
10607 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10608 //        if(typeof w == 'number'){
10609 //            var x = w - this.trigger.getWidth();
10610 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10611 //            this.trigger.setStyle('left', x+'px');
10612 //        }
10613     },
10614
10615     // private
10616     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10617
10618     // private
10619     getResizeEl : function(){
10620         return this.inputEl();
10621     },
10622
10623     // private
10624     getPositionEl : function(){
10625         return this.inputEl();
10626     },
10627
10628     // private
10629     alignErrorIcon : function(){
10630         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10631     },
10632
10633     // private
10634     initEvents : function(){
10635         
10636         this.createList();
10637         
10638         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10639         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10640         if(!this.multiple && this.showToggleBtn){
10641             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10642             if(this.hideTrigger){
10643                 this.trigger.setDisplayed(false);
10644             }
10645             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10646         }
10647         
10648         if(this.multiple){
10649             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10650         }
10651         
10652         if(this.removable && !this.editable && !this.tickable){
10653             var close = this.closeTriggerEl();
10654             
10655             if(close){
10656                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10657                 close.on('click', this.removeBtnClick, this, close);
10658             }
10659         }
10660         
10661         //this.trigger.addClassOnOver('x-form-trigger-over');
10662         //this.trigger.addClassOnClick('x-form-trigger-click');
10663         
10664         //if(!this.width){
10665         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10666         //}
10667     },
10668     
10669     closeTriggerEl : function()
10670     {
10671         var close = this.el.select('.roo-combo-removable-btn', true).first();
10672         return close ? close : false;
10673     },
10674     
10675     removeBtnClick : function(e, h, el)
10676     {
10677         e.preventDefault();
10678         
10679         if(this.fireEvent("remove", this) !== false){
10680             this.reset();
10681             this.fireEvent("afterremove", this)
10682         }
10683     },
10684     
10685     createList : function()
10686     {
10687         this.list = Roo.get(document.body).createChild({
10688             tag: 'ul',
10689             cls: 'typeahead typeahead-long dropdown-menu',
10690             style: 'display:none'
10691         });
10692         
10693         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10694         
10695     },
10696
10697     // private
10698     initTrigger : function(){
10699        
10700     },
10701
10702     // private
10703     onDestroy : function(){
10704         if(this.trigger){
10705             this.trigger.removeAllListeners();
10706           //  this.trigger.remove();
10707         }
10708         //if(this.wrap){
10709         //    this.wrap.remove();
10710         //}
10711         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10712     },
10713
10714     // private
10715     onFocus : function(){
10716         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10717         /*
10718         if(!this.mimicing){
10719             this.wrap.addClass('x-trigger-wrap-focus');
10720             this.mimicing = true;
10721             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10722             if(this.monitorTab){
10723                 this.el.on("keydown", this.checkTab, this);
10724             }
10725         }
10726         */
10727     },
10728
10729     // private
10730     checkTab : function(e){
10731         if(e.getKey() == e.TAB){
10732             this.triggerBlur();
10733         }
10734     },
10735
10736     // private
10737     onBlur : function(){
10738         // do nothing
10739     },
10740
10741     // private
10742     mimicBlur : function(e, t){
10743         /*
10744         if(!this.wrap.contains(t) && this.validateBlur()){
10745             this.triggerBlur();
10746         }
10747         */
10748     },
10749
10750     // private
10751     triggerBlur : function(){
10752         this.mimicing = false;
10753         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10754         if(this.monitorTab){
10755             this.el.un("keydown", this.checkTab, this);
10756         }
10757         //this.wrap.removeClass('x-trigger-wrap-focus');
10758         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10759     },
10760
10761     // private
10762     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10763     validateBlur : function(e, t){
10764         return true;
10765     },
10766
10767     // private
10768     onDisable : function(){
10769         this.inputEl().dom.disabled = true;
10770         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10771         //if(this.wrap){
10772         //    this.wrap.addClass('x-item-disabled');
10773         //}
10774     },
10775
10776     // private
10777     onEnable : function(){
10778         this.inputEl().dom.disabled = false;
10779         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10780         //if(this.wrap){
10781         //    this.el.removeClass('x-item-disabled');
10782         //}
10783     },
10784
10785     // private
10786     onShow : function(){
10787         var ae = this.getActionEl();
10788         
10789         if(ae){
10790             ae.dom.style.display = '';
10791             ae.dom.style.visibility = 'visible';
10792         }
10793     },
10794
10795     // private
10796     
10797     onHide : function(){
10798         var ae = this.getActionEl();
10799         ae.dom.style.display = 'none';
10800     },
10801
10802     /**
10803      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10804      * by an implementing function.
10805      * @method
10806      * @param {EventObject} e
10807      */
10808     onTriggerClick : Roo.emptyFn
10809 });
10810  /*
10811  * Based on:
10812  * Ext JS Library 1.1.1
10813  * Copyright(c) 2006-2007, Ext JS, LLC.
10814  *
10815  * Originally Released Under LGPL - original licence link has changed is not relivant.
10816  *
10817  * Fork - LGPL
10818  * <script type="text/javascript">
10819  */
10820
10821
10822 /**
10823  * @class Roo.data.SortTypes
10824  * @singleton
10825  * Defines the default sorting (casting?) comparison functions used when sorting data.
10826  */
10827 Roo.data.SortTypes = {
10828     /**
10829      * Default sort that does nothing
10830      * @param {Mixed} s The value being converted
10831      * @return {Mixed} The comparison value
10832      */
10833     none : function(s){
10834         return s;
10835     },
10836     
10837     /**
10838      * The regular expression used to strip tags
10839      * @type {RegExp}
10840      * @property
10841      */
10842     stripTagsRE : /<\/?[^>]+>/gi,
10843     
10844     /**
10845      * Strips all HTML tags to sort on text only
10846      * @param {Mixed} s The value being converted
10847      * @return {String} The comparison value
10848      */
10849     asText : function(s){
10850         return String(s).replace(this.stripTagsRE, "");
10851     },
10852     
10853     /**
10854      * Strips all HTML tags to sort on text only - Case insensitive
10855      * @param {Mixed} s The value being converted
10856      * @return {String} The comparison value
10857      */
10858     asUCText : function(s){
10859         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10860     },
10861     
10862     /**
10863      * Case insensitive string
10864      * @param {Mixed} s The value being converted
10865      * @return {String} The comparison value
10866      */
10867     asUCString : function(s) {
10868         return String(s).toUpperCase();
10869     },
10870     
10871     /**
10872      * Date sorting
10873      * @param {Mixed} s The value being converted
10874      * @return {Number} The comparison value
10875      */
10876     asDate : function(s) {
10877         if(!s){
10878             return 0;
10879         }
10880         if(s instanceof Date){
10881             return s.getTime();
10882         }
10883         return Date.parse(String(s));
10884     },
10885     
10886     /**
10887      * Float sorting
10888      * @param {Mixed} s The value being converted
10889      * @return {Float} The comparison value
10890      */
10891     asFloat : function(s) {
10892         var val = parseFloat(String(s).replace(/,/g, ""));
10893         if(isNaN(val)) {
10894             val = 0;
10895         }
10896         return val;
10897     },
10898     
10899     /**
10900      * Integer sorting
10901      * @param {Mixed} s The value being converted
10902      * @return {Number} The comparison value
10903      */
10904     asInt : function(s) {
10905         var val = parseInt(String(s).replace(/,/g, ""));
10906         if(isNaN(val)) {
10907             val = 0;
10908         }
10909         return val;
10910     }
10911 };/*
10912  * Based on:
10913  * Ext JS Library 1.1.1
10914  * Copyright(c) 2006-2007, Ext JS, LLC.
10915  *
10916  * Originally Released Under LGPL - original licence link has changed is not relivant.
10917  *
10918  * Fork - LGPL
10919  * <script type="text/javascript">
10920  */
10921
10922 /**
10923 * @class Roo.data.Record
10924  * Instances of this class encapsulate both record <em>definition</em> information, and record
10925  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10926  * to access Records cached in an {@link Roo.data.Store} object.<br>
10927  * <p>
10928  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10929  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10930  * objects.<br>
10931  * <p>
10932  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10933  * @constructor
10934  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10935  * {@link #create}. The parameters are the same.
10936  * @param {Array} data An associative Array of data values keyed by the field name.
10937  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10938  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10939  * not specified an integer id is generated.
10940  */
10941 Roo.data.Record = function(data, id){
10942     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10943     this.data = data;
10944 };
10945
10946 /**
10947  * Generate a constructor for a specific record layout.
10948  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10949  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10950  * Each field definition object may contain the following properties: <ul>
10951  * <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,
10952  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10953  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10954  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10955  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10956  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10957  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10958  * this may be omitted.</p></li>
10959  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10960  * <ul><li>auto (Default, implies no conversion)</li>
10961  * <li>string</li>
10962  * <li>int</li>
10963  * <li>float</li>
10964  * <li>boolean</li>
10965  * <li>date</li></ul></p></li>
10966  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10967  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10968  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10969  * by the Reader into an object that will be stored in the Record. It is passed the
10970  * following parameters:<ul>
10971  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10972  * </ul></p></li>
10973  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10974  * </ul>
10975  * <br>usage:<br><pre><code>
10976 var TopicRecord = Roo.data.Record.create(
10977     {name: 'title', mapping: 'topic_title'},
10978     {name: 'author', mapping: 'username'},
10979     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10980     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10981     {name: 'lastPoster', mapping: 'user2'},
10982     {name: 'excerpt', mapping: 'post_text'}
10983 );
10984
10985 var myNewRecord = new TopicRecord({
10986     title: 'Do my job please',
10987     author: 'noobie',
10988     totalPosts: 1,
10989     lastPost: new Date(),
10990     lastPoster: 'Animal',
10991     excerpt: 'No way dude!'
10992 });
10993 myStore.add(myNewRecord);
10994 </code></pre>
10995  * @method create
10996  * @static
10997  */
10998 Roo.data.Record.create = function(o){
10999     var f = function(){
11000         f.superclass.constructor.apply(this, arguments);
11001     };
11002     Roo.extend(f, Roo.data.Record);
11003     var p = f.prototype;
11004     p.fields = new Roo.util.MixedCollection(false, function(field){
11005         return field.name;
11006     });
11007     for(var i = 0, len = o.length; i < len; i++){
11008         p.fields.add(new Roo.data.Field(o[i]));
11009     }
11010     f.getField = function(name){
11011         return p.fields.get(name);  
11012     };
11013     return f;
11014 };
11015
11016 Roo.data.Record.AUTO_ID = 1000;
11017 Roo.data.Record.EDIT = 'edit';
11018 Roo.data.Record.REJECT = 'reject';
11019 Roo.data.Record.COMMIT = 'commit';
11020
11021 Roo.data.Record.prototype = {
11022     /**
11023      * Readonly flag - true if this record has been modified.
11024      * @type Boolean
11025      */
11026     dirty : false,
11027     editing : false,
11028     error: null,
11029     modified: null,
11030
11031     // private
11032     join : function(store){
11033         this.store = store;
11034     },
11035
11036     /**
11037      * Set the named field to the specified value.
11038      * @param {String} name The name of the field to set.
11039      * @param {Object} value The value to set the field to.
11040      */
11041     set : function(name, value){
11042         if(this.data[name] == value){
11043             return;
11044         }
11045         this.dirty = true;
11046         if(!this.modified){
11047             this.modified = {};
11048         }
11049         if(typeof this.modified[name] == 'undefined'){
11050             this.modified[name] = this.data[name];
11051         }
11052         this.data[name] = value;
11053         if(!this.editing && this.store){
11054             this.store.afterEdit(this);
11055         }       
11056     },
11057
11058     /**
11059      * Get the value of the named field.
11060      * @param {String} name The name of the field to get the value of.
11061      * @return {Object} The value of the field.
11062      */
11063     get : function(name){
11064         return this.data[name]; 
11065     },
11066
11067     // private
11068     beginEdit : function(){
11069         this.editing = true;
11070         this.modified = {}; 
11071     },
11072
11073     // private
11074     cancelEdit : function(){
11075         this.editing = false;
11076         delete this.modified;
11077     },
11078
11079     // private
11080     endEdit : function(){
11081         this.editing = false;
11082         if(this.dirty && this.store){
11083             this.store.afterEdit(this);
11084         }
11085     },
11086
11087     /**
11088      * Usually called by the {@link Roo.data.Store} which owns the Record.
11089      * Rejects all changes made to the Record since either creation, or the last commit operation.
11090      * Modified fields are reverted to their original values.
11091      * <p>
11092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11093      * of reject operations.
11094      */
11095     reject : function(){
11096         var m = this.modified;
11097         for(var n in m){
11098             if(typeof m[n] != "function"){
11099                 this.data[n] = m[n];
11100             }
11101         }
11102         this.dirty = false;
11103         delete this.modified;
11104         this.editing = false;
11105         if(this.store){
11106             this.store.afterReject(this);
11107         }
11108     },
11109
11110     /**
11111      * Usually called by the {@link Roo.data.Store} which owns the Record.
11112      * Commits all changes made to the Record since either creation, or the last commit operation.
11113      * <p>
11114      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11115      * of commit operations.
11116      */
11117     commit : function(){
11118         this.dirty = false;
11119         delete this.modified;
11120         this.editing = false;
11121         if(this.store){
11122             this.store.afterCommit(this);
11123         }
11124     },
11125
11126     // private
11127     hasError : function(){
11128         return this.error != null;
11129     },
11130
11131     // private
11132     clearError : function(){
11133         this.error = null;
11134     },
11135
11136     /**
11137      * Creates a copy of this record.
11138      * @param {String} id (optional) A new record id if you don't want to use this record's id
11139      * @return {Record}
11140      */
11141     copy : function(newId) {
11142         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11143     }
11144 };/*
11145  * Based on:
11146  * Ext JS Library 1.1.1
11147  * Copyright(c) 2006-2007, Ext JS, LLC.
11148  *
11149  * Originally Released Under LGPL - original licence link has changed is not relivant.
11150  *
11151  * Fork - LGPL
11152  * <script type="text/javascript">
11153  */
11154
11155
11156
11157 /**
11158  * @class Roo.data.Store
11159  * @extends Roo.util.Observable
11160  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11161  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11162  * <p>
11163  * 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
11164  * has no knowledge of the format of the data returned by the Proxy.<br>
11165  * <p>
11166  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11167  * instances from the data object. These records are cached and made available through accessor functions.
11168  * @constructor
11169  * Creates a new Store.
11170  * @param {Object} config A config object containing the objects needed for the Store to access data,
11171  * and read the data into Records.
11172  */
11173 Roo.data.Store = function(config){
11174     this.data = new Roo.util.MixedCollection(false);
11175     this.data.getKey = function(o){
11176         return o.id;
11177     };
11178     this.baseParams = {};
11179     // private
11180     this.paramNames = {
11181         "start" : "start",
11182         "limit" : "limit",
11183         "sort" : "sort",
11184         "dir" : "dir",
11185         "multisort" : "_multisort"
11186     };
11187
11188     if(config && config.data){
11189         this.inlineData = config.data;
11190         delete config.data;
11191     }
11192
11193     Roo.apply(this, config);
11194     
11195     if(this.reader){ // reader passed
11196         this.reader = Roo.factory(this.reader, Roo.data);
11197         this.reader.xmodule = this.xmodule || false;
11198         if(!this.recordType){
11199             this.recordType = this.reader.recordType;
11200         }
11201         if(this.reader.onMetaChange){
11202             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11203         }
11204     }
11205
11206     if(this.recordType){
11207         this.fields = this.recordType.prototype.fields;
11208     }
11209     this.modified = [];
11210
11211     this.addEvents({
11212         /**
11213          * @event datachanged
11214          * Fires when the data cache has changed, and a widget which is using this Store
11215          * as a Record cache should refresh its view.
11216          * @param {Store} this
11217          */
11218         datachanged : true,
11219         /**
11220          * @event metachange
11221          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11222          * @param {Store} this
11223          * @param {Object} meta The JSON metadata
11224          */
11225         metachange : true,
11226         /**
11227          * @event add
11228          * Fires when Records have been added to the Store
11229          * @param {Store} this
11230          * @param {Roo.data.Record[]} records The array of Records added
11231          * @param {Number} index The index at which the record(s) were added
11232          */
11233         add : true,
11234         /**
11235          * @event remove
11236          * Fires when a Record has been removed from the Store
11237          * @param {Store} this
11238          * @param {Roo.data.Record} record The Record that was removed
11239          * @param {Number} index The index at which the record was removed
11240          */
11241         remove : true,
11242         /**
11243          * @event update
11244          * Fires when a Record has been updated
11245          * @param {Store} this
11246          * @param {Roo.data.Record} record The Record that was updated
11247          * @param {String} operation The update operation being performed.  Value may be one of:
11248          * <pre><code>
11249  Roo.data.Record.EDIT
11250  Roo.data.Record.REJECT
11251  Roo.data.Record.COMMIT
11252          * </code></pre>
11253          */
11254         update : true,
11255         /**
11256          * @event clear
11257          * Fires when the data cache has been cleared.
11258          * @param {Store} this
11259          */
11260         clear : true,
11261         /**
11262          * @event beforeload
11263          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11264          * the load action will be canceled.
11265          * @param {Store} this
11266          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11267          */
11268         beforeload : true,
11269         /**
11270          * @event beforeloadadd
11271          * Fires after a new set of Records has been loaded.
11272          * @param {Store} this
11273          * @param {Roo.data.Record[]} records The Records that were loaded
11274          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11275          */
11276         beforeloadadd : true,
11277         /**
11278          * @event load
11279          * Fires after a new set of Records has been loaded, before they are added to the store.
11280          * @param {Store} this
11281          * @param {Roo.data.Record[]} records The Records that were loaded
11282          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11283          * @params {Object} return from reader
11284          */
11285         load : true,
11286         /**
11287          * @event loadexception
11288          * Fires if an exception occurs in the Proxy during loading.
11289          * Called with the signature of the Proxy's "loadexception" event.
11290          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11291          * 
11292          * @param {Proxy} 
11293          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11294          * @param {Object} load options 
11295          * @param {Object} jsonData from your request (normally this contains the Exception)
11296          */
11297         loadexception : true
11298     });
11299     
11300     if(this.proxy){
11301         this.proxy = Roo.factory(this.proxy, Roo.data);
11302         this.proxy.xmodule = this.xmodule || false;
11303         this.relayEvents(this.proxy,  ["loadexception"]);
11304     }
11305     this.sortToggle = {};
11306     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11307
11308     Roo.data.Store.superclass.constructor.call(this);
11309
11310     if(this.inlineData){
11311         this.loadData(this.inlineData);
11312         delete this.inlineData;
11313     }
11314 };
11315
11316 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11317      /**
11318     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11319     * without a remote query - used by combo/forms at present.
11320     */
11321     
11322     /**
11323     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11324     */
11325     /**
11326     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11327     */
11328     /**
11329     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11330     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11331     */
11332     /**
11333     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11334     * on any HTTP request
11335     */
11336     /**
11337     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11338     */
11339     /**
11340     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11341     */
11342     multiSort: false,
11343     /**
11344     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11345     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11346     */
11347     remoteSort : false,
11348
11349     /**
11350     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11351      * loaded or when a record is removed. (defaults to false).
11352     */
11353     pruneModifiedRecords : false,
11354
11355     // private
11356     lastOptions : null,
11357
11358     /**
11359      * Add Records to the Store and fires the add event.
11360      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11361      */
11362     add : function(records){
11363         records = [].concat(records);
11364         for(var i = 0, len = records.length; i < len; i++){
11365             records[i].join(this);
11366         }
11367         var index = this.data.length;
11368         this.data.addAll(records);
11369         this.fireEvent("add", this, records, index);
11370     },
11371
11372     /**
11373      * Remove a Record from the Store and fires the remove event.
11374      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11375      */
11376     remove : function(record){
11377         var index = this.data.indexOf(record);
11378         this.data.removeAt(index);
11379  
11380         if(this.pruneModifiedRecords){
11381             this.modified.remove(record);
11382         }
11383         this.fireEvent("remove", this, record, index);
11384     },
11385
11386     /**
11387      * Remove all Records from the Store and fires the clear event.
11388      */
11389     removeAll : function(){
11390         this.data.clear();
11391         if(this.pruneModifiedRecords){
11392             this.modified = [];
11393         }
11394         this.fireEvent("clear", this);
11395     },
11396
11397     /**
11398      * Inserts Records to the Store at the given index and fires the add event.
11399      * @param {Number} index The start index at which to insert the passed Records.
11400      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11401      */
11402     insert : function(index, records){
11403         records = [].concat(records);
11404         for(var i = 0, len = records.length; i < len; i++){
11405             this.data.insert(index, records[i]);
11406             records[i].join(this);
11407         }
11408         this.fireEvent("add", this, records, index);
11409     },
11410
11411     /**
11412      * Get the index within the cache of the passed Record.
11413      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11414      * @return {Number} The index of the passed Record. Returns -1 if not found.
11415      */
11416     indexOf : function(record){
11417         return this.data.indexOf(record);
11418     },
11419
11420     /**
11421      * Get the index within the cache of the Record with the passed id.
11422      * @param {String} id The id of the Record to find.
11423      * @return {Number} The index of the Record. Returns -1 if not found.
11424      */
11425     indexOfId : function(id){
11426         return this.data.indexOfKey(id);
11427     },
11428
11429     /**
11430      * Get the Record with the specified id.
11431      * @param {String} id The id of the Record to find.
11432      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11433      */
11434     getById : function(id){
11435         return this.data.key(id);
11436     },
11437
11438     /**
11439      * Get the Record at the specified index.
11440      * @param {Number} index The index of the Record to find.
11441      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11442      */
11443     getAt : function(index){
11444         return this.data.itemAt(index);
11445     },
11446
11447     /**
11448      * Returns a range of Records between specified indices.
11449      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11450      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11451      * @return {Roo.data.Record[]} An array of Records
11452      */
11453     getRange : function(start, end){
11454         return this.data.getRange(start, end);
11455     },
11456
11457     // private
11458     storeOptions : function(o){
11459         o = Roo.apply({}, o);
11460         delete o.callback;
11461         delete o.scope;
11462         this.lastOptions = o;
11463     },
11464
11465     /**
11466      * Loads the Record cache from the configured Proxy using the configured Reader.
11467      * <p>
11468      * If using remote paging, then the first load call must specify the <em>start</em>
11469      * and <em>limit</em> properties in the options.params property to establish the initial
11470      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11471      * <p>
11472      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11473      * and this call will return before the new data has been loaded. Perform any post-processing
11474      * in a callback function, or in a "load" event handler.</strong>
11475      * <p>
11476      * @param {Object} options An object containing properties which control loading options:<ul>
11477      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11478      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11479      * passed the following arguments:<ul>
11480      * <li>r : Roo.data.Record[]</li>
11481      * <li>options: Options object from the load call</li>
11482      * <li>success: Boolean success indicator</li></ul></li>
11483      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11484      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11485      * </ul>
11486      */
11487     load : function(options){
11488         options = options || {};
11489         if(this.fireEvent("beforeload", this, options) !== false){
11490             this.storeOptions(options);
11491             var p = Roo.apply(options.params || {}, this.baseParams);
11492             // if meta was not loaded from remote source.. try requesting it.
11493             if (!this.reader.metaFromRemote) {
11494                 p._requestMeta = 1;
11495             }
11496             if(this.sortInfo && this.remoteSort){
11497                 var pn = this.paramNames;
11498                 p[pn["sort"]] = this.sortInfo.field;
11499                 p[pn["dir"]] = this.sortInfo.direction;
11500             }
11501             if (this.multiSort) {
11502                 var pn = this.paramNames;
11503                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11504             }
11505             
11506             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11507         }
11508     },
11509
11510     /**
11511      * Reloads the Record cache from the configured Proxy using the configured Reader and
11512      * the options from the last load operation performed.
11513      * @param {Object} options (optional) An object containing properties which may override the options
11514      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11515      * the most recently used options are reused).
11516      */
11517     reload : function(options){
11518         this.load(Roo.applyIf(options||{}, this.lastOptions));
11519     },
11520
11521     // private
11522     // Called as a callback by the Reader during a load operation.
11523     loadRecords : function(o, options, success){
11524         if(!o || success === false){
11525             if(success !== false){
11526                 this.fireEvent("load", this, [], options, o);
11527             }
11528             if(options.callback){
11529                 options.callback.call(options.scope || this, [], options, false);
11530             }
11531             return;
11532         }
11533         // if data returned failure - throw an exception.
11534         if (o.success === false) {
11535             // show a message if no listener is registered.
11536             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11537                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11538             }
11539             // loadmask wil be hooked into this..
11540             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11541             return;
11542         }
11543         var r = o.records, t = o.totalRecords || r.length;
11544         
11545         this.fireEvent("beforeloadadd", this, r, options, o);
11546         
11547         if(!options || options.add !== true){
11548             if(this.pruneModifiedRecords){
11549                 this.modified = [];
11550             }
11551             for(var i = 0, len = r.length; i < len; i++){
11552                 r[i].join(this);
11553             }
11554             if(this.snapshot){
11555                 this.data = this.snapshot;
11556                 delete this.snapshot;
11557             }
11558             this.data.clear();
11559             this.data.addAll(r);
11560             this.totalLength = t;
11561             this.applySort();
11562             this.fireEvent("datachanged", this);
11563         }else{
11564             this.totalLength = Math.max(t, this.data.length+r.length);
11565             this.add(r);
11566         }
11567         
11568         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11569                 
11570             var e = new Roo.data.Record({});
11571
11572             e.set(this.parent.displayField, this.parent.emptyTitle);
11573             e.set(this.parent.valueField, '');
11574
11575             this.insert(0, e);
11576         }
11577             
11578         this.fireEvent("load", this, r, options, o);
11579         if(options.callback){
11580             options.callback.call(options.scope || this, r, options, true);
11581         }
11582     },
11583
11584
11585     /**
11586      * Loads data from a passed data block. A Reader which understands the format of the data
11587      * must have been configured in the constructor.
11588      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11589      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11590      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11591      */
11592     loadData : function(o, append){
11593         var r = this.reader.readRecords(o);
11594         this.loadRecords(r, {add: append}, true);
11595     },
11596
11597     /**
11598      * Gets the number of cached records.
11599      * <p>
11600      * <em>If using paging, this may not be the total size of the dataset. If the data object
11601      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11602      * the data set size</em>
11603      */
11604     getCount : function(){
11605         return this.data.length || 0;
11606     },
11607
11608     /**
11609      * Gets the total number of records in the dataset as returned by the server.
11610      * <p>
11611      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11612      * the dataset size</em>
11613      */
11614     getTotalCount : function(){
11615         return this.totalLength || 0;
11616     },
11617
11618     /**
11619      * Returns the sort state of the Store as an object with two properties:
11620      * <pre><code>
11621  field {String} The name of the field by which the Records are sorted
11622  direction {String} The sort order, "ASC" or "DESC"
11623      * </code></pre>
11624      */
11625     getSortState : function(){
11626         return this.sortInfo;
11627     },
11628
11629     // private
11630     applySort : function(){
11631         if(this.sortInfo && !this.remoteSort){
11632             var s = this.sortInfo, f = s.field;
11633             var st = this.fields.get(f).sortType;
11634             var fn = function(r1, r2){
11635                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11636                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11637             };
11638             this.data.sort(s.direction, fn);
11639             if(this.snapshot && this.snapshot != this.data){
11640                 this.snapshot.sort(s.direction, fn);
11641             }
11642         }
11643     },
11644
11645     /**
11646      * Sets the default sort column and order to be used by the next load operation.
11647      * @param {String} fieldName The name of the field to sort by.
11648      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11649      */
11650     setDefaultSort : function(field, dir){
11651         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11652     },
11653
11654     /**
11655      * Sort the Records.
11656      * If remote sorting is used, the sort is performed on the server, and the cache is
11657      * reloaded. If local sorting is used, the cache is sorted internally.
11658      * @param {String} fieldName The name of the field to sort by.
11659      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11660      */
11661     sort : function(fieldName, dir){
11662         var f = this.fields.get(fieldName);
11663         if(!dir){
11664             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11665             
11666             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11667                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11668             }else{
11669                 dir = f.sortDir;
11670             }
11671         }
11672         this.sortToggle[f.name] = dir;
11673         this.sortInfo = {field: f.name, direction: dir};
11674         if(!this.remoteSort){
11675             this.applySort();
11676             this.fireEvent("datachanged", this);
11677         }else{
11678             this.load(this.lastOptions);
11679         }
11680     },
11681
11682     /**
11683      * Calls the specified function for each of the Records in the cache.
11684      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11685      * Returning <em>false</em> aborts and exits the iteration.
11686      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11687      */
11688     each : function(fn, scope){
11689         this.data.each(fn, scope);
11690     },
11691
11692     /**
11693      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11694      * (e.g., during paging).
11695      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11696      */
11697     getModifiedRecords : function(){
11698         return this.modified;
11699     },
11700
11701     // private
11702     createFilterFn : function(property, value, anyMatch){
11703         if(!value.exec){ // not a regex
11704             value = String(value);
11705             if(value.length == 0){
11706                 return false;
11707             }
11708             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11709         }
11710         return function(r){
11711             return value.test(r.data[property]);
11712         };
11713     },
11714
11715     /**
11716      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11717      * @param {String} property A field on your records
11718      * @param {Number} start The record index to start at (defaults to 0)
11719      * @param {Number} end The last record index to include (defaults to length - 1)
11720      * @return {Number} The sum
11721      */
11722     sum : function(property, start, end){
11723         var rs = this.data.items, v = 0;
11724         start = start || 0;
11725         end = (end || end === 0) ? end : rs.length-1;
11726
11727         for(var i = start; i <= end; i++){
11728             v += (rs[i].data[property] || 0);
11729         }
11730         return v;
11731     },
11732
11733     /**
11734      * Filter the records by a specified property.
11735      * @param {String} field A field on your records
11736      * @param {String/RegExp} value Either a string that the field
11737      * should start with or a RegExp to test against the field
11738      * @param {Boolean} anyMatch True to match any part not just the beginning
11739      */
11740     filter : function(property, value, anyMatch){
11741         var fn = this.createFilterFn(property, value, anyMatch);
11742         return fn ? this.filterBy(fn) : this.clearFilter();
11743     },
11744
11745     /**
11746      * Filter by a function. The specified function will be called with each
11747      * record in this data source. If the function returns true the record is included,
11748      * otherwise it is filtered.
11749      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11750      * @param {Object} scope (optional) The scope of the function (defaults to this)
11751      */
11752     filterBy : function(fn, scope){
11753         this.snapshot = this.snapshot || this.data;
11754         this.data = this.queryBy(fn, scope||this);
11755         this.fireEvent("datachanged", this);
11756     },
11757
11758     /**
11759      * Query the records by a specified property.
11760      * @param {String} field A field on your records
11761      * @param {String/RegExp} value Either a string that the field
11762      * should start with or a RegExp to test against the field
11763      * @param {Boolean} anyMatch True to match any part not just the beginning
11764      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11765      */
11766     query : function(property, value, anyMatch){
11767         var fn = this.createFilterFn(property, value, anyMatch);
11768         return fn ? this.queryBy(fn) : this.data.clone();
11769     },
11770
11771     /**
11772      * Query by a function. The specified function will be called with each
11773      * record in this data source. If the function returns true the record is included
11774      * in the results.
11775      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11776      * @param {Object} scope (optional) The scope of the function (defaults to this)
11777       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11778      **/
11779     queryBy : function(fn, scope){
11780         var data = this.snapshot || this.data;
11781         return data.filterBy(fn, scope||this);
11782     },
11783
11784     /**
11785      * Collects unique values for a particular dataIndex from this store.
11786      * @param {String} dataIndex The property to collect
11787      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11788      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11789      * @return {Array} An array of the unique values
11790      **/
11791     collect : function(dataIndex, allowNull, bypassFilter){
11792         var d = (bypassFilter === true && this.snapshot) ?
11793                 this.snapshot.items : this.data.items;
11794         var v, sv, r = [], l = {};
11795         for(var i = 0, len = d.length; i < len; i++){
11796             v = d[i].data[dataIndex];
11797             sv = String(v);
11798             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11799                 l[sv] = true;
11800                 r[r.length] = v;
11801             }
11802         }
11803         return r;
11804     },
11805
11806     /**
11807      * Revert to a view of the Record cache with no filtering applied.
11808      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11809      */
11810     clearFilter : function(suppressEvent){
11811         if(this.snapshot && this.snapshot != this.data){
11812             this.data = this.snapshot;
11813             delete this.snapshot;
11814             if(suppressEvent !== true){
11815                 this.fireEvent("datachanged", this);
11816             }
11817         }
11818     },
11819
11820     // private
11821     afterEdit : function(record){
11822         if(this.modified.indexOf(record) == -1){
11823             this.modified.push(record);
11824         }
11825         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11826     },
11827     
11828     // private
11829     afterReject : function(record){
11830         this.modified.remove(record);
11831         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11832     },
11833
11834     // private
11835     afterCommit : function(record){
11836         this.modified.remove(record);
11837         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11838     },
11839
11840     /**
11841      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11842      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11843      */
11844     commitChanges : function(){
11845         var m = this.modified.slice(0);
11846         this.modified = [];
11847         for(var i = 0, len = m.length; i < len; i++){
11848             m[i].commit();
11849         }
11850     },
11851
11852     /**
11853      * Cancel outstanding changes on all changed records.
11854      */
11855     rejectChanges : function(){
11856         var m = this.modified.slice(0);
11857         this.modified = [];
11858         for(var i = 0, len = m.length; i < len; i++){
11859             m[i].reject();
11860         }
11861     },
11862
11863     onMetaChange : function(meta, rtype, o){
11864         this.recordType = rtype;
11865         this.fields = rtype.prototype.fields;
11866         delete this.snapshot;
11867         this.sortInfo = meta.sortInfo || this.sortInfo;
11868         this.modified = [];
11869         this.fireEvent('metachange', this, this.reader.meta);
11870     },
11871     
11872     moveIndex : function(data, type)
11873     {
11874         var index = this.indexOf(data);
11875         
11876         var newIndex = index + type;
11877         
11878         this.remove(data);
11879         
11880         this.insert(newIndex, data);
11881         
11882     }
11883 });/*
11884  * Based on:
11885  * Ext JS Library 1.1.1
11886  * Copyright(c) 2006-2007, Ext JS, LLC.
11887  *
11888  * Originally Released Under LGPL - original licence link has changed is not relivant.
11889  *
11890  * Fork - LGPL
11891  * <script type="text/javascript">
11892  */
11893
11894 /**
11895  * @class Roo.data.SimpleStore
11896  * @extends Roo.data.Store
11897  * Small helper class to make creating Stores from Array data easier.
11898  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11899  * @cfg {Array} fields An array of field definition objects, or field name strings.
11900  * @cfg {Array} data The multi-dimensional array of data
11901  * @constructor
11902  * @param {Object} config
11903  */
11904 Roo.data.SimpleStore = function(config){
11905     Roo.data.SimpleStore.superclass.constructor.call(this, {
11906         isLocal : true,
11907         reader: new Roo.data.ArrayReader({
11908                 id: config.id
11909             },
11910             Roo.data.Record.create(config.fields)
11911         ),
11912         proxy : new Roo.data.MemoryProxy(config.data)
11913     });
11914     this.load();
11915 };
11916 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11917  * Based on:
11918  * Ext JS Library 1.1.1
11919  * Copyright(c) 2006-2007, Ext JS, LLC.
11920  *
11921  * Originally Released Under LGPL - original licence link has changed is not relivant.
11922  *
11923  * Fork - LGPL
11924  * <script type="text/javascript">
11925  */
11926
11927 /**
11928 /**
11929  * @extends Roo.data.Store
11930  * @class Roo.data.JsonStore
11931  * Small helper class to make creating Stores for JSON data easier. <br/>
11932 <pre><code>
11933 var store = new Roo.data.JsonStore({
11934     url: 'get-images.php',
11935     root: 'images',
11936     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11937 });
11938 </code></pre>
11939  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11940  * JsonReader and HttpProxy (unless inline data is provided).</b>
11941  * @cfg {Array} fields An array of field definition objects, or field name strings.
11942  * @constructor
11943  * @param {Object} config
11944  */
11945 Roo.data.JsonStore = function(c){
11946     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11947         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11948         reader: new Roo.data.JsonReader(c, c.fields)
11949     }));
11950 };
11951 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11952  * Based on:
11953  * Ext JS Library 1.1.1
11954  * Copyright(c) 2006-2007, Ext JS, LLC.
11955  *
11956  * Originally Released Under LGPL - original licence link has changed is not relivant.
11957  *
11958  * Fork - LGPL
11959  * <script type="text/javascript">
11960  */
11961
11962  
11963 Roo.data.Field = function(config){
11964     if(typeof config == "string"){
11965         config = {name: config};
11966     }
11967     Roo.apply(this, config);
11968     
11969     if(!this.type){
11970         this.type = "auto";
11971     }
11972     
11973     var st = Roo.data.SortTypes;
11974     // named sortTypes are supported, here we look them up
11975     if(typeof this.sortType == "string"){
11976         this.sortType = st[this.sortType];
11977     }
11978     
11979     // set default sortType for strings and dates
11980     if(!this.sortType){
11981         switch(this.type){
11982             case "string":
11983                 this.sortType = st.asUCString;
11984                 break;
11985             case "date":
11986                 this.sortType = st.asDate;
11987                 break;
11988             default:
11989                 this.sortType = st.none;
11990         }
11991     }
11992
11993     // define once
11994     var stripRe = /[\$,%]/g;
11995
11996     // prebuilt conversion function for this field, instead of
11997     // switching every time we're reading a value
11998     if(!this.convert){
11999         var cv, dateFormat = this.dateFormat;
12000         switch(this.type){
12001             case "":
12002             case "auto":
12003             case undefined:
12004                 cv = function(v){ return v; };
12005                 break;
12006             case "string":
12007                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12008                 break;
12009             case "int":
12010                 cv = function(v){
12011                     return v !== undefined && v !== null && v !== '' ?
12012                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12013                     };
12014                 break;
12015             case "float":
12016                 cv = function(v){
12017                     return v !== undefined && v !== null && v !== '' ?
12018                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12019                     };
12020                 break;
12021             case "bool":
12022             case "boolean":
12023                 cv = function(v){ return v === true || v === "true" || v == 1; };
12024                 break;
12025             case "date":
12026                 cv = function(v){
12027                     if(!v){
12028                         return '';
12029                     }
12030                     if(v instanceof Date){
12031                         return v;
12032                     }
12033                     if(dateFormat){
12034                         if(dateFormat == "timestamp"){
12035                             return new Date(v*1000);
12036                         }
12037                         return Date.parseDate(v, dateFormat);
12038                     }
12039                     var parsed = Date.parse(v);
12040                     return parsed ? new Date(parsed) : null;
12041                 };
12042              break;
12043             
12044         }
12045         this.convert = cv;
12046     }
12047 };
12048
12049 Roo.data.Field.prototype = {
12050     dateFormat: null,
12051     defaultValue: "",
12052     mapping: null,
12053     sortType : null,
12054     sortDir : "ASC"
12055 };/*
12056  * Based on:
12057  * Ext JS Library 1.1.1
12058  * Copyright(c) 2006-2007, Ext JS, LLC.
12059  *
12060  * Originally Released Under LGPL - original licence link has changed is not relivant.
12061  *
12062  * Fork - LGPL
12063  * <script type="text/javascript">
12064  */
12065  
12066 // Base class for reading structured data from a data source.  This class is intended to be
12067 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12068
12069 /**
12070  * @class Roo.data.DataReader
12071  * Base class for reading structured data from a data source.  This class is intended to be
12072  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12073  */
12074
12075 Roo.data.DataReader = function(meta, recordType){
12076     
12077     this.meta = meta;
12078     
12079     this.recordType = recordType instanceof Array ? 
12080         Roo.data.Record.create(recordType) : recordType;
12081 };
12082
12083 Roo.data.DataReader.prototype = {
12084      /**
12085      * Create an empty record
12086      * @param {Object} data (optional) - overlay some values
12087      * @return {Roo.data.Record} record created.
12088      */
12089     newRow :  function(d) {
12090         var da =  {};
12091         this.recordType.prototype.fields.each(function(c) {
12092             switch( c.type) {
12093                 case 'int' : da[c.name] = 0; break;
12094                 case 'date' : da[c.name] = new Date(); break;
12095                 case 'float' : da[c.name] = 0.0; break;
12096                 case 'boolean' : da[c.name] = false; break;
12097                 default : da[c.name] = ""; break;
12098             }
12099             
12100         });
12101         return new this.recordType(Roo.apply(da, d));
12102     }
12103     
12104 };/*
12105  * Based on:
12106  * Ext JS Library 1.1.1
12107  * Copyright(c) 2006-2007, Ext JS, LLC.
12108  *
12109  * Originally Released Under LGPL - original licence link has changed is not relivant.
12110  *
12111  * Fork - LGPL
12112  * <script type="text/javascript">
12113  */
12114
12115 /**
12116  * @class Roo.data.DataProxy
12117  * @extends Roo.data.Observable
12118  * This class is an abstract base class for implementations which provide retrieval of
12119  * unformatted data objects.<br>
12120  * <p>
12121  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12122  * (of the appropriate type which knows how to parse the data object) to provide a block of
12123  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12124  * <p>
12125  * Custom implementations must implement the load method as described in
12126  * {@link Roo.data.HttpProxy#load}.
12127  */
12128 Roo.data.DataProxy = function(){
12129     this.addEvents({
12130         /**
12131          * @event beforeload
12132          * Fires before a network request is made to retrieve a data object.
12133          * @param {Object} This DataProxy object.
12134          * @param {Object} params The params parameter to the load function.
12135          */
12136         beforeload : true,
12137         /**
12138          * @event load
12139          * Fires before the load method's callback is called.
12140          * @param {Object} This DataProxy object.
12141          * @param {Object} o The data object.
12142          * @param {Object} arg The callback argument object passed to the load function.
12143          */
12144         load : true,
12145         /**
12146          * @event loadexception
12147          * Fires if an Exception occurs during data retrieval.
12148          * @param {Object} This DataProxy object.
12149          * @param {Object} o The data object.
12150          * @param {Object} arg The callback argument object passed to the load function.
12151          * @param {Object} e The Exception.
12152          */
12153         loadexception : true
12154     });
12155     Roo.data.DataProxy.superclass.constructor.call(this);
12156 };
12157
12158 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12159
12160     /**
12161      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12162      */
12163 /*
12164  * Based on:
12165  * Ext JS Library 1.1.1
12166  * Copyright(c) 2006-2007, Ext JS, LLC.
12167  *
12168  * Originally Released Under LGPL - original licence link has changed is not relivant.
12169  *
12170  * Fork - LGPL
12171  * <script type="text/javascript">
12172  */
12173 /**
12174  * @class Roo.data.MemoryProxy
12175  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12176  * to the Reader when its load method is called.
12177  * @constructor
12178  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12179  */
12180 Roo.data.MemoryProxy = function(data){
12181     if (data.data) {
12182         data = data.data;
12183     }
12184     Roo.data.MemoryProxy.superclass.constructor.call(this);
12185     this.data = data;
12186 };
12187
12188 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12189     
12190     /**
12191      * Load data from the requested source (in this case an in-memory
12192      * data object passed to the constructor), read the data object into
12193      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12194      * process that block using the passed callback.
12195      * @param {Object} params This parameter is not used by the MemoryProxy class.
12196      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12197      * object into a block of Roo.data.Records.
12198      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12199      * The function must be passed <ul>
12200      * <li>The Record block object</li>
12201      * <li>The "arg" argument from the load function</li>
12202      * <li>A boolean success indicator</li>
12203      * </ul>
12204      * @param {Object} scope The scope in which to call the callback
12205      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12206      */
12207     load : function(params, reader, callback, scope, arg){
12208         params = params || {};
12209         var result;
12210         try {
12211             result = reader.readRecords(this.data);
12212         }catch(e){
12213             this.fireEvent("loadexception", this, arg, null, e);
12214             callback.call(scope, null, arg, false);
12215             return;
12216         }
12217         callback.call(scope, result, arg, true);
12218     },
12219     
12220     // private
12221     update : function(params, records){
12222         
12223     }
12224 });/*
12225  * Based on:
12226  * Ext JS Library 1.1.1
12227  * Copyright(c) 2006-2007, Ext JS, LLC.
12228  *
12229  * Originally Released Under LGPL - original licence link has changed is not relivant.
12230  *
12231  * Fork - LGPL
12232  * <script type="text/javascript">
12233  */
12234 /**
12235  * @class Roo.data.HttpProxy
12236  * @extends Roo.data.DataProxy
12237  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12238  * configured to reference a certain URL.<br><br>
12239  * <p>
12240  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12241  * from which the running page was served.<br><br>
12242  * <p>
12243  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12244  * <p>
12245  * Be aware that to enable the browser to parse an XML document, the server must set
12246  * the Content-Type header in the HTTP response to "text/xml".
12247  * @constructor
12248  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12249  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12250  * will be used to make the request.
12251  */
12252 Roo.data.HttpProxy = function(conn){
12253     Roo.data.HttpProxy.superclass.constructor.call(this);
12254     // is conn a conn config or a real conn?
12255     this.conn = conn;
12256     this.useAjax = !conn || !conn.events;
12257   
12258 };
12259
12260 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12261     // thse are take from connection...
12262     
12263     /**
12264      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12265      */
12266     /**
12267      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12268      * extra parameters to each request made by this object. (defaults to undefined)
12269      */
12270     /**
12271      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12272      *  to each request made by this object. (defaults to undefined)
12273      */
12274     /**
12275      * @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)
12276      */
12277     /**
12278      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12279      */
12280      /**
12281      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12282      * @type Boolean
12283      */
12284   
12285
12286     /**
12287      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12288      * @type Boolean
12289      */
12290     /**
12291      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12292      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12293      * a finer-grained basis than the DataProxy events.
12294      */
12295     getConnection : function(){
12296         return this.useAjax ? Roo.Ajax : this.conn;
12297     },
12298
12299     /**
12300      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12301      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12302      * process that block using the passed callback.
12303      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12304      * for the request to the remote server.
12305      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12306      * object into a block of Roo.data.Records.
12307      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12308      * The function must be passed <ul>
12309      * <li>The Record block object</li>
12310      * <li>The "arg" argument from the load function</li>
12311      * <li>A boolean success indicator</li>
12312      * </ul>
12313      * @param {Object} scope The scope in which to call the callback
12314      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12315      */
12316     load : function(params, reader, callback, scope, arg){
12317         if(this.fireEvent("beforeload", this, params) !== false){
12318             var  o = {
12319                 params : params || {},
12320                 request: {
12321                     callback : callback,
12322                     scope : scope,
12323                     arg : arg
12324                 },
12325                 reader: reader,
12326                 callback : this.loadResponse,
12327                 scope: this
12328             };
12329             if(this.useAjax){
12330                 Roo.applyIf(o, this.conn);
12331                 if(this.activeRequest){
12332                     Roo.Ajax.abort(this.activeRequest);
12333                 }
12334                 this.activeRequest = Roo.Ajax.request(o);
12335             }else{
12336                 this.conn.request(o);
12337             }
12338         }else{
12339             callback.call(scope||this, null, arg, false);
12340         }
12341     },
12342
12343     // private
12344     loadResponse : function(o, success, response){
12345         delete this.activeRequest;
12346         if(!success){
12347             this.fireEvent("loadexception", this, o, response);
12348             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12349             return;
12350         }
12351         var result;
12352         try {
12353             result = o.reader.read(response);
12354         }catch(e){
12355             this.fireEvent("loadexception", this, o, response, e);
12356             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12357             return;
12358         }
12359         
12360         this.fireEvent("load", this, o, o.request.arg);
12361         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12362     },
12363
12364     // private
12365     update : function(dataSet){
12366
12367     },
12368
12369     // private
12370     updateResponse : function(dataSet){
12371
12372     }
12373 });/*
12374  * Based on:
12375  * Ext JS Library 1.1.1
12376  * Copyright(c) 2006-2007, Ext JS, LLC.
12377  *
12378  * Originally Released Under LGPL - original licence link has changed is not relivant.
12379  *
12380  * Fork - LGPL
12381  * <script type="text/javascript">
12382  */
12383
12384 /**
12385  * @class Roo.data.ScriptTagProxy
12386  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12387  * other than the originating domain of the running page.<br><br>
12388  * <p>
12389  * <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
12390  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12391  * <p>
12392  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12393  * source code that is used as the source inside a &lt;script> tag.<br><br>
12394  * <p>
12395  * In order for the browser to process the returned data, the server must wrap the data object
12396  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12397  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12398  * depending on whether the callback name was passed:
12399  * <p>
12400  * <pre><code>
12401 boolean scriptTag = false;
12402 String cb = request.getParameter("callback");
12403 if (cb != null) {
12404     scriptTag = true;
12405     response.setContentType("text/javascript");
12406 } else {
12407     response.setContentType("application/x-json");
12408 }
12409 Writer out = response.getWriter();
12410 if (scriptTag) {
12411     out.write(cb + "(");
12412 }
12413 out.print(dataBlock.toJsonString());
12414 if (scriptTag) {
12415     out.write(");");
12416 }
12417 </pre></code>
12418  *
12419  * @constructor
12420  * @param {Object} config A configuration object.
12421  */
12422 Roo.data.ScriptTagProxy = function(config){
12423     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12424     Roo.apply(this, config);
12425     this.head = document.getElementsByTagName("head")[0];
12426 };
12427
12428 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12429
12430 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12431     /**
12432      * @cfg {String} url The URL from which to request the data object.
12433      */
12434     /**
12435      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12436      */
12437     timeout : 30000,
12438     /**
12439      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12440      * the server the name of the callback function set up by the load call to process the returned data object.
12441      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12442      * javascript output which calls this named function passing the data object as its only parameter.
12443      */
12444     callbackParam : "callback",
12445     /**
12446      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12447      * name to the request.
12448      */
12449     nocache : true,
12450
12451     /**
12452      * Load data from the configured URL, read the data object into
12453      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12454      * process that block using the passed callback.
12455      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12456      * for the request to the remote server.
12457      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12458      * object into a block of Roo.data.Records.
12459      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12460      * The function must be passed <ul>
12461      * <li>The Record block object</li>
12462      * <li>The "arg" argument from the load function</li>
12463      * <li>A boolean success indicator</li>
12464      * </ul>
12465      * @param {Object} scope The scope in which to call the callback
12466      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12467      */
12468     load : function(params, reader, callback, scope, arg){
12469         if(this.fireEvent("beforeload", this, params) !== false){
12470
12471             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12472
12473             var url = this.url;
12474             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12475             if(this.nocache){
12476                 url += "&_dc=" + (new Date().getTime());
12477             }
12478             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12479             var trans = {
12480                 id : transId,
12481                 cb : "stcCallback"+transId,
12482                 scriptId : "stcScript"+transId,
12483                 params : params,
12484                 arg : arg,
12485                 url : url,
12486                 callback : callback,
12487                 scope : scope,
12488                 reader : reader
12489             };
12490             var conn = this;
12491
12492             window[trans.cb] = function(o){
12493                 conn.handleResponse(o, trans);
12494             };
12495
12496             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12497
12498             if(this.autoAbort !== false){
12499                 this.abort();
12500             }
12501
12502             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12503
12504             var script = document.createElement("script");
12505             script.setAttribute("src", url);
12506             script.setAttribute("type", "text/javascript");
12507             script.setAttribute("id", trans.scriptId);
12508             this.head.appendChild(script);
12509
12510             this.trans = trans;
12511         }else{
12512             callback.call(scope||this, null, arg, false);
12513         }
12514     },
12515
12516     // private
12517     isLoading : function(){
12518         return this.trans ? true : false;
12519     },
12520
12521     /**
12522      * Abort the current server request.
12523      */
12524     abort : function(){
12525         if(this.isLoading()){
12526             this.destroyTrans(this.trans);
12527         }
12528     },
12529
12530     // private
12531     destroyTrans : function(trans, isLoaded){
12532         this.head.removeChild(document.getElementById(trans.scriptId));
12533         clearTimeout(trans.timeoutId);
12534         if(isLoaded){
12535             window[trans.cb] = undefined;
12536             try{
12537                 delete window[trans.cb];
12538             }catch(e){}
12539         }else{
12540             // if hasn't been loaded, wait for load to remove it to prevent script error
12541             window[trans.cb] = function(){
12542                 window[trans.cb] = undefined;
12543                 try{
12544                     delete window[trans.cb];
12545                 }catch(e){}
12546             };
12547         }
12548     },
12549
12550     // private
12551     handleResponse : function(o, trans){
12552         this.trans = false;
12553         this.destroyTrans(trans, true);
12554         var result;
12555         try {
12556             result = trans.reader.readRecords(o);
12557         }catch(e){
12558             this.fireEvent("loadexception", this, o, trans.arg, e);
12559             trans.callback.call(trans.scope||window, null, trans.arg, false);
12560             return;
12561         }
12562         this.fireEvent("load", this, o, trans.arg);
12563         trans.callback.call(trans.scope||window, result, trans.arg, true);
12564     },
12565
12566     // private
12567     handleFailure : function(trans){
12568         this.trans = false;
12569         this.destroyTrans(trans, false);
12570         this.fireEvent("loadexception", this, null, trans.arg);
12571         trans.callback.call(trans.scope||window, null, trans.arg, false);
12572     }
12573 });/*
12574  * Based on:
12575  * Ext JS Library 1.1.1
12576  * Copyright(c) 2006-2007, Ext JS, LLC.
12577  *
12578  * Originally Released Under LGPL - original licence link has changed is not relivant.
12579  *
12580  * Fork - LGPL
12581  * <script type="text/javascript">
12582  */
12583
12584 /**
12585  * @class Roo.data.JsonReader
12586  * @extends Roo.data.DataReader
12587  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12588  * based on mappings in a provided Roo.data.Record constructor.
12589  * 
12590  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12591  * in the reply previously. 
12592  * 
12593  * <p>
12594  * Example code:
12595  * <pre><code>
12596 var RecordDef = Roo.data.Record.create([
12597     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12598     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12599 ]);
12600 var myReader = new Roo.data.JsonReader({
12601     totalProperty: "results",    // The property which contains the total dataset size (optional)
12602     root: "rows",                // The property which contains an Array of row objects
12603     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12604 }, RecordDef);
12605 </code></pre>
12606  * <p>
12607  * This would consume a JSON file like this:
12608  * <pre><code>
12609 { 'results': 2, 'rows': [
12610     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12611     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12612 }
12613 </code></pre>
12614  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12615  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12616  * paged from the remote server.
12617  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12618  * @cfg {String} root name of the property which contains the Array of row objects.
12619  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12620  * @cfg {Array} fields Array of field definition objects
12621  * @constructor
12622  * Create a new JsonReader
12623  * @param {Object} meta Metadata configuration options
12624  * @param {Object} recordType Either an Array of field definition objects,
12625  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12626  */
12627 Roo.data.JsonReader = function(meta, recordType){
12628     
12629     meta = meta || {};
12630     // set some defaults:
12631     Roo.applyIf(meta, {
12632         totalProperty: 'total',
12633         successProperty : 'success',
12634         root : 'data',
12635         id : 'id'
12636     });
12637     
12638     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12639 };
12640 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12641     
12642     /**
12643      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12644      * Used by Store query builder to append _requestMeta to params.
12645      * 
12646      */
12647     metaFromRemote : false,
12648     /**
12649      * This method is only used by a DataProxy which has retrieved data from a remote server.
12650      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12651      * @return {Object} data A data block which is used by an Roo.data.Store object as
12652      * a cache of Roo.data.Records.
12653      */
12654     read : function(response){
12655         var json = response.responseText;
12656        
12657         var o = /* eval:var:o */ eval("("+json+")");
12658         if(!o) {
12659             throw {message: "JsonReader.read: Json object not found"};
12660         }
12661         
12662         if(o.metaData){
12663             
12664             delete this.ef;
12665             this.metaFromRemote = true;
12666             this.meta = o.metaData;
12667             this.recordType = Roo.data.Record.create(o.metaData.fields);
12668             this.onMetaChange(this.meta, this.recordType, o);
12669         }
12670         return this.readRecords(o);
12671     },
12672
12673     // private function a store will implement
12674     onMetaChange : function(meta, recordType, o){
12675
12676     },
12677
12678     /**
12679          * @ignore
12680          */
12681     simpleAccess: function(obj, subsc) {
12682         return obj[subsc];
12683     },
12684
12685         /**
12686          * @ignore
12687          */
12688     getJsonAccessor: function(){
12689         var re = /[\[\.]/;
12690         return function(expr) {
12691             try {
12692                 return(re.test(expr))
12693                     ? new Function("obj", "return obj." + expr)
12694                     : function(obj){
12695                         return obj[expr];
12696                     };
12697             } catch(e){}
12698             return Roo.emptyFn;
12699         };
12700     }(),
12701
12702     /**
12703      * Create a data block containing Roo.data.Records from an XML document.
12704      * @param {Object} o An object which contains an Array of row objects in the property specified
12705      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12706      * which contains the total size of the dataset.
12707      * @return {Object} data A data block which is used by an Roo.data.Store object as
12708      * a cache of Roo.data.Records.
12709      */
12710     readRecords : function(o){
12711         /**
12712          * After any data loads, the raw JSON data is available for further custom processing.
12713          * @type Object
12714          */
12715         this.o = o;
12716         var s = this.meta, Record = this.recordType,
12717             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12718
12719 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12720         if (!this.ef) {
12721             if(s.totalProperty) {
12722                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12723                 }
12724                 if(s.successProperty) {
12725                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12726                 }
12727                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12728                 if (s.id) {
12729                         var g = this.getJsonAccessor(s.id);
12730                         this.getId = function(rec) {
12731                                 var r = g(rec);  
12732                                 return (r === undefined || r === "") ? null : r;
12733                         };
12734                 } else {
12735                         this.getId = function(){return null;};
12736                 }
12737             this.ef = [];
12738             for(var jj = 0; jj < fl; jj++){
12739                 f = fi[jj];
12740                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12741                 this.ef[jj] = this.getJsonAccessor(map);
12742             }
12743         }
12744
12745         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12746         if(s.totalProperty){
12747             var vt = parseInt(this.getTotal(o), 10);
12748             if(!isNaN(vt)){
12749                 totalRecords = vt;
12750             }
12751         }
12752         if(s.successProperty){
12753             var vs = this.getSuccess(o);
12754             if(vs === false || vs === 'false'){
12755                 success = false;
12756             }
12757         }
12758         var records = [];
12759         for(var i = 0; i < c; i++){
12760                 var n = root[i];
12761             var values = {};
12762             var id = this.getId(n);
12763             for(var j = 0; j < fl; j++){
12764                 f = fi[j];
12765             var v = this.ef[j](n);
12766             if (!f.convert) {
12767                 Roo.log('missing convert for ' + f.name);
12768                 Roo.log(f);
12769                 continue;
12770             }
12771             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12772             }
12773             var record = new Record(values, id);
12774             record.json = n;
12775             records[i] = record;
12776         }
12777         return {
12778             raw : o,
12779             success : success,
12780             records : records,
12781             totalRecords : totalRecords
12782         };
12783     }
12784 });/*
12785  * Based on:
12786  * Ext JS Library 1.1.1
12787  * Copyright(c) 2006-2007, Ext JS, LLC.
12788  *
12789  * Originally Released Under LGPL - original licence link has changed is not relivant.
12790  *
12791  * Fork - LGPL
12792  * <script type="text/javascript">
12793  */
12794
12795 /**
12796  * @class Roo.data.ArrayReader
12797  * @extends Roo.data.DataReader
12798  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12799  * Each element of that Array represents a row of data fields. The
12800  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12801  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12802  * <p>
12803  * Example code:.
12804  * <pre><code>
12805 var RecordDef = Roo.data.Record.create([
12806     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12807     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12808 ]);
12809 var myReader = new Roo.data.ArrayReader({
12810     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12811 }, RecordDef);
12812 </code></pre>
12813  * <p>
12814  * This would consume an Array like this:
12815  * <pre><code>
12816 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12817   </code></pre>
12818  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12819  * @constructor
12820  * Create a new JsonReader
12821  * @param {Object} meta Metadata configuration options.
12822  * @param {Object} recordType Either an Array of field definition objects
12823  * as specified to {@link Roo.data.Record#create},
12824  * or an {@link Roo.data.Record} object
12825  * created using {@link Roo.data.Record#create}.
12826  */
12827 Roo.data.ArrayReader = function(meta, recordType){
12828     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12829 };
12830
12831 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12832     /**
12833      * Create a data block containing Roo.data.Records from an XML document.
12834      * @param {Object} o An Array of row objects which represents the dataset.
12835      * @return {Object} data A data block which is used by an Roo.data.Store object as
12836      * a cache of Roo.data.Records.
12837      */
12838     readRecords : function(o){
12839         var sid = this.meta ? this.meta.id : null;
12840         var recordType = this.recordType, fields = recordType.prototype.fields;
12841         var records = [];
12842         var root = o;
12843             for(var i = 0; i < root.length; i++){
12844                     var n = root[i];
12845                 var values = {};
12846                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12847                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12848                 var f = fields.items[j];
12849                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12850                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12851                 v = f.convert(v);
12852                 values[f.name] = v;
12853             }
12854                 var record = new recordType(values, id);
12855                 record.json = n;
12856                 records[records.length] = record;
12857             }
12858             return {
12859                 records : records,
12860                 totalRecords : records.length
12861             };
12862     }
12863 });/*
12864  * - LGPL
12865  * * 
12866  */
12867
12868 /**
12869  * @class Roo.bootstrap.ComboBox
12870  * @extends Roo.bootstrap.TriggerField
12871  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12872  * @cfg {Boolean} append (true|false) default false
12873  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12874  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12875  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12876  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12877  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12878  * @cfg {Boolean} animate default true
12879  * @cfg {Boolean} emptyResultText only for touch device
12880  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12881  * @cfg {String} emptyTitle default ''
12882  * @constructor
12883  * Create a new ComboBox.
12884  * @param {Object} config Configuration options
12885  */
12886 Roo.bootstrap.ComboBox = function(config){
12887     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12888     this.addEvents({
12889         /**
12890          * @event expand
12891          * Fires when the dropdown list is expanded
12892         * @param {Roo.bootstrap.ComboBox} combo This combo box
12893         */
12894         'expand' : true,
12895         /**
12896          * @event collapse
12897          * Fires when the dropdown list is collapsed
12898         * @param {Roo.bootstrap.ComboBox} combo This combo box
12899         */
12900         'collapse' : true,
12901         /**
12902          * @event beforeselect
12903          * Fires before a list item is selected. Return false to cancel the selection.
12904         * @param {Roo.bootstrap.ComboBox} combo This combo box
12905         * @param {Roo.data.Record} record The data record returned from the underlying store
12906         * @param {Number} index The index of the selected item in the dropdown list
12907         */
12908         'beforeselect' : true,
12909         /**
12910          * @event select
12911          * Fires when a list item is selected
12912         * @param {Roo.bootstrap.ComboBox} combo This combo box
12913         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12914         * @param {Number} index The index of the selected item in the dropdown list
12915         */
12916         'select' : true,
12917         /**
12918          * @event beforequery
12919          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12920          * The event object passed has these properties:
12921         * @param {Roo.bootstrap.ComboBox} combo This combo box
12922         * @param {String} query The query
12923         * @param {Boolean} forceAll true to force "all" query
12924         * @param {Boolean} cancel true to cancel the query
12925         * @param {Object} e The query event object
12926         */
12927         'beforequery': true,
12928          /**
12929          * @event add
12930          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12931         * @param {Roo.bootstrap.ComboBox} combo This combo box
12932         */
12933         'add' : true,
12934         /**
12935          * @event edit
12936          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12937         * @param {Roo.bootstrap.ComboBox} combo This combo box
12938         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12939         */
12940         'edit' : true,
12941         /**
12942          * @event remove
12943          * Fires when the remove value from the combobox array
12944         * @param {Roo.bootstrap.ComboBox} combo This combo box
12945         */
12946         'remove' : true,
12947         /**
12948          * @event afterremove
12949          * Fires when the remove value from the combobox array
12950         * @param {Roo.bootstrap.ComboBox} combo This combo box
12951         */
12952         'afterremove' : true,
12953         /**
12954          * @event specialfilter
12955          * Fires when specialfilter
12956             * @param {Roo.bootstrap.ComboBox} combo This combo box
12957             */
12958         'specialfilter' : true,
12959         /**
12960          * @event tick
12961          * Fires when tick the element
12962             * @param {Roo.bootstrap.ComboBox} combo This combo box
12963             */
12964         'tick' : true,
12965         /**
12966          * @event touchviewdisplay
12967          * Fires when touch view require special display (default is using displayField)
12968             * @param {Roo.bootstrap.ComboBox} combo This combo box
12969             * @param {Object} cfg set html .
12970             */
12971         'touchviewdisplay' : true
12972         
12973     });
12974     
12975     this.item = [];
12976     this.tickItems = [];
12977     
12978     this.selectedIndex = -1;
12979     if(this.mode == 'local'){
12980         if(config.queryDelay === undefined){
12981             this.queryDelay = 10;
12982         }
12983         if(config.minChars === undefined){
12984             this.minChars = 0;
12985         }
12986     }
12987 };
12988
12989 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12990      
12991     /**
12992      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12993      * rendering into an Roo.Editor, defaults to false)
12994      */
12995     /**
12996      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12997      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12998      */
12999     /**
13000      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13001      */
13002     /**
13003      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13004      * the dropdown list (defaults to undefined, with no header element)
13005      */
13006
13007      /**
13008      * @cfg {String/Roo.Template} tpl The template to use to render the output
13009      */
13010      
13011      /**
13012      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13013      */
13014     listWidth: undefined,
13015     /**
13016      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13017      * mode = 'remote' or 'text' if mode = 'local')
13018      */
13019     displayField: undefined,
13020     
13021     /**
13022      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13023      * mode = 'remote' or 'value' if mode = 'local'). 
13024      * Note: use of a valueField requires the user make a selection
13025      * in order for a value to be mapped.
13026      */
13027     valueField: undefined,
13028     /**
13029      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13030      */
13031     modalTitle : '',
13032     
13033     /**
13034      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13035      * field's data value (defaults to the underlying DOM element's name)
13036      */
13037     hiddenName: undefined,
13038     /**
13039      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13040      */
13041     listClass: '',
13042     /**
13043      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13044      */
13045     selectedClass: 'active',
13046     
13047     /**
13048      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13049      */
13050     shadow:'sides',
13051     /**
13052      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13053      * anchor positions (defaults to 'tl-bl')
13054      */
13055     listAlign: 'tl-bl?',
13056     /**
13057      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13058      */
13059     maxHeight: 300,
13060     /**
13061      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13062      * query specified by the allQuery config option (defaults to 'query')
13063      */
13064     triggerAction: 'query',
13065     /**
13066      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13067      * (defaults to 4, does not apply if editable = false)
13068      */
13069     minChars : 4,
13070     /**
13071      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13072      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13073      */
13074     typeAhead: false,
13075     /**
13076      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13077      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13078      */
13079     queryDelay: 500,
13080     /**
13081      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13082      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13083      */
13084     pageSize: 0,
13085     /**
13086      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13087      * when editable = true (defaults to false)
13088      */
13089     selectOnFocus:false,
13090     /**
13091      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13092      */
13093     queryParam: 'query',
13094     /**
13095      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13096      * when mode = 'remote' (defaults to 'Loading...')
13097      */
13098     loadingText: 'Loading...',
13099     /**
13100      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13101      */
13102     resizable: false,
13103     /**
13104      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13105      */
13106     handleHeight : 8,
13107     /**
13108      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13109      * traditional select (defaults to true)
13110      */
13111     editable: true,
13112     /**
13113      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13114      */
13115     allQuery: '',
13116     /**
13117      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13118      */
13119     mode: 'remote',
13120     /**
13121      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13122      * listWidth has a higher value)
13123      */
13124     minListWidth : 70,
13125     /**
13126      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13127      * allow the user to set arbitrary text into the field (defaults to false)
13128      */
13129     forceSelection:false,
13130     /**
13131      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13132      * if typeAhead = true (defaults to 250)
13133      */
13134     typeAheadDelay : 250,
13135     /**
13136      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13137      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13138      */
13139     valueNotFoundText : undefined,
13140     /**
13141      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13142      */
13143     blockFocus : false,
13144     
13145     /**
13146      * @cfg {Boolean} disableClear Disable showing of clear button.
13147      */
13148     disableClear : false,
13149     /**
13150      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13151      */
13152     alwaysQuery : false,
13153     
13154     /**
13155      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13156      */
13157     multiple : false,
13158     
13159     /**
13160      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13161      */
13162     invalidClass : "has-warning",
13163     
13164     /**
13165      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13166      */
13167     validClass : "has-success",
13168     
13169     /**
13170      * @cfg {Boolean} specialFilter (true|false) special filter default false
13171      */
13172     specialFilter : false,
13173     
13174     /**
13175      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13176      */
13177     mobileTouchView : true,
13178     
13179     /**
13180      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13181      */
13182     useNativeIOS : false,
13183     
13184     /**
13185      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13186      */
13187     mobile_restrict_height : false,
13188     
13189     ios_options : false,
13190     
13191     //private
13192     addicon : false,
13193     editicon: false,
13194     
13195     page: 0,
13196     hasQuery: false,
13197     append: false,
13198     loadNext: false,
13199     autoFocus : true,
13200     tickable : false,
13201     btnPosition : 'right',
13202     triggerList : true,
13203     showToggleBtn : true,
13204     animate : true,
13205     emptyResultText: 'Empty',
13206     triggerText : 'Select',
13207     emptyTitle : '',
13208     
13209     // element that contains real text value.. (when hidden is used..)
13210     
13211     getAutoCreate : function()
13212     {   
13213         var cfg = false;
13214         //render
13215         /*
13216          * Render classic select for iso
13217          */
13218         
13219         if(Roo.isIOS && this.useNativeIOS){
13220             cfg = this.getAutoCreateNativeIOS();
13221             return cfg;
13222         }
13223         
13224         /*
13225          * Touch Devices
13226          */
13227         
13228         if(Roo.isTouch && this.mobileTouchView){
13229             cfg = this.getAutoCreateTouchView();
13230             return cfg;;
13231         }
13232         
13233         /*
13234          *  Normal ComboBox
13235          */
13236         if(!this.tickable){
13237             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13238             return cfg;
13239         }
13240         
13241         /*
13242          *  ComboBox with tickable selections
13243          */
13244              
13245         var align = this.labelAlign || this.parentLabelAlign();
13246         
13247         cfg = {
13248             cls : 'form-group roo-combobox-tickable' //input-group
13249         };
13250         
13251         var btn_text_select = '';
13252         var btn_text_done = '';
13253         var btn_text_cancel = '';
13254         
13255         if (this.btn_text_show) {
13256             btn_text_select = 'Select';
13257             btn_text_done = 'Done';
13258             btn_text_cancel = 'Cancel'; 
13259         }
13260         
13261         var buttons = {
13262             tag : 'div',
13263             cls : 'tickable-buttons',
13264             cn : [
13265                 {
13266                     tag : 'button',
13267                     type : 'button',
13268                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13269                     //html : this.triggerText
13270                     html: btn_text_select
13271                 },
13272                 {
13273                     tag : 'button',
13274                     type : 'button',
13275                     name : 'ok',
13276                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13277                     //html : 'Done'
13278                     html: btn_text_done
13279                 },
13280                 {
13281                     tag : 'button',
13282                     type : 'button',
13283                     name : 'cancel',
13284                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13285                     //html : 'Cancel'
13286                     html: btn_text_cancel
13287                 }
13288             ]
13289         };
13290         
13291         if(this.editable){
13292             buttons.cn.unshift({
13293                 tag: 'input',
13294                 cls: 'roo-select2-search-field-input'
13295             });
13296         }
13297         
13298         var _this = this;
13299         
13300         Roo.each(buttons.cn, function(c){
13301             if (_this.size) {
13302                 c.cls += ' btn-' + _this.size;
13303             }
13304
13305             if (_this.disabled) {
13306                 c.disabled = true;
13307             }
13308         });
13309         
13310         var box = {
13311             tag: 'div',
13312             cn: [
13313                 {
13314                     tag: 'input',
13315                     type : 'hidden',
13316                     cls: 'form-hidden-field'
13317                 },
13318                 {
13319                     tag: 'ul',
13320                     cls: 'roo-select2-choices',
13321                     cn:[
13322                         {
13323                             tag: 'li',
13324                             cls: 'roo-select2-search-field',
13325                             cn: [
13326                                 buttons
13327                             ]
13328                         }
13329                     ]
13330                 }
13331             ]
13332         };
13333         
13334         var combobox = {
13335             cls: 'roo-select2-container input-group roo-select2-container-multi',
13336             cn: [
13337                 
13338                 box
13339 //                {
13340 //                    tag: 'ul',
13341 //                    cls: 'typeahead typeahead-long dropdown-menu',
13342 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13343 //                }
13344             ]
13345         };
13346         
13347         if(this.hasFeedback && !this.allowBlank){
13348             
13349             var feedback = {
13350                 tag: 'span',
13351                 cls: 'glyphicon form-control-feedback'
13352             };
13353
13354             combobox.cn.push(feedback);
13355         }
13356         
13357         var indicator = {
13358             tag : 'i',
13359             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13360             tooltip : 'This field is required'
13361         };
13362         if (Roo.bootstrap.version == 4) {
13363             indicator = {
13364                 tag : 'i',
13365                 style : 'display:none'
13366             };
13367         }
13368         if (align ==='left' && this.fieldLabel.length) {
13369             
13370             cfg.cls += ' roo-form-group-label-left row';
13371             
13372             cfg.cn = [
13373                 indicator,
13374                 {
13375                     tag: 'label',
13376                     'for' :  id,
13377                     cls : 'control-label col-form-label',
13378                     html : this.fieldLabel
13379
13380                 },
13381                 {
13382                     cls : "", 
13383                     cn: [
13384                         combobox
13385                     ]
13386                 }
13387
13388             ];
13389             
13390             var labelCfg = cfg.cn[1];
13391             var contentCfg = cfg.cn[2];
13392             
13393
13394             if(this.indicatorpos == 'right'){
13395                 
13396                 cfg.cn = [
13397                     {
13398                         tag: 'label',
13399                         'for' :  id,
13400                         cls : 'control-label col-form-label',
13401                         cn : [
13402                             {
13403                                 tag : 'span',
13404                                 html : this.fieldLabel
13405                             },
13406                             indicator
13407                         ]
13408                     },
13409                     {
13410                         cls : "",
13411                         cn: [
13412                             combobox
13413                         ]
13414                     }
13415
13416                 ];
13417                 
13418                 
13419                 
13420                 labelCfg = cfg.cn[0];
13421                 contentCfg = cfg.cn[1];
13422             
13423             }
13424             
13425             if(this.labelWidth > 12){
13426                 labelCfg.style = "width: " + this.labelWidth + 'px';
13427             }
13428             
13429             if(this.labelWidth < 13 && this.labelmd == 0){
13430                 this.labelmd = this.labelWidth;
13431             }
13432             
13433             if(this.labellg > 0){
13434                 labelCfg.cls += ' col-lg-' + this.labellg;
13435                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13436             }
13437             
13438             if(this.labelmd > 0){
13439                 labelCfg.cls += ' col-md-' + this.labelmd;
13440                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13441             }
13442             
13443             if(this.labelsm > 0){
13444                 labelCfg.cls += ' col-sm-' + this.labelsm;
13445                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13446             }
13447             
13448             if(this.labelxs > 0){
13449                 labelCfg.cls += ' col-xs-' + this.labelxs;
13450                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13451             }
13452                 
13453                 
13454         } else if ( this.fieldLabel.length) {
13455 //                Roo.log(" label");
13456                  cfg.cn = [
13457                    indicator,
13458                     {
13459                         tag: 'label',
13460                         //cls : 'input-group-addon',
13461                         html : this.fieldLabel
13462                     },
13463                     combobox
13464                 ];
13465                 
13466                 if(this.indicatorpos == 'right'){
13467                     cfg.cn = [
13468                         {
13469                             tag: 'label',
13470                             //cls : 'input-group-addon',
13471                             html : this.fieldLabel
13472                         },
13473                         indicator,
13474                         combobox
13475                     ];
13476                     
13477                 }
13478
13479         } else {
13480             
13481 //                Roo.log(" no label && no align");
13482                 cfg = combobox
13483                      
13484                 
13485         }
13486          
13487         var settings=this;
13488         ['xs','sm','md','lg'].map(function(size){
13489             if (settings[size]) {
13490                 cfg.cls += ' col-' + size + '-' + settings[size];
13491             }
13492         });
13493         
13494         return cfg;
13495         
13496     },
13497     
13498     _initEventsCalled : false,
13499     
13500     // private
13501     initEvents: function()
13502     {   
13503         if (this._initEventsCalled) { // as we call render... prevent looping...
13504             return;
13505         }
13506         this._initEventsCalled = true;
13507         
13508         if (!this.store) {
13509             throw "can not find store for combo";
13510         }
13511         
13512         this.indicator = this.indicatorEl();
13513         
13514         this.store = Roo.factory(this.store, Roo.data);
13515         this.store.parent = this;
13516         
13517         // if we are building from html. then this element is so complex, that we can not really
13518         // use the rendered HTML.
13519         // so we have to trash and replace the previous code.
13520         if (Roo.XComponent.build_from_html) {
13521             // remove this element....
13522             var e = this.el.dom, k=0;
13523             while (e ) { e = e.previousSibling;  ++k;}
13524
13525             this.el.remove();
13526             
13527             this.el=false;
13528             this.rendered = false;
13529             
13530             this.render(this.parent().getChildContainer(true), k);
13531         }
13532         
13533         if(Roo.isIOS && this.useNativeIOS){
13534             this.initIOSView();
13535             return;
13536         }
13537         
13538         /*
13539          * Touch Devices
13540          */
13541         
13542         if(Roo.isTouch && this.mobileTouchView){
13543             this.initTouchView();
13544             return;
13545         }
13546         
13547         if(this.tickable){
13548             this.initTickableEvents();
13549             return;
13550         }
13551         
13552         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13553         
13554         if(this.hiddenName){
13555             
13556             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13557             
13558             this.hiddenField.dom.value =
13559                 this.hiddenValue !== undefined ? this.hiddenValue :
13560                 this.value !== undefined ? this.value : '';
13561
13562             // prevent input submission
13563             this.el.dom.removeAttribute('name');
13564             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13565              
13566              
13567         }
13568         //if(Roo.isGecko){
13569         //    this.el.dom.setAttribute('autocomplete', 'off');
13570         //}
13571         
13572         var cls = 'x-combo-list';
13573         
13574         //this.list = new Roo.Layer({
13575         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13576         //});
13577         
13578         var _this = this;
13579         
13580         (function(){
13581             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13582             _this.list.setWidth(lw);
13583         }).defer(100);
13584         
13585         this.list.on('mouseover', this.onViewOver, this);
13586         this.list.on('mousemove', this.onViewMove, this);
13587         this.list.on('scroll', this.onViewScroll, this);
13588         
13589         /*
13590         this.list.swallowEvent('mousewheel');
13591         this.assetHeight = 0;
13592
13593         if(this.title){
13594             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13595             this.assetHeight += this.header.getHeight();
13596         }
13597
13598         this.innerList = this.list.createChild({cls:cls+'-inner'});
13599         this.innerList.on('mouseover', this.onViewOver, this);
13600         this.innerList.on('mousemove', this.onViewMove, this);
13601         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13602         
13603         if(this.allowBlank && !this.pageSize && !this.disableClear){
13604             this.footer = this.list.createChild({cls:cls+'-ft'});
13605             this.pageTb = new Roo.Toolbar(this.footer);
13606            
13607         }
13608         if(this.pageSize){
13609             this.footer = this.list.createChild({cls:cls+'-ft'});
13610             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13611                     {pageSize: this.pageSize});
13612             
13613         }
13614         
13615         if (this.pageTb && this.allowBlank && !this.disableClear) {
13616             var _this = this;
13617             this.pageTb.add(new Roo.Toolbar.Fill(), {
13618                 cls: 'x-btn-icon x-btn-clear',
13619                 text: '&#160;',
13620                 handler: function()
13621                 {
13622                     _this.collapse();
13623                     _this.clearValue();
13624                     _this.onSelect(false, -1);
13625                 }
13626             });
13627         }
13628         if (this.footer) {
13629             this.assetHeight += this.footer.getHeight();
13630         }
13631         */
13632             
13633         if(!this.tpl){
13634             this.tpl = '<li><a class="drobpdown-item" href="#">{' + this.displayField + '}</a></li>';
13635         }
13636
13637         this.view = new Roo.View(this.list, this.tpl, {
13638             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13639         });
13640         //this.view.wrapEl.setDisplayed(false);
13641         this.view.on('click', this.onViewClick, this);
13642         
13643         
13644         this.store.on('beforeload', this.onBeforeLoad, this);
13645         this.store.on('load', this.onLoad, this);
13646         this.store.on('loadexception', this.onLoadException, this);
13647         /*
13648         if(this.resizable){
13649             this.resizer = new Roo.Resizable(this.list,  {
13650                pinned:true, handles:'se'
13651             });
13652             this.resizer.on('resize', function(r, w, h){
13653                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13654                 this.listWidth = w;
13655                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13656                 this.restrictHeight();
13657             }, this);
13658             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13659         }
13660         */
13661         if(!this.editable){
13662             this.editable = true;
13663             this.setEditable(false);
13664         }
13665         
13666         /*
13667         
13668         if (typeof(this.events.add.listeners) != 'undefined') {
13669             
13670             this.addicon = this.wrap.createChild(
13671                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13672        
13673             this.addicon.on('click', function(e) {
13674                 this.fireEvent('add', this);
13675             }, this);
13676         }
13677         if (typeof(this.events.edit.listeners) != 'undefined') {
13678             
13679             this.editicon = this.wrap.createChild(
13680                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13681             if (this.addicon) {
13682                 this.editicon.setStyle('margin-left', '40px');
13683             }
13684             this.editicon.on('click', function(e) {
13685                 
13686                 // we fire even  if inothing is selected..
13687                 this.fireEvent('edit', this, this.lastData );
13688                 
13689             }, this);
13690         }
13691         */
13692         
13693         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13694             "up" : function(e){
13695                 this.inKeyMode = true;
13696                 this.selectPrev();
13697             },
13698
13699             "down" : function(e){
13700                 if(!this.isExpanded()){
13701                     this.onTriggerClick();
13702                 }else{
13703                     this.inKeyMode = true;
13704                     this.selectNext();
13705                 }
13706             },
13707
13708             "enter" : function(e){
13709 //                this.onViewClick();
13710                 //return true;
13711                 this.collapse();
13712                 
13713                 if(this.fireEvent("specialkey", this, e)){
13714                     this.onViewClick(false);
13715                 }
13716                 
13717                 return true;
13718             },
13719
13720             "esc" : function(e){
13721                 this.collapse();
13722             },
13723
13724             "tab" : function(e){
13725                 this.collapse();
13726                 
13727                 if(this.fireEvent("specialkey", this, e)){
13728                     this.onViewClick(false);
13729                 }
13730                 
13731                 return true;
13732             },
13733
13734             scope : this,
13735
13736             doRelay : function(foo, bar, hname){
13737                 if(hname == 'down' || this.scope.isExpanded()){
13738                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13739                 }
13740                 return true;
13741             },
13742
13743             forceKeyDown: true
13744         });
13745         
13746         
13747         this.queryDelay = Math.max(this.queryDelay || 10,
13748                 this.mode == 'local' ? 10 : 250);
13749         
13750         
13751         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13752         
13753         if(this.typeAhead){
13754             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13755         }
13756         if(this.editable !== false){
13757             this.inputEl().on("keyup", this.onKeyUp, this);
13758         }
13759         if(this.forceSelection){
13760             this.inputEl().on('blur', this.doForce, this);
13761         }
13762         
13763         if(this.multiple){
13764             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13765             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13766         }
13767     },
13768     
13769     initTickableEvents: function()
13770     {   
13771         this.createList();
13772         
13773         if(this.hiddenName){
13774             
13775             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13776             
13777             this.hiddenField.dom.value =
13778                 this.hiddenValue !== undefined ? this.hiddenValue :
13779                 this.value !== undefined ? this.value : '';
13780
13781             // prevent input submission
13782             this.el.dom.removeAttribute('name');
13783             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13784              
13785              
13786         }
13787         
13788 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13789         
13790         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13791         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13792         if(this.triggerList){
13793             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13794         }
13795          
13796         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13797         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13798         
13799         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13800         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13801         
13802         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13803         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13804         
13805         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13806         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13807         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13808         
13809         this.okBtn.hide();
13810         this.cancelBtn.hide();
13811         
13812         var _this = this;
13813         
13814         (function(){
13815             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13816             _this.list.setWidth(lw);
13817         }).defer(100);
13818         
13819         this.list.on('mouseover', this.onViewOver, this);
13820         this.list.on('mousemove', this.onViewMove, this);
13821         
13822         this.list.on('scroll', this.onViewScroll, this);
13823         
13824         if(!this.tpl){
13825             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13826                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13827         }
13828
13829         this.view = new Roo.View(this.list, this.tpl, {
13830             singleSelect:true,
13831             tickable:true,
13832             parent:this,
13833             store: this.store,
13834             selectedClass: this.selectedClass
13835         });
13836         
13837         //this.view.wrapEl.setDisplayed(false);
13838         this.view.on('click', this.onViewClick, this);
13839         
13840         
13841         
13842         this.store.on('beforeload', this.onBeforeLoad, this);
13843         this.store.on('load', this.onLoad, this);
13844         this.store.on('loadexception', this.onLoadException, this);
13845         
13846         if(this.editable){
13847             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13848                 "up" : function(e){
13849                     this.inKeyMode = true;
13850                     this.selectPrev();
13851                 },
13852
13853                 "down" : function(e){
13854                     this.inKeyMode = true;
13855                     this.selectNext();
13856                 },
13857
13858                 "enter" : function(e){
13859                     if(this.fireEvent("specialkey", this, e)){
13860                         this.onViewClick(false);
13861                     }
13862                     
13863                     return true;
13864                 },
13865
13866                 "esc" : function(e){
13867                     this.onTickableFooterButtonClick(e, false, false);
13868                 },
13869
13870                 "tab" : function(e){
13871                     this.fireEvent("specialkey", this, e);
13872                     
13873                     this.onTickableFooterButtonClick(e, false, false);
13874                     
13875                     return true;
13876                 },
13877
13878                 scope : this,
13879
13880                 doRelay : function(e, fn, key){
13881                     if(this.scope.isExpanded()){
13882                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13883                     }
13884                     return true;
13885                 },
13886
13887                 forceKeyDown: true
13888             });
13889         }
13890         
13891         this.queryDelay = Math.max(this.queryDelay || 10,
13892                 this.mode == 'local' ? 10 : 250);
13893         
13894         
13895         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13896         
13897         if(this.typeAhead){
13898             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13899         }
13900         
13901         if(this.editable !== false){
13902             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13903         }
13904         
13905         this.indicator = this.indicatorEl();
13906         
13907         if(this.indicator){
13908             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13909             this.indicator.hide();
13910         }
13911         
13912     },
13913
13914     onDestroy : function(){
13915         if(this.view){
13916             this.view.setStore(null);
13917             this.view.el.removeAllListeners();
13918             this.view.el.remove();
13919             this.view.purgeListeners();
13920         }
13921         if(this.list){
13922             this.list.dom.innerHTML  = '';
13923         }
13924         
13925         if(this.store){
13926             this.store.un('beforeload', this.onBeforeLoad, this);
13927             this.store.un('load', this.onLoad, this);
13928             this.store.un('loadexception', this.onLoadException, this);
13929         }
13930         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13931     },
13932
13933     // private
13934     fireKey : function(e){
13935         if(e.isNavKeyPress() && !this.list.isVisible()){
13936             this.fireEvent("specialkey", this, e);
13937         }
13938     },
13939
13940     // private
13941     onResize: function(w, h){
13942 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13943 //        
13944 //        if(typeof w != 'number'){
13945 //            // we do not handle it!?!?
13946 //            return;
13947 //        }
13948 //        var tw = this.trigger.getWidth();
13949 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13950 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13951 //        var x = w - tw;
13952 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13953 //            
13954 //        //this.trigger.setStyle('left', x+'px');
13955 //        
13956 //        if(this.list && this.listWidth === undefined){
13957 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13958 //            this.list.setWidth(lw);
13959 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13960 //        }
13961         
13962     
13963         
13964     },
13965
13966     /**
13967      * Allow or prevent the user from directly editing the field text.  If false is passed,
13968      * the user will only be able to select from the items defined in the dropdown list.  This method
13969      * is the runtime equivalent of setting the 'editable' config option at config time.
13970      * @param {Boolean} value True to allow the user to directly edit the field text
13971      */
13972     setEditable : function(value){
13973         if(value == this.editable){
13974             return;
13975         }
13976         this.editable = value;
13977         if(!value){
13978             this.inputEl().dom.setAttribute('readOnly', true);
13979             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13980             this.inputEl().addClass('x-combo-noedit');
13981         }else{
13982             this.inputEl().dom.setAttribute('readOnly', false);
13983             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13984             this.inputEl().removeClass('x-combo-noedit');
13985         }
13986     },
13987
13988     // private
13989     
13990     onBeforeLoad : function(combo,opts){
13991         if(!this.hasFocus){
13992             return;
13993         }
13994          if (!opts.add) {
13995             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13996          }
13997         this.restrictHeight();
13998         this.selectedIndex = -1;
13999     },
14000
14001     // private
14002     onLoad : function(){
14003         
14004         this.hasQuery = false;
14005         
14006         if(!this.hasFocus){
14007             return;
14008         }
14009         
14010         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14011             this.loading.hide();
14012         }
14013         
14014         if(this.store.getCount() > 0){
14015             
14016             this.expand();
14017             this.restrictHeight();
14018             if(this.lastQuery == this.allQuery){
14019                 if(this.editable && !this.tickable){
14020                     this.inputEl().dom.select();
14021                 }
14022                 
14023                 if(
14024                     !this.selectByValue(this.value, true) &&
14025                     this.autoFocus && 
14026                     (
14027                         !this.store.lastOptions ||
14028                         typeof(this.store.lastOptions.add) == 'undefined' || 
14029                         this.store.lastOptions.add != true
14030                     )
14031                 ){
14032                     this.select(0, true);
14033                 }
14034             }else{
14035                 if(this.autoFocus){
14036                     this.selectNext();
14037                 }
14038                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14039                     this.taTask.delay(this.typeAheadDelay);
14040                 }
14041             }
14042         }else{
14043             this.onEmptyResults();
14044         }
14045         
14046         //this.el.focus();
14047     },
14048     // private
14049     onLoadException : function()
14050     {
14051         this.hasQuery = false;
14052         
14053         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14054             this.loading.hide();
14055         }
14056         
14057         if(this.tickable && this.editable){
14058             return;
14059         }
14060         
14061         this.collapse();
14062         // only causes errors at present
14063         //Roo.log(this.store.reader.jsonData);
14064         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14065             // fixme
14066             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14067         //}
14068         
14069         
14070     },
14071     // private
14072     onTypeAhead : function(){
14073         if(this.store.getCount() > 0){
14074             var r = this.store.getAt(0);
14075             var newValue = r.data[this.displayField];
14076             var len = newValue.length;
14077             var selStart = this.getRawValue().length;
14078             
14079             if(selStart != len){
14080                 this.setRawValue(newValue);
14081                 this.selectText(selStart, newValue.length);
14082             }
14083         }
14084     },
14085
14086     // private
14087     onSelect : function(record, index){
14088         
14089         if(this.fireEvent('beforeselect', this, record, index) !== false){
14090         
14091             this.setFromData(index > -1 ? record.data : false);
14092             
14093             this.collapse();
14094             this.fireEvent('select', this, record, index);
14095         }
14096     },
14097
14098     /**
14099      * Returns the currently selected field value or empty string if no value is set.
14100      * @return {String} value The selected value
14101      */
14102     getValue : function()
14103     {
14104         if(Roo.isIOS && this.useNativeIOS){
14105             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14106         }
14107         
14108         if(this.multiple){
14109             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14110         }
14111         
14112         if(this.valueField){
14113             return typeof this.value != 'undefined' ? this.value : '';
14114         }else{
14115             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14116         }
14117     },
14118     
14119     getRawValue : function()
14120     {
14121         if(Roo.isIOS && this.useNativeIOS){
14122             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14123         }
14124         
14125         var v = this.inputEl().getValue();
14126         
14127         return v;
14128     },
14129
14130     /**
14131      * Clears any text/value currently set in the field
14132      */
14133     clearValue : function(){
14134         
14135         if(this.hiddenField){
14136             this.hiddenField.dom.value = '';
14137         }
14138         this.value = '';
14139         this.setRawValue('');
14140         this.lastSelectionText = '';
14141         this.lastData = false;
14142         
14143         var close = this.closeTriggerEl();
14144         
14145         if(close){
14146             close.hide();
14147         }
14148         
14149         this.validate();
14150         
14151     },
14152
14153     /**
14154      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14155      * will be displayed in the field.  If the value does not match the data value of an existing item,
14156      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14157      * Otherwise the field will be blank (although the value will still be set).
14158      * @param {String} value The value to match
14159      */
14160     setValue : function(v)
14161     {
14162         if(Roo.isIOS && this.useNativeIOS){
14163             this.setIOSValue(v);
14164             return;
14165         }
14166         
14167         if(this.multiple){
14168             this.syncValue();
14169             return;
14170         }
14171         
14172         var text = v;
14173         if(this.valueField){
14174             var r = this.findRecord(this.valueField, v);
14175             if(r){
14176                 text = r.data[this.displayField];
14177             }else if(this.valueNotFoundText !== undefined){
14178                 text = this.valueNotFoundText;
14179             }
14180         }
14181         this.lastSelectionText = text;
14182         if(this.hiddenField){
14183             this.hiddenField.dom.value = v;
14184         }
14185         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14186         this.value = v;
14187         
14188         var close = this.closeTriggerEl();
14189         
14190         if(close){
14191             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14192         }
14193         
14194         this.validate();
14195     },
14196     /**
14197      * @property {Object} the last set data for the element
14198      */
14199     
14200     lastData : false,
14201     /**
14202      * Sets the value of the field based on a object which is related to the record format for the store.
14203      * @param {Object} value the value to set as. or false on reset?
14204      */
14205     setFromData : function(o){
14206         
14207         if(this.multiple){
14208             this.addItem(o);
14209             return;
14210         }
14211             
14212         var dv = ''; // display value
14213         var vv = ''; // value value..
14214         this.lastData = o;
14215         if (this.displayField) {
14216             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14217         } else {
14218             // this is an error condition!!!
14219             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14220         }
14221         
14222         if(this.valueField){
14223             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14224         }
14225         
14226         var close = this.closeTriggerEl();
14227         
14228         if(close){
14229             if(dv.length || vv * 1 > 0){
14230                 close.show() ;
14231                 this.blockFocus=true;
14232             } else {
14233                 close.hide();
14234             }             
14235         }
14236         
14237         if(this.hiddenField){
14238             this.hiddenField.dom.value = vv;
14239             
14240             this.lastSelectionText = dv;
14241             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14242             this.value = vv;
14243             return;
14244         }
14245         // no hidden field.. - we store the value in 'value', but still display
14246         // display field!!!!
14247         this.lastSelectionText = dv;
14248         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14249         this.value = vv;
14250         
14251         
14252         
14253     },
14254     // private
14255     reset : function(){
14256         // overridden so that last data is reset..
14257         
14258         if(this.multiple){
14259             this.clearItem();
14260             return;
14261         }
14262         
14263         this.setValue(this.originalValue);
14264         //this.clearInvalid();
14265         this.lastData = false;
14266         if (this.view) {
14267             this.view.clearSelections();
14268         }
14269         
14270         this.validate();
14271     },
14272     // private
14273     findRecord : function(prop, value){
14274         var record;
14275         if(this.store.getCount() > 0){
14276             this.store.each(function(r){
14277                 if(r.data[prop] == value){
14278                     record = r;
14279                     return false;
14280                 }
14281                 return true;
14282             });
14283         }
14284         return record;
14285     },
14286     
14287     getName: function()
14288     {
14289         // returns hidden if it's set..
14290         if (!this.rendered) {return ''};
14291         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14292         
14293     },
14294     // private
14295     onViewMove : function(e, t){
14296         this.inKeyMode = false;
14297     },
14298
14299     // private
14300     onViewOver : function(e, t){
14301         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14302             return;
14303         }
14304         var item = this.view.findItemFromChild(t);
14305         
14306         if(item){
14307             var index = this.view.indexOf(item);
14308             this.select(index, false);
14309         }
14310     },
14311
14312     // private
14313     onViewClick : function(view, doFocus, el, e)
14314     {
14315         var index = this.view.getSelectedIndexes()[0];
14316         
14317         var r = this.store.getAt(index);
14318         
14319         if(this.tickable){
14320             
14321             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14322                 return;
14323             }
14324             
14325             var rm = false;
14326             var _this = this;
14327             
14328             Roo.each(this.tickItems, function(v,k){
14329                 
14330                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14331                     Roo.log(v);
14332                     _this.tickItems.splice(k, 1);
14333                     
14334                     if(typeof(e) == 'undefined' && view == false){
14335                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14336                     }
14337                     
14338                     rm = true;
14339                     return;
14340                 }
14341             });
14342             
14343             if(rm){
14344                 return;
14345             }
14346             
14347             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14348                 this.tickItems.push(r.data);
14349             }
14350             
14351             if(typeof(e) == 'undefined' && view == false){
14352                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14353             }
14354                     
14355             return;
14356         }
14357         
14358         if(r){
14359             this.onSelect(r, index);
14360         }
14361         if(doFocus !== false && !this.blockFocus){
14362             this.inputEl().focus();
14363         }
14364     },
14365
14366     // private
14367     restrictHeight : function(){
14368         //this.innerList.dom.style.height = '';
14369         //var inner = this.innerList.dom;
14370         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14371         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14372         //this.list.beginUpdate();
14373         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14374         this.list.alignTo(this.inputEl(), this.listAlign);
14375         this.list.alignTo(this.inputEl(), this.listAlign);
14376         //this.list.endUpdate();
14377     },
14378
14379     // private
14380     onEmptyResults : function(){
14381         
14382         if(this.tickable && this.editable){
14383             this.hasFocus = false;
14384             this.restrictHeight();
14385             return;
14386         }
14387         
14388         this.collapse();
14389     },
14390
14391     /**
14392      * Returns true if the dropdown list is expanded, else false.
14393      */
14394     isExpanded : function(){
14395         return this.list.isVisible();
14396     },
14397
14398     /**
14399      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14400      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14401      * @param {String} value The data value of the item to select
14402      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14403      * selected item if it is not currently in view (defaults to true)
14404      * @return {Boolean} True if the value matched an item in the list, else false
14405      */
14406     selectByValue : function(v, scrollIntoView){
14407         if(v !== undefined && v !== null){
14408             var r = this.findRecord(this.valueField || this.displayField, v);
14409             if(r){
14410                 this.select(this.store.indexOf(r), scrollIntoView);
14411                 return true;
14412             }
14413         }
14414         return false;
14415     },
14416
14417     /**
14418      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14419      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14420      * @param {Number} index The zero-based index of the list item to select
14421      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14422      * selected item if it is not currently in view (defaults to true)
14423      */
14424     select : function(index, scrollIntoView){
14425         this.selectedIndex = index;
14426         this.view.select(index);
14427         if(scrollIntoView !== false){
14428             var el = this.view.getNode(index);
14429             /*
14430              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14431              */
14432             if(el){
14433                 this.list.scrollChildIntoView(el, false);
14434             }
14435         }
14436     },
14437
14438     // private
14439     selectNext : function(){
14440         var ct = this.store.getCount();
14441         if(ct > 0){
14442             if(this.selectedIndex == -1){
14443                 this.select(0);
14444             }else if(this.selectedIndex < ct-1){
14445                 this.select(this.selectedIndex+1);
14446             }
14447         }
14448     },
14449
14450     // private
14451     selectPrev : function(){
14452         var ct = this.store.getCount();
14453         if(ct > 0){
14454             if(this.selectedIndex == -1){
14455                 this.select(0);
14456             }else if(this.selectedIndex != 0){
14457                 this.select(this.selectedIndex-1);
14458             }
14459         }
14460     },
14461
14462     // private
14463     onKeyUp : function(e){
14464         if(this.editable !== false && !e.isSpecialKey()){
14465             this.lastKey = e.getKey();
14466             this.dqTask.delay(this.queryDelay);
14467         }
14468     },
14469
14470     // private
14471     validateBlur : function(){
14472         return !this.list || !this.list.isVisible();   
14473     },
14474
14475     // private
14476     initQuery : function(){
14477         
14478         var v = this.getRawValue();
14479         
14480         if(this.tickable && this.editable){
14481             v = this.tickableInputEl().getValue();
14482         }
14483         
14484         this.doQuery(v);
14485     },
14486
14487     // private
14488     doForce : function(){
14489         if(this.inputEl().dom.value.length > 0){
14490             this.inputEl().dom.value =
14491                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14492              
14493         }
14494     },
14495
14496     /**
14497      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14498      * query allowing the query action to be canceled if needed.
14499      * @param {String} query The SQL query to execute
14500      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14501      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14502      * saved in the current store (defaults to false)
14503      */
14504     doQuery : function(q, forceAll){
14505         
14506         if(q === undefined || q === null){
14507             q = '';
14508         }
14509         var qe = {
14510             query: q,
14511             forceAll: forceAll,
14512             combo: this,
14513             cancel:false
14514         };
14515         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14516             return false;
14517         }
14518         q = qe.query;
14519         
14520         forceAll = qe.forceAll;
14521         if(forceAll === true || (q.length >= this.minChars)){
14522             
14523             this.hasQuery = true;
14524             
14525             if(this.lastQuery != q || this.alwaysQuery){
14526                 this.lastQuery = q;
14527                 if(this.mode == 'local'){
14528                     this.selectedIndex = -1;
14529                     if(forceAll){
14530                         this.store.clearFilter();
14531                     }else{
14532                         
14533                         if(this.specialFilter){
14534                             this.fireEvent('specialfilter', this);
14535                             this.onLoad();
14536                             return;
14537                         }
14538                         
14539                         this.store.filter(this.displayField, q);
14540                     }
14541                     
14542                     this.store.fireEvent("datachanged", this.store);
14543                     
14544                     this.onLoad();
14545                     
14546                     
14547                 }else{
14548                     
14549                     this.store.baseParams[this.queryParam] = q;
14550                     
14551                     var options = {params : this.getParams(q)};
14552                     
14553                     if(this.loadNext){
14554                         options.add = true;
14555                         options.params.start = this.page * this.pageSize;
14556                     }
14557                     
14558                     this.store.load(options);
14559                     
14560                     /*
14561                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14562                      *  we should expand the list on onLoad
14563                      *  so command out it
14564                      */
14565 //                    this.expand();
14566                 }
14567             }else{
14568                 this.selectedIndex = -1;
14569                 this.onLoad();   
14570             }
14571         }
14572         
14573         this.loadNext = false;
14574     },
14575     
14576     // private
14577     getParams : function(q){
14578         var p = {};
14579         //p[this.queryParam] = q;
14580         
14581         if(this.pageSize){
14582             p.start = 0;
14583             p.limit = this.pageSize;
14584         }
14585         return p;
14586     },
14587
14588     /**
14589      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14590      */
14591     collapse : function(){
14592         if(!this.isExpanded()){
14593             return;
14594         }
14595         
14596         this.list.hide();
14597         
14598         this.hasFocus = false;
14599         
14600         if(this.tickable){
14601             this.okBtn.hide();
14602             this.cancelBtn.hide();
14603             this.trigger.show();
14604             
14605             if(this.editable){
14606                 this.tickableInputEl().dom.value = '';
14607                 this.tickableInputEl().blur();
14608             }
14609             
14610         }
14611         
14612         Roo.get(document).un('mousedown', this.collapseIf, this);
14613         Roo.get(document).un('mousewheel', this.collapseIf, this);
14614         if (!this.editable) {
14615             Roo.get(document).un('keydown', this.listKeyPress, this);
14616         }
14617         this.fireEvent('collapse', this);
14618         
14619         this.validate();
14620     },
14621
14622     // private
14623     collapseIf : function(e){
14624         var in_combo  = e.within(this.el);
14625         var in_list =  e.within(this.list);
14626         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14627         
14628         if (in_combo || in_list || is_list) {
14629             //e.stopPropagation();
14630             return;
14631         }
14632         
14633         if(this.tickable){
14634             this.onTickableFooterButtonClick(e, false, false);
14635         }
14636
14637         this.collapse();
14638         
14639     },
14640
14641     /**
14642      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14643      */
14644     expand : function(){
14645        
14646         if(this.isExpanded() || !this.hasFocus){
14647             return;
14648         }
14649         
14650         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14651         this.list.setWidth(lw);
14652         
14653         Roo.log('expand');
14654         
14655         this.list.show();
14656         
14657         this.restrictHeight();
14658         
14659         if(this.tickable){
14660             
14661             this.tickItems = Roo.apply([], this.item);
14662             
14663             this.okBtn.show();
14664             this.cancelBtn.show();
14665             this.trigger.hide();
14666             
14667             if(this.editable){
14668                 this.tickableInputEl().focus();
14669             }
14670             
14671         }
14672         
14673         Roo.get(document).on('mousedown', this.collapseIf, this);
14674         Roo.get(document).on('mousewheel', this.collapseIf, this);
14675         if (!this.editable) {
14676             Roo.get(document).on('keydown', this.listKeyPress, this);
14677         }
14678         
14679         this.fireEvent('expand', this);
14680     },
14681
14682     // private
14683     // Implements the default empty TriggerField.onTriggerClick function
14684     onTriggerClick : function(e)
14685     {
14686         Roo.log('trigger click');
14687         
14688         if(this.disabled || !this.triggerList){
14689             return;
14690         }
14691         
14692         this.page = 0;
14693         this.loadNext = false;
14694         
14695         if(this.isExpanded()){
14696             this.collapse();
14697             if (!this.blockFocus) {
14698                 this.inputEl().focus();
14699             }
14700             
14701         }else {
14702             this.hasFocus = true;
14703             if(this.triggerAction == 'all') {
14704                 this.doQuery(this.allQuery, true);
14705             } else {
14706                 this.doQuery(this.getRawValue());
14707             }
14708             if (!this.blockFocus) {
14709                 this.inputEl().focus();
14710             }
14711         }
14712     },
14713     
14714     onTickableTriggerClick : function(e)
14715     {
14716         if(this.disabled){
14717             return;
14718         }
14719         
14720         this.page = 0;
14721         this.loadNext = false;
14722         this.hasFocus = true;
14723         
14724         if(this.triggerAction == 'all') {
14725             this.doQuery(this.allQuery, true);
14726         } else {
14727             this.doQuery(this.getRawValue());
14728         }
14729     },
14730     
14731     onSearchFieldClick : function(e)
14732     {
14733         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14734             this.onTickableFooterButtonClick(e, false, false);
14735             return;
14736         }
14737         
14738         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14739             return;
14740         }
14741         
14742         this.page = 0;
14743         this.loadNext = false;
14744         this.hasFocus = true;
14745         
14746         if(this.triggerAction == 'all') {
14747             this.doQuery(this.allQuery, true);
14748         } else {
14749             this.doQuery(this.getRawValue());
14750         }
14751     },
14752     
14753     listKeyPress : function(e)
14754     {
14755         //Roo.log('listkeypress');
14756         // scroll to first matching element based on key pres..
14757         if (e.isSpecialKey()) {
14758             return false;
14759         }
14760         var k = String.fromCharCode(e.getKey()).toUpperCase();
14761         //Roo.log(k);
14762         var match  = false;
14763         var csel = this.view.getSelectedNodes();
14764         var cselitem = false;
14765         if (csel.length) {
14766             var ix = this.view.indexOf(csel[0]);
14767             cselitem  = this.store.getAt(ix);
14768             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14769                 cselitem = false;
14770             }
14771             
14772         }
14773         
14774         this.store.each(function(v) { 
14775             if (cselitem) {
14776                 // start at existing selection.
14777                 if (cselitem.id == v.id) {
14778                     cselitem = false;
14779                 }
14780                 return true;
14781             }
14782                 
14783             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14784                 match = this.store.indexOf(v);
14785                 return false;
14786             }
14787             return true;
14788         }, this);
14789         
14790         if (match === false) {
14791             return true; // no more action?
14792         }
14793         // scroll to?
14794         this.view.select(match);
14795         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14796         sn.scrollIntoView(sn.dom.parentNode, false);
14797     },
14798     
14799     onViewScroll : function(e, t){
14800         
14801         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){
14802             return;
14803         }
14804         
14805         this.hasQuery = true;
14806         
14807         this.loading = this.list.select('.loading', true).first();
14808         
14809         if(this.loading === null){
14810             this.list.createChild({
14811                 tag: 'div',
14812                 cls: 'loading roo-select2-more-results roo-select2-active',
14813                 html: 'Loading more results...'
14814             });
14815             
14816             this.loading = this.list.select('.loading', true).first();
14817             
14818             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14819             
14820             this.loading.hide();
14821         }
14822         
14823         this.loading.show();
14824         
14825         var _combo = this;
14826         
14827         this.page++;
14828         this.loadNext = true;
14829         
14830         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14831         
14832         return;
14833     },
14834     
14835     addItem : function(o)
14836     {   
14837         var dv = ''; // display value
14838         
14839         if (this.displayField) {
14840             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14841         } else {
14842             // this is an error condition!!!
14843             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14844         }
14845         
14846         if(!dv.length){
14847             return;
14848         }
14849         
14850         var choice = this.choices.createChild({
14851             tag: 'li',
14852             cls: 'roo-select2-search-choice',
14853             cn: [
14854                 {
14855                     tag: 'div',
14856                     html: dv
14857                 },
14858                 {
14859                     tag: 'a',
14860                     href: '#',
14861                     cls: 'roo-select2-search-choice-close fa fa-times',
14862                     tabindex: '-1'
14863                 }
14864             ]
14865             
14866         }, this.searchField);
14867         
14868         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14869         
14870         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14871         
14872         this.item.push(o);
14873         
14874         this.lastData = o;
14875         
14876         this.syncValue();
14877         
14878         this.inputEl().dom.value = '';
14879         
14880         this.validate();
14881     },
14882     
14883     onRemoveItem : function(e, _self, o)
14884     {
14885         e.preventDefault();
14886         
14887         this.lastItem = Roo.apply([], this.item);
14888         
14889         var index = this.item.indexOf(o.data) * 1;
14890         
14891         if( index < 0){
14892             Roo.log('not this item?!');
14893             return;
14894         }
14895         
14896         this.item.splice(index, 1);
14897         o.item.remove();
14898         
14899         this.syncValue();
14900         
14901         this.fireEvent('remove', this, e);
14902         
14903         this.validate();
14904         
14905     },
14906     
14907     syncValue : function()
14908     {
14909         if(!this.item.length){
14910             this.clearValue();
14911             return;
14912         }
14913             
14914         var value = [];
14915         var _this = this;
14916         Roo.each(this.item, function(i){
14917             if(_this.valueField){
14918                 value.push(i[_this.valueField]);
14919                 return;
14920             }
14921
14922             value.push(i);
14923         });
14924
14925         this.value = value.join(',');
14926
14927         if(this.hiddenField){
14928             this.hiddenField.dom.value = this.value;
14929         }
14930         
14931         this.store.fireEvent("datachanged", this.store);
14932         
14933         this.validate();
14934     },
14935     
14936     clearItem : function()
14937     {
14938         if(!this.multiple){
14939             return;
14940         }
14941         
14942         this.item = [];
14943         
14944         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14945            c.remove();
14946         });
14947         
14948         this.syncValue();
14949         
14950         this.validate();
14951         
14952         if(this.tickable && !Roo.isTouch){
14953             this.view.refresh();
14954         }
14955     },
14956     
14957     inputEl: function ()
14958     {
14959         if(Roo.isIOS && this.useNativeIOS){
14960             return this.el.select('select.roo-ios-select', true).first();
14961         }
14962         
14963         if(Roo.isTouch && this.mobileTouchView){
14964             return this.el.select('input.form-control',true).first();
14965         }
14966         
14967         if(this.tickable){
14968             return this.searchField;
14969         }
14970         
14971         return this.el.select('input.form-control',true).first();
14972     },
14973     
14974     onTickableFooterButtonClick : function(e, btn, el)
14975     {
14976         e.preventDefault();
14977         
14978         this.lastItem = Roo.apply([], this.item);
14979         
14980         if(btn && btn.name == 'cancel'){
14981             this.tickItems = Roo.apply([], this.item);
14982             this.collapse();
14983             return;
14984         }
14985         
14986         this.clearItem();
14987         
14988         var _this = this;
14989         
14990         Roo.each(this.tickItems, function(o){
14991             _this.addItem(o);
14992         });
14993         
14994         this.collapse();
14995         
14996     },
14997     
14998     validate : function()
14999     {
15000         if(this.getVisibilityEl().hasClass('hidden')){
15001             return true;
15002         }
15003         
15004         var v = this.getRawValue();
15005         
15006         if(this.multiple){
15007             v = this.getValue();
15008         }
15009         
15010         if(this.disabled || this.allowBlank || v.length){
15011             this.markValid();
15012             return true;
15013         }
15014         
15015         this.markInvalid();
15016         return false;
15017     },
15018     
15019     tickableInputEl : function()
15020     {
15021         if(!this.tickable || !this.editable){
15022             return this.inputEl();
15023         }
15024         
15025         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15026     },
15027     
15028     
15029     getAutoCreateTouchView : function()
15030     {
15031         var id = Roo.id();
15032         
15033         var cfg = {
15034             cls: 'form-group' //input-group
15035         };
15036         
15037         var input =  {
15038             tag: 'input',
15039             id : id,
15040             type : this.inputType,
15041             cls : 'form-control x-combo-noedit',
15042             autocomplete: 'new-password',
15043             placeholder : this.placeholder || '',
15044             readonly : true
15045         };
15046         
15047         if (this.name) {
15048             input.name = this.name;
15049         }
15050         
15051         if (this.size) {
15052             input.cls += ' input-' + this.size;
15053         }
15054         
15055         if (this.disabled) {
15056             input.disabled = true;
15057         }
15058         
15059         var inputblock = {
15060             cls : '',
15061             cn : [
15062                 input
15063             ]
15064         };
15065         
15066         if(this.before){
15067             inputblock.cls += ' input-group';
15068             
15069             inputblock.cn.unshift({
15070                 tag :'span',
15071                 cls : 'input-group-addon input-group-prepend input-group-text',
15072                 html : this.before
15073             });
15074         }
15075         
15076         if(this.removable && !this.multiple){
15077             inputblock.cls += ' roo-removable';
15078             
15079             inputblock.cn.push({
15080                 tag: 'button',
15081                 html : 'x',
15082                 cls : 'roo-combo-removable-btn close'
15083             });
15084         }
15085
15086         if(this.hasFeedback && !this.allowBlank){
15087             
15088             inputblock.cls += ' has-feedback';
15089             
15090             inputblock.cn.push({
15091                 tag: 'span',
15092                 cls: 'glyphicon form-control-feedback'
15093             });
15094             
15095         }
15096         
15097         if (this.after) {
15098             
15099             inputblock.cls += (this.before) ? '' : ' input-group';
15100             
15101             inputblock.cn.push({
15102                 tag :'span',
15103                 cls : 'input-group-addon input-group-append input-group-text',
15104                 html : this.after
15105             });
15106         }
15107
15108         
15109         var ibwrap = inputblock;
15110         
15111         if(this.multiple){
15112             ibwrap = {
15113                 tag: 'ul',
15114                 cls: 'roo-select2-choices',
15115                 cn:[
15116                     {
15117                         tag: 'li',
15118                         cls: 'roo-select2-search-field',
15119                         cn: [
15120
15121                             inputblock
15122                         ]
15123                     }
15124                 ]
15125             };
15126         
15127             
15128         }
15129         
15130         var combobox = {
15131             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15132             cn: [
15133                 {
15134                     tag: 'input',
15135                     type : 'hidden',
15136                     cls: 'form-hidden-field'
15137                 },
15138                 ibwrap
15139             ]
15140         };
15141         
15142         if(!this.multiple && this.showToggleBtn){
15143             
15144             var caret = {
15145                         tag: 'span',
15146                         cls: 'caret'
15147             };
15148             
15149             if (this.caret != false) {
15150                 caret = {
15151                      tag: 'i',
15152                      cls: 'fa fa-' + this.caret
15153                 };
15154                 
15155             }
15156             
15157             combobox.cn.push({
15158                 tag :'span',
15159                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15160                 cn : [
15161                     caret,
15162                     {
15163                         tag: 'span',
15164                         cls: 'combobox-clear',
15165                         cn  : [
15166                             {
15167                                 tag : 'i',
15168                                 cls: 'icon-remove'
15169                             }
15170                         ]
15171                     }
15172                 ]
15173
15174             })
15175         }
15176         
15177         if(this.multiple){
15178             combobox.cls += ' roo-select2-container-multi';
15179         }
15180         
15181         var align = this.labelAlign || this.parentLabelAlign();
15182         
15183         if (align ==='left' && this.fieldLabel.length) {
15184
15185             cfg.cn = [
15186                 {
15187                    tag : 'i',
15188                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15189                    tooltip : 'This field is required'
15190                 },
15191                 {
15192                     tag: 'label',
15193                     cls : 'control-label col-form-label',
15194                     html : this.fieldLabel
15195
15196                 },
15197                 {
15198                     cls : '', 
15199                     cn: [
15200                         combobox
15201                     ]
15202                 }
15203             ];
15204             
15205             var labelCfg = cfg.cn[1];
15206             var contentCfg = cfg.cn[2];
15207             
15208
15209             if(this.indicatorpos == 'right'){
15210                 cfg.cn = [
15211                     {
15212                         tag: 'label',
15213                         'for' :  id,
15214                         cls : 'control-label col-form-label',
15215                         cn : [
15216                             {
15217                                 tag : 'span',
15218                                 html : this.fieldLabel
15219                             },
15220                             {
15221                                 tag : 'i',
15222                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15223                                 tooltip : 'This field is required'
15224                             }
15225                         ]
15226                     },
15227                     {
15228                         cls : "",
15229                         cn: [
15230                             combobox
15231                         ]
15232                     }
15233
15234                 ];
15235                 
15236                 labelCfg = cfg.cn[0];
15237                 contentCfg = cfg.cn[1];
15238             }
15239             
15240            
15241             
15242             if(this.labelWidth > 12){
15243                 labelCfg.style = "width: " + this.labelWidth + 'px';
15244             }
15245             
15246             if(this.labelWidth < 13 && this.labelmd == 0){
15247                 this.labelmd = this.labelWidth;
15248             }
15249             
15250             if(this.labellg > 0){
15251                 labelCfg.cls += ' col-lg-' + this.labellg;
15252                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15253             }
15254             
15255             if(this.labelmd > 0){
15256                 labelCfg.cls += ' col-md-' + this.labelmd;
15257                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15258             }
15259             
15260             if(this.labelsm > 0){
15261                 labelCfg.cls += ' col-sm-' + this.labelsm;
15262                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15263             }
15264             
15265             if(this.labelxs > 0){
15266                 labelCfg.cls += ' col-xs-' + this.labelxs;
15267                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15268             }
15269                 
15270                 
15271         } else if ( this.fieldLabel.length) {
15272             cfg.cn = [
15273                 {
15274                    tag : 'i',
15275                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15276                    tooltip : 'This field is required'
15277                 },
15278                 {
15279                     tag: 'label',
15280                     cls : 'control-label',
15281                     html : this.fieldLabel
15282
15283                 },
15284                 {
15285                     cls : '', 
15286                     cn: [
15287                         combobox
15288                     ]
15289                 }
15290             ];
15291             
15292             if(this.indicatorpos == 'right'){
15293                 cfg.cn = [
15294                     {
15295                         tag: 'label',
15296                         cls : 'control-label',
15297                         html : this.fieldLabel,
15298                         cn : [
15299                             {
15300                                tag : 'i',
15301                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15302                                tooltip : 'This field is required'
15303                             }
15304                         ]
15305                     },
15306                     {
15307                         cls : '', 
15308                         cn: [
15309                             combobox
15310                         ]
15311                     }
15312                 ];
15313             }
15314         } else {
15315             cfg.cn = combobox;    
15316         }
15317         
15318         
15319         var settings = this;
15320         
15321         ['xs','sm','md','lg'].map(function(size){
15322             if (settings[size]) {
15323                 cfg.cls += ' col-' + size + '-' + settings[size];
15324             }
15325         });
15326         
15327         return cfg;
15328     },
15329     
15330     initTouchView : function()
15331     {
15332         this.renderTouchView();
15333         
15334         this.touchViewEl.on('scroll', function(){
15335             this.el.dom.scrollTop = 0;
15336         }, this);
15337         
15338         this.originalValue = this.getValue();
15339         
15340         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15341         
15342         this.inputEl().on("click", this.showTouchView, this);
15343         if (this.triggerEl) {
15344             this.triggerEl.on("click", this.showTouchView, this);
15345         }
15346         
15347         
15348         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15349         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15350         
15351         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15352         
15353         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15354         this.store.on('load', this.onTouchViewLoad, this);
15355         this.store.on('loadexception', this.onTouchViewLoadException, this);
15356         
15357         if(this.hiddenName){
15358             
15359             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15360             
15361             this.hiddenField.dom.value =
15362                 this.hiddenValue !== undefined ? this.hiddenValue :
15363                 this.value !== undefined ? this.value : '';
15364         
15365             this.el.dom.removeAttribute('name');
15366             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15367         }
15368         
15369         if(this.multiple){
15370             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15371             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15372         }
15373         
15374         if(this.removable && !this.multiple){
15375             var close = this.closeTriggerEl();
15376             if(close){
15377                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15378                 close.on('click', this.removeBtnClick, this, close);
15379             }
15380         }
15381         /*
15382          * fix the bug in Safari iOS8
15383          */
15384         this.inputEl().on("focus", function(e){
15385             document.activeElement.blur();
15386         }, this);
15387         
15388         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15389         
15390         return;
15391         
15392         
15393     },
15394     
15395     renderTouchView : function()
15396     {
15397         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15398         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15399         
15400         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15401         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15402         
15403         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15404         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15405         this.touchViewBodyEl.setStyle('overflow', 'auto');
15406         
15407         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15408         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15409         
15410         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15411         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15412         
15413     },
15414     
15415     showTouchView : function()
15416     {
15417         if(this.disabled){
15418             return;
15419         }
15420         
15421         this.touchViewHeaderEl.hide();
15422
15423         if(this.modalTitle.length){
15424             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15425             this.touchViewHeaderEl.show();
15426         }
15427
15428         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15429         this.touchViewEl.show();
15430
15431         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15432         
15433         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15434         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15435
15436         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15437
15438         if(this.modalTitle.length){
15439             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15440         }
15441         
15442         this.touchViewBodyEl.setHeight(bodyHeight);
15443
15444         if(this.animate){
15445             var _this = this;
15446             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15447         }else{
15448             this.touchViewEl.addClass('in');
15449         }
15450         
15451         if(this._touchViewMask){
15452             Roo.get(document.body).addClass("x-body-masked");
15453             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15454             this._touchViewMask.setStyle('z-index', 10000);
15455             this._touchViewMask.addClass('show');
15456         }
15457         
15458         this.doTouchViewQuery();
15459         
15460     },
15461     
15462     hideTouchView : function()
15463     {
15464         this.touchViewEl.removeClass('in');
15465
15466         if(this.animate){
15467             var _this = this;
15468             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15469         }else{
15470             this.touchViewEl.setStyle('display', 'none');
15471         }
15472         
15473         if(this._touchViewMask){
15474             this._touchViewMask.removeClass('show');
15475             Roo.get(document.body).removeClass("x-body-masked");
15476         }
15477     },
15478     
15479     setTouchViewValue : function()
15480     {
15481         if(this.multiple){
15482             this.clearItem();
15483         
15484             var _this = this;
15485
15486             Roo.each(this.tickItems, function(o){
15487                 this.addItem(o);
15488             }, this);
15489         }
15490         
15491         this.hideTouchView();
15492     },
15493     
15494     doTouchViewQuery : function()
15495     {
15496         var qe = {
15497             query: '',
15498             forceAll: true,
15499             combo: this,
15500             cancel:false
15501         };
15502         
15503         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15504             return false;
15505         }
15506         
15507         if(!this.alwaysQuery || this.mode == 'local'){
15508             this.onTouchViewLoad();
15509             return;
15510         }
15511         
15512         this.store.load();
15513     },
15514     
15515     onTouchViewBeforeLoad : function(combo,opts)
15516     {
15517         return;
15518     },
15519
15520     // private
15521     onTouchViewLoad : function()
15522     {
15523         if(this.store.getCount() < 1){
15524             this.onTouchViewEmptyResults();
15525             return;
15526         }
15527         
15528         this.clearTouchView();
15529         
15530         var rawValue = this.getRawValue();
15531         
15532         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15533         
15534         this.tickItems = [];
15535         
15536         this.store.data.each(function(d, rowIndex){
15537             var row = this.touchViewListGroup.createChild(template);
15538             
15539             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15540                 row.addClass(d.data.cls);
15541             }
15542             
15543             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15544                 var cfg = {
15545                     data : d.data,
15546                     html : d.data[this.displayField]
15547                 };
15548                 
15549                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15550                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15551                 }
15552             }
15553             row.removeClass('selected');
15554             if(!this.multiple && this.valueField &&
15555                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15556             {
15557                 // radio buttons..
15558                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15559                 row.addClass('selected');
15560             }
15561             
15562             if(this.multiple && this.valueField &&
15563                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15564             {
15565                 
15566                 // checkboxes...
15567                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15568                 this.tickItems.push(d.data);
15569             }
15570             
15571             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15572             
15573         }, this);
15574         
15575         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15576         
15577         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15578
15579         if(this.modalTitle.length){
15580             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15581         }
15582
15583         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15584         
15585         if(this.mobile_restrict_height && listHeight < bodyHeight){
15586             this.touchViewBodyEl.setHeight(listHeight);
15587         }
15588         
15589         var _this = this;
15590         
15591         if(firstChecked && listHeight > bodyHeight){
15592             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15593         }
15594         
15595     },
15596     
15597     onTouchViewLoadException : function()
15598     {
15599         this.hideTouchView();
15600     },
15601     
15602     onTouchViewEmptyResults : function()
15603     {
15604         this.clearTouchView();
15605         
15606         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15607         
15608         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15609         
15610     },
15611     
15612     clearTouchView : function()
15613     {
15614         this.touchViewListGroup.dom.innerHTML = '';
15615     },
15616     
15617     onTouchViewClick : function(e, el, o)
15618     {
15619         e.preventDefault();
15620         
15621         var row = o.row;
15622         var rowIndex = o.rowIndex;
15623         
15624         var r = this.store.getAt(rowIndex);
15625         
15626         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15627             
15628             if(!this.multiple){
15629                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15630                     c.dom.removeAttribute('checked');
15631                 }, this);
15632
15633                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15634
15635                 this.setFromData(r.data);
15636
15637                 var close = this.closeTriggerEl();
15638
15639                 if(close){
15640                     close.show();
15641                 }
15642
15643                 this.hideTouchView();
15644
15645                 this.fireEvent('select', this, r, rowIndex);
15646
15647                 return;
15648             }
15649
15650             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15651                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15652                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15653                 return;
15654             }
15655
15656             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15657             this.addItem(r.data);
15658             this.tickItems.push(r.data);
15659         }
15660     },
15661     
15662     getAutoCreateNativeIOS : function()
15663     {
15664         var cfg = {
15665             cls: 'form-group' //input-group,
15666         };
15667         
15668         var combobox =  {
15669             tag: 'select',
15670             cls : 'roo-ios-select'
15671         };
15672         
15673         if (this.name) {
15674             combobox.name = this.name;
15675         }
15676         
15677         if (this.disabled) {
15678             combobox.disabled = true;
15679         }
15680         
15681         var settings = this;
15682         
15683         ['xs','sm','md','lg'].map(function(size){
15684             if (settings[size]) {
15685                 cfg.cls += ' col-' + size + '-' + settings[size];
15686             }
15687         });
15688         
15689         cfg.cn = combobox;
15690         
15691         return cfg;
15692         
15693     },
15694     
15695     initIOSView : function()
15696     {
15697         this.store.on('load', this.onIOSViewLoad, this);
15698         
15699         return;
15700     },
15701     
15702     onIOSViewLoad : function()
15703     {
15704         if(this.store.getCount() < 1){
15705             return;
15706         }
15707         
15708         this.clearIOSView();
15709         
15710         if(this.allowBlank) {
15711             
15712             var default_text = '-- SELECT --';
15713             
15714             if(this.placeholder.length){
15715                 default_text = this.placeholder;
15716             }
15717             
15718             if(this.emptyTitle.length){
15719                 default_text += ' - ' + this.emptyTitle + ' -';
15720             }
15721             
15722             var opt = this.inputEl().createChild({
15723                 tag: 'option',
15724                 value : 0,
15725                 html : default_text
15726             });
15727             
15728             var o = {};
15729             o[this.valueField] = 0;
15730             o[this.displayField] = default_text;
15731             
15732             this.ios_options.push({
15733                 data : o,
15734                 el : opt
15735             });
15736             
15737         }
15738         
15739         this.store.data.each(function(d, rowIndex){
15740             
15741             var html = '';
15742             
15743             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15744                 html = d.data[this.displayField];
15745             }
15746             
15747             var value = '';
15748             
15749             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15750                 value = d.data[this.valueField];
15751             }
15752             
15753             var option = {
15754                 tag: 'option',
15755                 value : value,
15756                 html : html
15757             };
15758             
15759             if(this.value == d.data[this.valueField]){
15760                 option['selected'] = true;
15761             }
15762             
15763             var opt = this.inputEl().createChild(option);
15764             
15765             this.ios_options.push({
15766                 data : d.data,
15767                 el : opt
15768             });
15769             
15770         }, this);
15771         
15772         this.inputEl().on('change', function(){
15773            this.fireEvent('select', this);
15774         }, this);
15775         
15776     },
15777     
15778     clearIOSView: function()
15779     {
15780         this.inputEl().dom.innerHTML = '';
15781         
15782         this.ios_options = [];
15783     },
15784     
15785     setIOSValue: function(v)
15786     {
15787         this.value = v;
15788         
15789         if(!this.ios_options){
15790             return;
15791         }
15792         
15793         Roo.each(this.ios_options, function(opts){
15794            
15795            opts.el.dom.removeAttribute('selected');
15796            
15797            if(opts.data[this.valueField] != v){
15798                return;
15799            }
15800            
15801            opts.el.dom.setAttribute('selected', true);
15802            
15803         }, this);
15804     }
15805
15806     /** 
15807     * @cfg {Boolean} grow 
15808     * @hide 
15809     */
15810     /** 
15811     * @cfg {Number} growMin 
15812     * @hide 
15813     */
15814     /** 
15815     * @cfg {Number} growMax 
15816     * @hide 
15817     */
15818     /**
15819      * @hide
15820      * @method autoSize
15821      */
15822 });
15823
15824 Roo.apply(Roo.bootstrap.ComboBox,  {
15825     
15826     header : {
15827         tag: 'div',
15828         cls: 'modal-header',
15829         cn: [
15830             {
15831                 tag: 'h4',
15832                 cls: 'modal-title'
15833             }
15834         ]
15835     },
15836     
15837     body : {
15838         tag: 'div',
15839         cls: 'modal-body',
15840         cn: [
15841             {
15842                 tag: 'ul',
15843                 cls: 'list-group'
15844             }
15845         ]
15846     },
15847     
15848     listItemRadio : {
15849         tag: 'li',
15850         cls: 'list-group-item',
15851         cn: [
15852             {
15853                 tag: 'span',
15854                 cls: 'roo-combobox-list-group-item-value'
15855             },
15856             {
15857                 tag: 'div',
15858                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15859                 cn: [
15860                     {
15861                         tag: 'input',
15862                         type: 'radio'
15863                     },
15864                     {
15865                         tag: 'label'
15866                     }
15867                 ]
15868             }
15869         ]
15870     },
15871     
15872     listItemCheckbox : {
15873         tag: 'li',
15874         cls: 'list-group-item',
15875         cn: [
15876             {
15877                 tag: 'span',
15878                 cls: 'roo-combobox-list-group-item-value'
15879             },
15880             {
15881                 tag: 'div',
15882                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15883                 cn: [
15884                     {
15885                         tag: 'input',
15886                         type: 'checkbox'
15887                     },
15888                     {
15889                         tag: 'label'
15890                     }
15891                 ]
15892             }
15893         ]
15894     },
15895     
15896     emptyResult : {
15897         tag: 'div',
15898         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15899     },
15900     
15901     footer : {
15902         tag: 'div',
15903         cls: 'modal-footer',
15904         cn: [
15905             {
15906                 tag: 'div',
15907                 cls: 'row',
15908                 cn: [
15909                     {
15910                         tag: 'div',
15911                         cls: 'col-xs-6 text-left',
15912                         cn: {
15913                             tag: 'button',
15914                             cls: 'btn btn-danger roo-touch-view-cancel',
15915                             html: 'Cancel'
15916                         }
15917                     },
15918                     {
15919                         tag: 'div',
15920                         cls: 'col-xs-6 text-right',
15921                         cn: {
15922                             tag: 'button',
15923                             cls: 'btn btn-success roo-touch-view-ok',
15924                             html: 'OK'
15925                         }
15926                     }
15927                 ]
15928             }
15929         ]
15930         
15931     }
15932 });
15933
15934 Roo.apply(Roo.bootstrap.ComboBox,  {
15935     
15936     touchViewTemplate : {
15937         tag: 'div',
15938         cls: 'modal fade roo-combobox-touch-view',
15939         cn: [
15940             {
15941                 tag: 'div',
15942                 cls: 'modal-dialog',
15943                 style : 'position:fixed', // we have to fix position....
15944                 cn: [
15945                     {
15946                         tag: 'div',
15947                         cls: 'modal-content',
15948                         cn: [
15949                             Roo.bootstrap.ComboBox.header,
15950                             Roo.bootstrap.ComboBox.body,
15951                             Roo.bootstrap.ComboBox.footer
15952                         ]
15953                     }
15954                 ]
15955             }
15956         ]
15957     }
15958 });/*
15959  * Based on:
15960  * Ext JS Library 1.1.1
15961  * Copyright(c) 2006-2007, Ext JS, LLC.
15962  *
15963  * Originally Released Under LGPL - original licence link has changed is not relivant.
15964  *
15965  * Fork - LGPL
15966  * <script type="text/javascript">
15967  */
15968
15969 /**
15970  * @class Roo.View
15971  * @extends Roo.util.Observable
15972  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15973  * This class also supports single and multi selection modes. <br>
15974  * Create a data model bound view:
15975  <pre><code>
15976  var store = new Roo.data.Store(...);
15977
15978  var view = new Roo.View({
15979     el : "my-element",
15980     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15981  
15982     singleSelect: true,
15983     selectedClass: "ydataview-selected",
15984     store: store
15985  });
15986
15987  // listen for node click?
15988  view.on("click", function(vw, index, node, e){
15989  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15990  });
15991
15992  // load XML data
15993  dataModel.load("foobar.xml");
15994  </code></pre>
15995  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15996  * <br><br>
15997  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15998  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15999  * 
16000  * Note: old style constructor is still suported (container, template, config)
16001  * 
16002  * @constructor
16003  * Create a new View
16004  * @param {Object} config The config object
16005  * 
16006  */
16007 Roo.View = function(config, depreciated_tpl, depreciated_config){
16008     
16009     this.parent = false;
16010     
16011     if (typeof(depreciated_tpl) == 'undefined') {
16012         // new way.. - universal constructor.
16013         Roo.apply(this, config);
16014         this.el  = Roo.get(this.el);
16015     } else {
16016         // old format..
16017         this.el  = Roo.get(config);
16018         this.tpl = depreciated_tpl;
16019         Roo.apply(this, depreciated_config);
16020     }
16021     this.wrapEl  = this.el.wrap().wrap();
16022     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16023     
16024     
16025     if(typeof(this.tpl) == "string"){
16026         this.tpl = new Roo.Template(this.tpl);
16027     } else {
16028         // support xtype ctors..
16029         this.tpl = new Roo.factory(this.tpl, Roo);
16030     }
16031     
16032     
16033     this.tpl.compile();
16034     
16035     /** @private */
16036     this.addEvents({
16037         /**
16038          * @event beforeclick
16039          * Fires before a click is processed. Returns false to cancel the default action.
16040          * @param {Roo.View} this
16041          * @param {Number} index The index of the target node
16042          * @param {HTMLElement} node The target node
16043          * @param {Roo.EventObject} e The raw event object
16044          */
16045             "beforeclick" : true,
16046         /**
16047          * @event click
16048          * Fires when a template node is clicked.
16049          * @param {Roo.View} this
16050          * @param {Number} index The index of the target node
16051          * @param {HTMLElement} node The target node
16052          * @param {Roo.EventObject} e The raw event object
16053          */
16054             "click" : true,
16055         /**
16056          * @event dblclick
16057          * Fires when a template node is double clicked.
16058          * @param {Roo.View} this
16059          * @param {Number} index The index of the target node
16060          * @param {HTMLElement} node The target node
16061          * @param {Roo.EventObject} e The raw event object
16062          */
16063             "dblclick" : true,
16064         /**
16065          * @event contextmenu
16066          * Fires when a template node is right clicked.
16067          * @param {Roo.View} this
16068          * @param {Number} index The index of the target node
16069          * @param {HTMLElement} node The target node
16070          * @param {Roo.EventObject} e The raw event object
16071          */
16072             "contextmenu" : true,
16073         /**
16074          * @event selectionchange
16075          * Fires when the selected nodes change.
16076          * @param {Roo.View} this
16077          * @param {Array} selections Array of the selected nodes
16078          */
16079             "selectionchange" : true,
16080     
16081         /**
16082          * @event beforeselect
16083          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16084          * @param {Roo.View} this
16085          * @param {HTMLElement} node The node to be selected
16086          * @param {Array} selections Array of currently selected nodes
16087          */
16088             "beforeselect" : true,
16089         /**
16090          * @event preparedata
16091          * Fires on every row to render, to allow you to change the data.
16092          * @param {Roo.View} this
16093          * @param {Object} data to be rendered (change this)
16094          */
16095           "preparedata" : true
16096           
16097           
16098         });
16099
16100
16101
16102     this.el.on({
16103         "click": this.onClick,
16104         "dblclick": this.onDblClick,
16105         "contextmenu": this.onContextMenu,
16106         scope:this
16107     });
16108
16109     this.selections = [];
16110     this.nodes = [];
16111     this.cmp = new Roo.CompositeElementLite([]);
16112     if(this.store){
16113         this.store = Roo.factory(this.store, Roo.data);
16114         this.setStore(this.store, true);
16115     }
16116     
16117     if ( this.footer && this.footer.xtype) {
16118            
16119          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16120         
16121         this.footer.dataSource = this.store;
16122         this.footer.container = fctr;
16123         this.footer = Roo.factory(this.footer, Roo);
16124         fctr.insertFirst(this.el);
16125         
16126         // this is a bit insane - as the paging toolbar seems to detach the el..
16127 //        dom.parentNode.parentNode.parentNode
16128          // they get detached?
16129     }
16130     
16131     
16132     Roo.View.superclass.constructor.call(this);
16133     
16134     
16135 };
16136
16137 Roo.extend(Roo.View, Roo.util.Observable, {
16138     
16139      /**
16140      * @cfg {Roo.data.Store} store Data store to load data from.
16141      */
16142     store : false,
16143     
16144     /**
16145      * @cfg {String|Roo.Element} el The container element.
16146      */
16147     el : '',
16148     
16149     /**
16150      * @cfg {String|Roo.Template} tpl The template used by this View 
16151      */
16152     tpl : false,
16153     /**
16154      * @cfg {String} dataName the named area of the template to use as the data area
16155      *                          Works with domtemplates roo-name="name"
16156      */
16157     dataName: false,
16158     /**
16159      * @cfg {String} selectedClass The css class to add to selected nodes
16160      */
16161     selectedClass : "x-view-selected",
16162      /**
16163      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16164      */
16165     emptyText : "",
16166     
16167     /**
16168      * @cfg {String} text to display on mask (default Loading)
16169      */
16170     mask : false,
16171     /**
16172      * @cfg {Boolean} multiSelect Allow multiple selection
16173      */
16174     multiSelect : false,
16175     /**
16176      * @cfg {Boolean} singleSelect Allow single selection
16177      */
16178     singleSelect:  false,
16179     
16180     /**
16181      * @cfg {Boolean} toggleSelect - selecting 
16182      */
16183     toggleSelect : false,
16184     
16185     /**
16186      * @cfg {Boolean} tickable - selecting 
16187      */
16188     tickable : false,
16189     
16190     /**
16191      * Returns the element this view is bound to.
16192      * @return {Roo.Element}
16193      */
16194     getEl : function(){
16195         return this.wrapEl;
16196     },
16197     
16198     
16199
16200     /**
16201      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16202      */
16203     refresh : function(){
16204         //Roo.log('refresh');
16205         var t = this.tpl;
16206         
16207         // if we are using something like 'domtemplate', then
16208         // the what gets used is:
16209         // t.applySubtemplate(NAME, data, wrapping data..)
16210         // the outer template then get' applied with
16211         //     the store 'extra data'
16212         // and the body get's added to the
16213         //      roo-name="data" node?
16214         //      <span class='roo-tpl-{name}'></span> ?????
16215         
16216         
16217         
16218         this.clearSelections();
16219         this.el.update("");
16220         var html = [];
16221         var records = this.store.getRange();
16222         if(records.length < 1) {
16223             
16224             // is this valid??  = should it render a template??
16225             
16226             this.el.update(this.emptyText);
16227             return;
16228         }
16229         var el = this.el;
16230         if (this.dataName) {
16231             this.el.update(t.apply(this.store.meta)); //????
16232             el = this.el.child('.roo-tpl-' + this.dataName);
16233         }
16234         
16235         for(var i = 0, len = records.length; i < len; i++){
16236             var data = this.prepareData(records[i].data, i, records[i]);
16237             this.fireEvent("preparedata", this, data, i, records[i]);
16238             
16239             var d = Roo.apply({}, data);
16240             
16241             if(this.tickable){
16242                 Roo.apply(d, {'roo-id' : Roo.id()});
16243                 
16244                 var _this = this;
16245             
16246                 Roo.each(this.parent.item, function(item){
16247                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16248                         return;
16249                     }
16250                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16251                 });
16252             }
16253             
16254             html[html.length] = Roo.util.Format.trim(
16255                 this.dataName ?
16256                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16257                     t.apply(d)
16258             );
16259         }
16260         
16261         
16262         
16263         el.update(html.join(""));
16264         this.nodes = el.dom.childNodes;
16265         this.updateIndexes(0);
16266     },
16267     
16268
16269     /**
16270      * Function to override to reformat the data that is sent to
16271      * the template for each node.
16272      * DEPRICATED - use the preparedata event handler.
16273      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16274      * a JSON object for an UpdateManager bound view).
16275      */
16276     prepareData : function(data, index, record)
16277     {
16278         this.fireEvent("preparedata", this, data, index, record);
16279         return data;
16280     },
16281
16282     onUpdate : function(ds, record){
16283         // Roo.log('on update');   
16284         this.clearSelections();
16285         var index = this.store.indexOf(record);
16286         var n = this.nodes[index];
16287         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16288         n.parentNode.removeChild(n);
16289         this.updateIndexes(index, index);
16290     },
16291
16292     
16293     
16294 // --------- FIXME     
16295     onAdd : function(ds, records, index)
16296     {
16297         //Roo.log(['on Add', ds, records, index] );        
16298         this.clearSelections();
16299         if(this.nodes.length == 0){
16300             this.refresh();
16301             return;
16302         }
16303         var n = this.nodes[index];
16304         for(var i = 0, len = records.length; i < len; i++){
16305             var d = this.prepareData(records[i].data, i, records[i]);
16306             if(n){
16307                 this.tpl.insertBefore(n, d);
16308             }else{
16309                 
16310                 this.tpl.append(this.el, d);
16311             }
16312         }
16313         this.updateIndexes(index);
16314     },
16315
16316     onRemove : function(ds, record, index){
16317        // Roo.log('onRemove');
16318         this.clearSelections();
16319         var el = this.dataName  ?
16320             this.el.child('.roo-tpl-' + this.dataName) :
16321             this.el; 
16322         
16323         el.dom.removeChild(this.nodes[index]);
16324         this.updateIndexes(index);
16325     },
16326
16327     /**
16328      * Refresh an individual node.
16329      * @param {Number} index
16330      */
16331     refreshNode : function(index){
16332         this.onUpdate(this.store, this.store.getAt(index));
16333     },
16334
16335     updateIndexes : function(startIndex, endIndex){
16336         var ns = this.nodes;
16337         startIndex = startIndex || 0;
16338         endIndex = endIndex || ns.length - 1;
16339         for(var i = startIndex; i <= endIndex; i++){
16340             ns[i].nodeIndex = i;
16341         }
16342     },
16343
16344     /**
16345      * Changes the data store this view uses and refresh the view.
16346      * @param {Store} store
16347      */
16348     setStore : function(store, initial){
16349         if(!initial && this.store){
16350             this.store.un("datachanged", this.refresh);
16351             this.store.un("add", this.onAdd);
16352             this.store.un("remove", this.onRemove);
16353             this.store.un("update", this.onUpdate);
16354             this.store.un("clear", this.refresh);
16355             this.store.un("beforeload", this.onBeforeLoad);
16356             this.store.un("load", this.onLoad);
16357             this.store.un("loadexception", this.onLoad);
16358         }
16359         if(store){
16360           
16361             store.on("datachanged", this.refresh, this);
16362             store.on("add", this.onAdd, this);
16363             store.on("remove", this.onRemove, this);
16364             store.on("update", this.onUpdate, this);
16365             store.on("clear", this.refresh, this);
16366             store.on("beforeload", this.onBeforeLoad, this);
16367             store.on("load", this.onLoad, this);
16368             store.on("loadexception", this.onLoad, this);
16369         }
16370         
16371         if(store){
16372             this.refresh();
16373         }
16374     },
16375     /**
16376      * onbeforeLoad - masks the loading area.
16377      *
16378      */
16379     onBeforeLoad : function(store,opts)
16380     {
16381          //Roo.log('onBeforeLoad');   
16382         if (!opts.add) {
16383             this.el.update("");
16384         }
16385         this.el.mask(this.mask ? this.mask : "Loading" ); 
16386     },
16387     onLoad : function ()
16388     {
16389         this.el.unmask();
16390     },
16391     
16392
16393     /**
16394      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16395      * @param {HTMLElement} node
16396      * @return {HTMLElement} The template node
16397      */
16398     findItemFromChild : function(node){
16399         var el = this.dataName  ?
16400             this.el.child('.roo-tpl-' + this.dataName,true) :
16401             this.el.dom; 
16402         
16403         if(!node || node.parentNode == el){
16404                     return node;
16405             }
16406             var p = node.parentNode;
16407             while(p && p != el){
16408             if(p.parentNode == el){
16409                 return p;
16410             }
16411             p = p.parentNode;
16412         }
16413             return null;
16414     },
16415
16416     /** @ignore */
16417     onClick : function(e){
16418         var item = this.findItemFromChild(e.getTarget());
16419         if(item){
16420             var index = this.indexOf(item);
16421             if(this.onItemClick(item, index, e) !== false){
16422                 this.fireEvent("click", this, index, item, e);
16423             }
16424         }else{
16425             this.clearSelections();
16426         }
16427     },
16428
16429     /** @ignore */
16430     onContextMenu : function(e){
16431         var item = this.findItemFromChild(e.getTarget());
16432         if(item){
16433             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16434         }
16435     },
16436
16437     /** @ignore */
16438     onDblClick : function(e){
16439         var item = this.findItemFromChild(e.getTarget());
16440         if(item){
16441             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16442         }
16443     },
16444
16445     onItemClick : function(item, index, e)
16446     {
16447         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16448             return false;
16449         }
16450         if (this.toggleSelect) {
16451             var m = this.isSelected(item) ? 'unselect' : 'select';
16452             //Roo.log(m);
16453             var _t = this;
16454             _t[m](item, true, false);
16455             return true;
16456         }
16457         if(this.multiSelect || this.singleSelect){
16458             if(this.multiSelect && e.shiftKey && this.lastSelection){
16459                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16460             }else{
16461                 this.select(item, this.multiSelect && e.ctrlKey);
16462                 this.lastSelection = item;
16463             }
16464             
16465             if(!this.tickable){
16466                 e.preventDefault();
16467             }
16468             
16469         }
16470         return true;
16471     },
16472
16473     /**
16474      * Get the number of selected nodes.
16475      * @return {Number}
16476      */
16477     getSelectionCount : function(){
16478         return this.selections.length;
16479     },
16480
16481     /**
16482      * Get the currently selected nodes.
16483      * @return {Array} An array of HTMLElements
16484      */
16485     getSelectedNodes : function(){
16486         return this.selections;
16487     },
16488
16489     /**
16490      * Get the indexes of the selected nodes.
16491      * @return {Array}
16492      */
16493     getSelectedIndexes : function(){
16494         var indexes = [], s = this.selections;
16495         for(var i = 0, len = s.length; i < len; i++){
16496             indexes.push(s[i].nodeIndex);
16497         }
16498         return indexes;
16499     },
16500
16501     /**
16502      * Clear all selections
16503      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16504      */
16505     clearSelections : function(suppressEvent){
16506         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16507             this.cmp.elements = this.selections;
16508             this.cmp.removeClass(this.selectedClass);
16509             this.selections = [];
16510             if(!suppressEvent){
16511                 this.fireEvent("selectionchange", this, this.selections);
16512             }
16513         }
16514     },
16515
16516     /**
16517      * Returns true if the passed node is selected
16518      * @param {HTMLElement/Number} node The node or node index
16519      * @return {Boolean}
16520      */
16521     isSelected : function(node){
16522         var s = this.selections;
16523         if(s.length < 1){
16524             return false;
16525         }
16526         node = this.getNode(node);
16527         return s.indexOf(node) !== -1;
16528     },
16529
16530     /**
16531      * Selects nodes.
16532      * @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
16533      * @param {Boolean} keepExisting (optional) true to keep existing selections
16534      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16535      */
16536     select : function(nodeInfo, keepExisting, suppressEvent){
16537         if(nodeInfo instanceof Array){
16538             if(!keepExisting){
16539                 this.clearSelections(true);
16540             }
16541             for(var i = 0, len = nodeInfo.length; i < len; i++){
16542                 this.select(nodeInfo[i], true, true);
16543             }
16544             return;
16545         } 
16546         var node = this.getNode(nodeInfo);
16547         if(!node || this.isSelected(node)){
16548             return; // already selected.
16549         }
16550         if(!keepExisting){
16551             this.clearSelections(true);
16552         }
16553         
16554         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16555             Roo.fly(node).addClass(this.selectedClass);
16556             this.selections.push(node);
16557             if(!suppressEvent){
16558                 this.fireEvent("selectionchange", this, this.selections);
16559             }
16560         }
16561         
16562         
16563     },
16564       /**
16565      * Unselects nodes.
16566      * @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
16567      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16569      */
16570     unselect : function(nodeInfo, keepExisting, suppressEvent)
16571     {
16572         if(nodeInfo instanceof Array){
16573             Roo.each(this.selections, function(s) {
16574                 this.unselect(s, nodeInfo);
16575             }, this);
16576             return;
16577         }
16578         var node = this.getNode(nodeInfo);
16579         if(!node || !this.isSelected(node)){
16580             //Roo.log("not selected");
16581             return; // not selected.
16582         }
16583         // fireevent???
16584         var ns = [];
16585         Roo.each(this.selections, function(s) {
16586             if (s == node ) {
16587                 Roo.fly(node).removeClass(this.selectedClass);
16588
16589                 return;
16590             }
16591             ns.push(s);
16592         },this);
16593         
16594         this.selections= ns;
16595         this.fireEvent("selectionchange", this, this.selections);
16596     },
16597
16598     /**
16599      * Gets a template node.
16600      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16601      * @return {HTMLElement} The node or null if it wasn't found
16602      */
16603     getNode : function(nodeInfo){
16604         if(typeof nodeInfo == "string"){
16605             return document.getElementById(nodeInfo);
16606         }else if(typeof nodeInfo == "number"){
16607             return this.nodes[nodeInfo];
16608         }
16609         return nodeInfo;
16610     },
16611
16612     /**
16613      * Gets a range template nodes.
16614      * @param {Number} startIndex
16615      * @param {Number} endIndex
16616      * @return {Array} An array of nodes
16617      */
16618     getNodes : function(start, end){
16619         var ns = this.nodes;
16620         start = start || 0;
16621         end = typeof end == "undefined" ? ns.length - 1 : end;
16622         var nodes = [];
16623         if(start <= end){
16624             for(var i = start; i <= end; i++){
16625                 nodes.push(ns[i]);
16626             }
16627         } else{
16628             for(var i = start; i >= end; i--){
16629                 nodes.push(ns[i]);
16630             }
16631         }
16632         return nodes;
16633     },
16634
16635     /**
16636      * Finds the index of the passed node
16637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16638      * @return {Number} The index of the node or -1
16639      */
16640     indexOf : function(node){
16641         node = this.getNode(node);
16642         if(typeof node.nodeIndex == "number"){
16643             return node.nodeIndex;
16644         }
16645         var ns = this.nodes;
16646         for(var i = 0, len = ns.length; i < len; i++){
16647             if(ns[i] == node){
16648                 return i;
16649             }
16650         }
16651         return -1;
16652     }
16653 });
16654 /*
16655  * - LGPL
16656  *
16657  * based on jquery fullcalendar
16658  * 
16659  */
16660
16661 Roo.bootstrap = Roo.bootstrap || {};
16662 /**
16663  * @class Roo.bootstrap.Calendar
16664  * @extends Roo.bootstrap.Component
16665  * Bootstrap Calendar class
16666  * @cfg {Boolean} loadMask (true|false) default false
16667  * @cfg {Object} header generate the user specific header of the calendar, default false
16668
16669  * @constructor
16670  * Create a new Container
16671  * @param {Object} config The config object
16672  */
16673
16674
16675
16676 Roo.bootstrap.Calendar = function(config){
16677     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16678      this.addEvents({
16679         /**
16680              * @event select
16681              * Fires when a date is selected
16682              * @param {DatePicker} this
16683              * @param {Date} date The selected date
16684              */
16685         'select': true,
16686         /**
16687              * @event monthchange
16688              * Fires when the displayed month changes 
16689              * @param {DatePicker} this
16690              * @param {Date} date The selected month
16691              */
16692         'monthchange': true,
16693         /**
16694              * @event evententer
16695              * Fires when mouse over an event
16696              * @param {Calendar} this
16697              * @param {event} Event
16698              */
16699         'evententer': true,
16700         /**
16701              * @event eventleave
16702              * Fires when the mouse leaves an
16703              * @param {Calendar} this
16704              * @param {event}
16705              */
16706         'eventleave': true,
16707         /**
16708              * @event eventclick
16709              * Fires when the mouse click an
16710              * @param {Calendar} this
16711              * @param {event}
16712              */
16713         'eventclick': true
16714         
16715     });
16716
16717 };
16718
16719 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16720     
16721      /**
16722      * @cfg {Number} startDay
16723      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16724      */
16725     startDay : 0,
16726     
16727     loadMask : false,
16728     
16729     header : false,
16730       
16731     getAutoCreate : function(){
16732         
16733         
16734         var fc_button = function(name, corner, style, content ) {
16735             return Roo.apply({},{
16736                 tag : 'span',
16737                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16738                          (corner.length ?
16739                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16740                             ''
16741                         ),
16742                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16743                 unselectable: 'on'
16744             });
16745         };
16746         
16747         var header = {};
16748         
16749         if(!this.header){
16750             header = {
16751                 tag : 'table',
16752                 cls : 'fc-header',
16753                 style : 'width:100%',
16754                 cn : [
16755                     {
16756                         tag: 'tr',
16757                         cn : [
16758                             {
16759                                 tag : 'td',
16760                                 cls : 'fc-header-left',
16761                                 cn : [
16762                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16763                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16764                                     { tag: 'span', cls: 'fc-header-space' },
16765                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16766
16767
16768                                 ]
16769                             },
16770
16771                             {
16772                                 tag : 'td',
16773                                 cls : 'fc-header-center',
16774                                 cn : [
16775                                     {
16776                                         tag: 'span',
16777                                         cls: 'fc-header-title',
16778                                         cn : {
16779                                             tag: 'H2',
16780                                             html : 'month / year'
16781                                         }
16782                                     }
16783
16784                                 ]
16785                             },
16786                             {
16787                                 tag : 'td',
16788                                 cls : 'fc-header-right',
16789                                 cn : [
16790                               /*      fc_button('month', 'left', '', 'month' ),
16791                                     fc_button('week', '', '', 'week' ),
16792                                     fc_button('day', 'right', '', 'day' )
16793                                 */    
16794
16795                                 ]
16796                             }
16797
16798                         ]
16799                     }
16800                 ]
16801             };
16802         }
16803         
16804         header = this.header;
16805         
16806        
16807         var cal_heads = function() {
16808             var ret = [];
16809             // fixme - handle this.
16810             
16811             for (var i =0; i < Date.dayNames.length; i++) {
16812                 var d = Date.dayNames[i];
16813                 ret.push({
16814                     tag: 'th',
16815                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16816                     html : d.substring(0,3)
16817                 });
16818                 
16819             }
16820             ret[0].cls += ' fc-first';
16821             ret[6].cls += ' fc-last';
16822             return ret;
16823         };
16824         var cal_cell = function(n) {
16825             return  {
16826                 tag: 'td',
16827                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16828                 cn : [
16829                     {
16830                         cn : [
16831                             {
16832                                 cls: 'fc-day-number',
16833                                 html: 'D'
16834                             },
16835                             {
16836                                 cls: 'fc-day-content',
16837                              
16838                                 cn : [
16839                                      {
16840                                         style: 'position: relative;' // height: 17px;
16841                                     }
16842                                 ]
16843                             }
16844                             
16845                             
16846                         ]
16847                     }
16848                 ]
16849                 
16850             }
16851         };
16852         var cal_rows = function() {
16853             
16854             var ret = [];
16855             for (var r = 0; r < 6; r++) {
16856                 var row= {
16857                     tag : 'tr',
16858                     cls : 'fc-week',
16859                     cn : []
16860                 };
16861                 
16862                 for (var i =0; i < Date.dayNames.length; i++) {
16863                     var d = Date.dayNames[i];
16864                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16865
16866                 }
16867                 row.cn[0].cls+=' fc-first';
16868                 row.cn[0].cn[0].style = 'min-height:90px';
16869                 row.cn[6].cls+=' fc-last';
16870                 ret.push(row);
16871                 
16872             }
16873             ret[0].cls += ' fc-first';
16874             ret[4].cls += ' fc-prev-last';
16875             ret[5].cls += ' fc-last';
16876             return ret;
16877             
16878         };
16879         
16880         var cal_table = {
16881             tag: 'table',
16882             cls: 'fc-border-separate',
16883             style : 'width:100%',
16884             cellspacing  : 0,
16885             cn : [
16886                 { 
16887                     tag: 'thead',
16888                     cn : [
16889                         { 
16890                             tag: 'tr',
16891                             cls : 'fc-first fc-last',
16892                             cn : cal_heads()
16893                         }
16894                     ]
16895                 },
16896                 { 
16897                     tag: 'tbody',
16898                     cn : cal_rows()
16899                 }
16900                   
16901             ]
16902         };
16903          
16904          var cfg = {
16905             cls : 'fc fc-ltr',
16906             cn : [
16907                 header,
16908                 {
16909                     cls : 'fc-content',
16910                     style : "position: relative;",
16911                     cn : [
16912                         {
16913                             cls : 'fc-view fc-view-month fc-grid',
16914                             style : 'position: relative',
16915                             unselectable : 'on',
16916                             cn : [
16917                                 {
16918                                     cls : 'fc-event-container',
16919                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16920                                 },
16921                                 cal_table
16922                             ]
16923                         }
16924                     ]
16925     
16926                 }
16927            ] 
16928             
16929         };
16930         
16931          
16932         
16933         return cfg;
16934     },
16935     
16936     
16937     initEvents : function()
16938     {
16939         if(!this.store){
16940             throw "can not find store for calendar";
16941         }
16942         
16943         var mark = {
16944             tag: "div",
16945             cls:"x-dlg-mask",
16946             style: "text-align:center",
16947             cn: [
16948                 {
16949                     tag: "div",
16950                     style: "background-color:white;width:50%;margin:250 auto",
16951                     cn: [
16952                         {
16953                             tag: "img",
16954                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16955                         },
16956                         {
16957                             tag: "span",
16958                             html: "Loading"
16959                         }
16960                         
16961                     ]
16962                 }
16963             ]
16964         };
16965         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16966         
16967         var size = this.el.select('.fc-content', true).first().getSize();
16968         this.maskEl.setSize(size.width, size.height);
16969         this.maskEl.enableDisplayMode("block");
16970         if(!this.loadMask){
16971             this.maskEl.hide();
16972         }
16973         
16974         this.store = Roo.factory(this.store, Roo.data);
16975         this.store.on('load', this.onLoad, this);
16976         this.store.on('beforeload', this.onBeforeLoad, this);
16977         
16978         this.resize();
16979         
16980         this.cells = this.el.select('.fc-day',true);
16981         //Roo.log(this.cells);
16982         this.textNodes = this.el.query('.fc-day-number');
16983         this.cells.addClassOnOver('fc-state-hover');
16984         
16985         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16986         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16987         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16988         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16989         
16990         this.on('monthchange', this.onMonthChange, this);
16991         
16992         this.update(new Date().clearTime());
16993     },
16994     
16995     resize : function() {
16996         var sz  = this.el.getSize();
16997         
16998         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16999         this.el.select('.fc-day-content div',true).setHeight(34);
17000     },
17001     
17002     
17003     // private
17004     showPrevMonth : function(e){
17005         this.update(this.activeDate.add("mo", -1));
17006     },
17007     showToday : function(e){
17008         this.update(new Date().clearTime());
17009     },
17010     // private
17011     showNextMonth : function(e){
17012         this.update(this.activeDate.add("mo", 1));
17013     },
17014
17015     // private
17016     showPrevYear : function(){
17017         this.update(this.activeDate.add("y", -1));
17018     },
17019
17020     // private
17021     showNextYear : function(){
17022         this.update(this.activeDate.add("y", 1));
17023     },
17024
17025     
17026    // private
17027     update : function(date)
17028     {
17029         var vd = this.activeDate;
17030         this.activeDate = date;
17031 //        if(vd && this.el){
17032 //            var t = date.getTime();
17033 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17034 //                Roo.log('using add remove');
17035 //                
17036 //                this.fireEvent('monthchange', this, date);
17037 //                
17038 //                this.cells.removeClass("fc-state-highlight");
17039 //                this.cells.each(function(c){
17040 //                   if(c.dateValue == t){
17041 //                       c.addClass("fc-state-highlight");
17042 //                       setTimeout(function(){
17043 //                            try{c.dom.firstChild.focus();}catch(e){}
17044 //                       }, 50);
17045 //                       return false;
17046 //                   }
17047 //                   return true;
17048 //                });
17049 //                return;
17050 //            }
17051 //        }
17052         
17053         var days = date.getDaysInMonth();
17054         
17055         var firstOfMonth = date.getFirstDateOfMonth();
17056         var startingPos = firstOfMonth.getDay()-this.startDay;
17057         
17058         if(startingPos < this.startDay){
17059             startingPos += 7;
17060         }
17061         
17062         var pm = date.add(Date.MONTH, -1);
17063         var prevStart = pm.getDaysInMonth()-startingPos;
17064 //        
17065         this.cells = this.el.select('.fc-day',true);
17066         this.textNodes = this.el.query('.fc-day-number');
17067         this.cells.addClassOnOver('fc-state-hover');
17068         
17069         var cells = this.cells.elements;
17070         var textEls = this.textNodes;
17071         
17072         Roo.each(cells, function(cell){
17073             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17074         });
17075         
17076         days += startingPos;
17077
17078         // convert everything to numbers so it's fast
17079         var day = 86400000;
17080         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17081         //Roo.log(d);
17082         //Roo.log(pm);
17083         //Roo.log(prevStart);
17084         
17085         var today = new Date().clearTime().getTime();
17086         var sel = date.clearTime().getTime();
17087         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17088         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17089         var ddMatch = this.disabledDatesRE;
17090         var ddText = this.disabledDatesText;
17091         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17092         var ddaysText = this.disabledDaysText;
17093         var format = this.format;
17094         
17095         var setCellClass = function(cal, cell){
17096             cell.row = 0;
17097             cell.events = [];
17098             cell.more = [];
17099             //Roo.log('set Cell Class');
17100             cell.title = "";
17101             var t = d.getTime();
17102             
17103             //Roo.log(d);
17104             
17105             cell.dateValue = t;
17106             if(t == today){
17107                 cell.className += " fc-today";
17108                 cell.className += " fc-state-highlight";
17109                 cell.title = cal.todayText;
17110             }
17111             if(t == sel){
17112                 // disable highlight in other month..
17113                 //cell.className += " fc-state-highlight";
17114                 
17115             }
17116             // disabling
17117             if(t < min) {
17118                 cell.className = " fc-state-disabled";
17119                 cell.title = cal.minText;
17120                 return;
17121             }
17122             if(t > max) {
17123                 cell.className = " fc-state-disabled";
17124                 cell.title = cal.maxText;
17125                 return;
17126             }
17127             if(ddays){
17128                 if(ddays.indexOf(d.getDay()) != -1){
17129                     cell.title = ddaysText;
17130                     cell.className = " fc-state-disabled";
17131                 }
17132             }
17133             if(ddMatch && format){
17134                 var fvalue = d.dateFormat(format);
17135                 if(ddMatch.test(fvalue)){
17136                     cell.title = ddText.replace("%0", fvalue);
17137                     cell.className = " fc-state-disabled";
17138                 }
17139             }
17140             
17141             if (!cell.initialClassName) {
17142                 cell.initialClassName = cell.dom.className;
17143             }
17144             
17145             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17146         };
17147
17148         var i = 0;
17149         
17150         for(; i < startingPos; i++) {
17151             textEls[i].innerHTML = (++prevStart);
17152             d.setDate(d.getDate()+1);
17153             
17154             cells[i].className = "fc-past fc-other-month";
17155             setCellClass(this, cells[i]);
17156         }
17157         
17158         var intDay = 0;
17159         
17160         for(; i < days; i++){
17161             intDay = i - startingPos + 1;
17162             textEls[i].innerHTML = (intDay);
17163             d.setDate(d.getDate()+1);
17164             
17165             cells[i].className = ''; // "x-date-active";
17166             setCellClass(this, cells[i]);
17167         }
17168         var extraDays = 0;
17169         
17170         for(; i < 42; i++) {
17171             textEls[i].innerHTML = (++extraDays);
17172             d.setDate(d.getDate()+1);
17173             
17174             cells[i].className = "fc-future fc-other-month";
17175             setCellClass(this, cells[i]);
17176         }
17177         
17178         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17179         
17180         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17181         
17182         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17183         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17184         
17185         if(totalRows != 6){
17186             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17187             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17188         }
17189         
17190         this.fireEvent('monthchange', this, date);
17191         
17192         
17193         /*
17194         if(!this.internalRender){
17195             var main = this.el.dom.firstChild;
17196             var w = main.offsetWidth;
17197             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17198             Roo.fly(main).setWidth(w);
17199             this.internalRender = true;
17200             // opera does not respect the auto grow header center column
17201             // then, after it gets a width opera refuses to recalculate
17202             // without a second pass
17203             if(Roo.isOpera && !this.secondPass){
17204                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17205                 this.secondPass = true;
17206                 this.update.defer(10, this, [date]);
17207             }
17208         }
17209         */
17210         
17211     },
17212     
17213     findCell : function(dt) {
17214         dt = dt.clearTime().getTime();
17215         var ret = false;
17216         this.cells.each(function(c){
17217             //Roo.log("check " +c.dateValue + '?=' + dt);
17218             if(c.dateValue == dt){
17219                 ret = c;
17220                 return false;
17221             }
17222             return true;
17223         });
17224         
17225         return ret;
17226     },
17227     
17228     findCells : function(ev) {
17229         var s = ev.start.clone().clearTime().getTime();
17230        // Roo.log(s);
17231         var e= ev.end.clone().clearTime().getTime();
17232        // Roo.log(e);
17233         var ret = [];
17234         this.cells.each(function(c){
17235              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17236             
17237             if(c.dateValue > e){
17238                 return ;
17239             }
17240             if(c.dateValue < s){
17241                 return ;
17242             }
17243             ret.push(c);
17244         });
17245         
17246         return ret;    
17247     },
17248     
17249 //    findBestRow: function(cells)
17250 //    {
17251 //        var ret = 0;
17252 //        
17253 //        for (var i =0 ; i < cells.length;i++) {
17254 //            ret  = Math.max(cells[i].rows || 0,ret);
17255 //        }
17256 //        return ret;
17257 //        
17258 //    },
17259     
17260     
17261     addItem : function(ev)
17262     {
17263         // look for vertical location slot in
17264         var cells = this.findCells(ev);
17265         
17266 //        ev.row = this.findBestRow(cells);
17267         
17268         // work out the location.
17269         
17270         var crow = false;
17271         var rows = [];
17272         for(var i =0; i < cells.length; i++) {
17273             
17274             cells[i].row = cells[0].row;
17275             
17276             if(i == 0){
17277                 cells[i].row = cells[i].row + 1;
17278             }
17279             
17280             if (!crow) {
17281                 crow = {
17282                     start : cells[i],
17283                     end :  cells[i]
17284                 };
17285                 continue;
17286             }
17287             if (crow.start.getY() == cells[i].getY()) {
17288                 // on same row.
17289                 crow.end = cells[i];
17290                 continue;
17291             }
17292             // different row.
17293             rows.push(crow);
17294             crow = {
17295                 start: cells[i],
17296                 end : cells[i]
17297             };
17298             
17299         }
17300         
17301         rows.push(crow);
17302         ev.els = [];
17303         ev.rows = rows;
17304         ev.cells = cells;
17305         
17306         cells[0].events.push(ev);
17307         
17308         this.calevents.push(ev);
17309     },
17310     
17311     clearEvents: function() {
17312         
17313         if(!this.calevents){
17314             return;
17315         }
17316         
17317         Roo.each(this.cells.elements, function(c){
17318             c.row = 0;
17319             c.events = [];
17320             c.more = [];
17321         });
17322         
17323         Roo.each(this.calevents, function(e) {
17324             Roo.each(e.els, function(el) {
17325                 el.un('mouseenter' ,this.onEventEnter, this);
17326                 el.un('mouseleave' ,this.onEventLeave, this);
17327                 el.remove();
17328             },this);
17329         },this);
17330         
17331         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17332             e.remove();
17333         });
17334         
17335     },
17336     
17337     renderEvents: function()
17338     {   
17339         var _this = this;
17340         
17341         this.cells.each(function(c) {
17342             
17343             if(c.row < 5){
17344                 return;
17345             }
17346             
17347             var ev = c.events;
17348             
17349             var r = 4;
17350             if(c.row != c.events.length){
17351                 r = 4 - (4 - (c.row - c.events.length));
17352             }
17353             
17354             c.events = ev.slice(0, r);
17355             c.more = ev.slice(r);
17356             
17357             if(c.more.length && c.more.length == 1){
17358                 c.events.push(c.more.pop());
17359             }
17360             
17361             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17362             
17363         });
17364             
17365         this.cells.each(function(c) {
17366             
17367             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17368             
17369             
17370             for (var e = 0; e < c.events.length; e++){
17371                 var ev = c.events[e];
17372                 var rows = ev.rows;
17373                 
17374                 for(var i = 0; i < rows.length; i++) {
17375                 
17376                     // how many rows should it span..
17377
17378                     var  cfg = {
17379                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17380                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17381
17382                         unselectable : "on",
17383                         cn : [
17384                             {
17385                                 cls: 'fc-event-inner',
17386                                 cn : [
17387     //                                {
17388     //                                  tag:'span',
17389     //                                  cls: 'fc-event-time',
17390     //                                  html : cells.length > 1 ? '' : ev.time
17391     //                                },
17392                                     {
17393                                       tag:'span',
17394                                       cls: 'fc-event-title',
17395                                       html : String.format('{0}', ev.title)
17396                                     }
17397
17398
17399                                 ]
17400                             },
17401                             {
17402                                 cls: 'ui-resizable-handle ui-resizable-e',
17403                                 html : '&nbsp;&nbsp;&nbsp'
17404                             }
17405
17406                         ]
17407                     };
17408
17409                     if (i == 0) {
17410                         cfg.cls += ' fc-event-start';
17411                     }
17412                     if ((i+1) == rows.length) {
17413                         cfg.cls += ' fc-event-end';
17414                     }
17415
17416                     var ctr = _this.el.select('.fc-event-container',true).first();
17417                     var cg = ctr.createChild(cfg);
17418
17419                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17420                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17421
17422                     var r = (c.more.length) ? 1 : 0;
17423                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17424                     cg.setWidth(ebox.right - sbox.x -2);
17425
17426                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17427                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17428                     cg.on('click', _this.onEventClick, _this, ev);
17429
17430                     ev.els.push(cg);
17431                     
17432                 }
17433                 
17434             }
17435             
17436             
17437             if(c.more.length){
17438                 var  cfg = {
17439                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17440                     style : 'position: absolute',
17441                     unselectable : "on",
17442                     cn : [
17443                         {
17444                             cls: 'fc-event-inner',
17445                             cn : [
17446                                 {
17447                                   tag:'span',
17448                                   cls: 'fc-event-title',
17449                                   html : 'More'
17450                                 }
17451
17452
17453                             ]
17454                         },
17455                         {
17456                             cls: 'ui-resizable-handle ui-resizable-e',
17457                             html : '&nbsp;&nbsp;&nbsp'
17458                         }
17459
17460                     ]
17461                 };
17462
17463                 var ctr = _this.el.select('.fc-event-container',true).first();
17464                 var cg = ctr.createChild(cfg);
17465
17466                 var sbox = c.select('.fc-day-content',true).first().getBox();
17467                 var ebox = c.select('.fc-day-content',true).first().getBox();
17468                 //Roo.log(cg);
17469                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17470                 cg.setWidth(ebox.right - sbox.x -2);
17471
17472                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17473                 
17474             }
17475             
17476         });
17477         
17478         
17479         
17480     },
17481     
17482     onEventEnter: function (e, el,event,d) {
17483         this.fireEvent('evententer', this, el, event);
17484     },
17485     
17486     onEventLeave: function (e, el,event,d) {
17487         this.fireEvent('eventleave', this, el, event);
17488     },
17489     
17490     onEventClick: function (e, el,event,d) {
17491         this.fireEvent('eventclick', this, el, event);
17492     },
17493     
17494     onMonthChange: function () {
17495         this.store.load();
17496     },
17497     
17498     onMoreEventClick: function(e, el, more)
17499     {
17500         var _this = this;
17501         
17502         this.calpopover.placement = 'right';
17503         this.calpopover.setTitle('More');
17504         
17505         this.calpopover.setContent('');
17506         
17507         var ctr = this.calpopover.el.select('.popover-content', true).first();
17508         
17509         Roo.each(more, function(m){
17510             var cfg = {
17511                 cls : 'fc-event-hori fc-event-draggable',
17512                 html : m.title
17513             };
17514             var cg = ctr.createChild(cfg);
17515             
17516             cg.on('click', _this.onEventClick, _this, m);
17517         });
17518         
17519         this.calpopover.show(el);
17520         
17521         
17522     },
17523     
17524     onLoad: function () 
17525     {   
17526         this.calevents = [];
17527         var cal = this;
17528         
17529         if(this.store.getCount() > 0){
17530             this.store.data.each(function(d){
17531                cal.addItem({
17532                     id : d.data.id,
17533                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17534                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17535                     time : d.data.start_time,
17536                     title : d.data.title,
17537                     description : d.data.description,
17538                     venue : d.data.venue
17539                 });
17540             });
17541         }
17542         
17543         this.renderEvents();
17544         
17545         if(this.calevents.length && this.loadMask){
17546             this.maskEl.hide();
17547         }
17548     },
17549     
17550     onBeforeLoad: function()
17551     {
17552         this.clearEvents();
17553         if(this.loadMask){
17554             this.maskEl.show();
17555         }
17556     }
17557 });
17558
17559  
17560  /*
17561  * - LGPL
17562  *
17563  * element
17564  * 
17565  */
17566
17567 /**
17568  * @class Roo.bootstrap.Popover
17569  * @extends Roo.bootstrap.Component
17570  * Bootstrap Popover class
17571  * @cfg {String} html contents of the popover   (or false to use children..)
17572  * @cfg {String} title of popover (or false to hide)
17573  * @cfg {String} placement how it is placed
17574  * @cfg {String} trigger click || hover (or false to trigger manually)
17575  * @cfg {String} over what (parent or false to trigger manually.)
17576  * @cfg {Number} delay - delay before showing
17577  
17578  * @constructor
17579  * Create a new Popover
17580  * @param {Object} config The config object
17581  */
17582
17583 Roo.bootstrap.Popover = function(config){
17584     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17585     
17586     this.addEvents({
17587         // raw events
17588          /**
17589          * @event show
17590          * After the popover show
17591          * 
17592          * @param {Roo.bootstrap.Popover} this
17593          */
17594         "show" : true,
17595         /**
17596          * @event hide
17597          * After the popover hide
17598          * 
17599          * @param {Roo.bootstrap.Popover} this
17600          */
17601         "hide" : true
17602     });
17603 };
17604
17605 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17606     
17607     title: 'Fill in a title',
17608     html: false,
17609     
17610     placement : 'right',
17611     trigger : 'hover', // hover
17612     
17613     delay : 0,
17614     
17615     over: 'parent',
17616     
17617     can_build_overlaid : false,
17618     
17619     getChildContainer : function()
17620     {
17621         return this.el.select('.popover-content',true).first();
17622     },
17623     
17624     getAutoCreate : function(){
17625          
17626         var cfg = {
17627            cls : 'popover roo-dynamic',
17628            style: 'display:block',
17629            cn : [
17630                 {
17631                     cls : 'arrow'
17632                 },
17633                 {
17634                     cls : 'popover-inner',
17635                     cn : [
17636                         {
17637                             tag: 'h3',
17638                             cls: 'popover-title popover-header',
17639                             html : this.title
17640                         },
17641                         {
17642                             cls : 'popover-content popover-body',
17643                             html : this.html
17644                         }
17645                     ]
17646                     
17647                 }
17648            ]
17649         };
17650         
17651         return cfg;
17652     },
17653     setTitle: function(str)
17654     {
17655         this.title = str;
17656         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17657     },
17658     setContent: function(str)
17659     {
17660         this.html = str;
17661         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17662     },
17663     // as it get's added to the bottom of the page.
17664     onRender : function(ct, position)
17665     {
17666         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17667         if(!this.el){
17668             var cfg = Roo.apply({},  this.getAutoCreate());
17669             cfg.id = Roo.id();
17670             
17671             if (this.cls) {
17672                 cfg.cls += ' ' + this.cls;
17673             }
17674             if (this.style) {
17675                 cfg.style = this.style;
17676             }
17677             //Roo.log("adding to ");
17678             this.el = Roo.get(document.body).createChild(cfg, position);
17679 //            Roo.log(this.el);
17680         }
17681         this.initEvents();
17682     },
17683     
17684     initEvents : function()
17685     {
17686         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17687         this.el.enableDisplayMode('block');
17688         this.el.hide();
17689         if (this.over === false) {
17690             return; 
17691         }
17692         if (this.triggers === false) {
17693             return;
17694         }
17695         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17696         var triggers = this.trigger ? this.trigger.split(' ') : [];
17697         Roo.each(triggers, function(trigger) {
17698         
17699             if (trigger == 'click') {
17700                 on_el.on('click', this.toggle, this);
17701             } else if (trigger != 'manual') {
17702                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17703                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17704       
17705                 on_el.on(eventIn  ,this.enter, this);
17706                 on_el.on(eventOut, this.leave, this);
17707             }
17708         }, this);
17709         
17710     },
17711     
17712     
17713     // private
17714     timeout : null,
17715     hoverState : null,
17716     
17717     toggle : function () {
17718         this.hoverState == 'in' ? this.leave() : this.enter();
17719     },
17720     
17721     enter : function () {
17722         
17723         clearTimeout(this.timeout);
17724     
17725         this.hoverState = 'in';
17726     
17727         if (!this.delay || !this.delay.show) {
17728             this.show();
17729             return;
17730         }
17731         var _t = this;
17732         this.timeout = setTimeout(function () {
17733             if (_t.hoverState == 'in') {
17734                 _t.show();
17735             }
17736         }, this.delay.show)
17737     },
17738     
17739     leave : function() {
17740         clearTimeout(this.timeout);
17741     
17742         this.hoverState = 'out';
17743     
17744         if (!this.delay || !this.delay.hide) {
17745             this.hide();
17746             return;
17747         }
17748         var _t = this;
17749         this.timeout = setTimeout(function () {
17750             if (_t.hoverState == 'out') {
17751                 _t.hide();
17752             }
17753         }, this.delay.hide)
17754     },
17755     
17756     show : function (on_el)
17757     {
17758         if (!on_el) {
17759             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17760         }
17761         
17762         // set content.
17763         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17764         if (this.html !== false) {
17765             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17766         }
17767         this.el.removeClass([
17768             'fade','top','bottom', 'left', 'right','in',
17769             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17770         ]);
17771         if (!this.title.length) {
17772             this.el.select('.popover-title',true).hide();
17773         }
17774         
17775         var placement = typeof this.placement == 'function' ?
17776             this.placement.call(this, this.el, on_el) :
17777             this.placement;
17778             
17779         var autoToken = /\s?auto?\s?/i;
17780         var autoPlace = autoToken.test(placement);
17781         if (autoPlace) {
17782             placement = placement.replace(autoToken, '') || 'top';
17783         }
17784         
17785         //this.el.detach()
17786         //this.el.setXY([0,0]);
17787         this.el.show();
17788         this.el.dom.style.display='block';
17789         this.el.addClass(placement);
17790         
17791         //this.el.appendTo(on_el);
17792         
17793         var p = this.getPosition();
17794         var box = this.el.getBox();
17795         
17796         if (autoPlace) {
17797             // fixme..
17798         }
17799         var align = Roo.bootstrap.Popover.alignment[placement];
17800         
17801 //        Roo.log(align);
17802         this.el.alignTo(on_el, align[0],align[1]);
17803         //var arrow = this.el.select('.arrow',true).first();
17804         //arrow.set(align[2], 
17805         
17806         this.el.addClass('in');
17807         
17808         
17809         if (this.el.hasClass('fade')) {
17810             // fade it?
17811         }
17812         
17813         this.hoverState = 'in';
17814         
17815         this.fireEvent('show', this);
17816         
17817     },
17818     hide : function()
17819     {
17820         this.el.setXY([0,0]);
17821         this.el.removeClass('in');
17822         this.el.hide();
17823         this.hoverState = null;
17824         
17825         this.fireEvent('hide', this);
17826     }
17827     
17828 });
17829
17830 Roo.bootstrap.Popover.alignment = {
17831     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17832     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17833     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17834     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17835 };
17836
17837  /*
17838  * - LGPL
17839  *
17840  * Progress
17841  * 
17842  */
17843
17844 /**
17845  * @class Roo.bootstrap.Progress
17846  * @extends Roo.bootstrap.Component
17847  * Bootstrap Progress class
17848  * @cfg {Boolean} striped striped of the progress bar
17849  * @cfg {Boolean} active animated of the progress bar
17850  * 
17851  * 
17852  * @constructor
17853  * Create a new Progress
17854  * @param {Object} config The config object
17855  */
17856
17857 Roo.bootstrap.Progress = function(config){
17858     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17859 };
17860
17861 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17862     
17863     striped : false,
17864     active: false,
17865     
17866     getAutoCreate : function(){
17867         var cfg = {
17868             tag: 'div',
17869             cls: 'progress'
17870         };
17871         
17872         
17873         if(this.striped){
17874             cfg.cls += ' progress-striped';
17875         }
17876       
17877         if(this.active){
17878             cfg.cls += ' active';
17879         }
17880         
17881         
17882         return cfg;
17883     }
17884    
17885 });
17886
17887  
17888
17889  /*
17890  * - LGPL
17891  *
17892  * ProgressBar
17893  * 
17894  */
17895
17896 /**
17897  * @class Roo.bootstrap.ProgressBar
17898  * @extends Roo.bootstrap.Component
17899  * Bootstrap ProgressBar class
17900  * @cfg {Number} aria_valuenow aria-value now
17901  * @cfg {Number} aria_valuemin aria-value min
17902  * @cfg {Number} aria_valuemax aria-value max
17903  * @cfg {String} label label for the progress bar
17904  * @cfg {String} panel (success | info | warning | danger )
17905  * @cfg {String} role role of the progress bar
17906  * @cfg {String} sr_only text
17907  * 
17908  * 
17909  * @constructor
17910  * Create a new ProgressBar
17911  * @param {Object} config The config object
17912  */
17913
17914 Roo.bootstrap.ProgressBar = function(config){
17915     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17916 };
17917
17918 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17919     
17920     aria_valuenow : 0,
17921     aria_valuemin : 0,
17922     aria_valuemax : 100,
17923     label : false,
17924     panel : false,
17925     role : false,
17926     sr_only: false,
17927     
17928     getAutoCreate : function()
17929     {
17930         
17931         var cfg = {
17932             tag: 'div',
17933             cls: 'progress-bar',
17934             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17935         };
17936         
17937         if(this.sr_only){
17938             cfg.cn = {
17939                 tag: 'span',
17940                 cls: 'sr-only',
17941                 html: this.sr_only
17942             }
17943         }
17944         
17945         if(this.role){
17946             cfg.role = this.role;
17947         }
17948         
17949         if(this.aria_valuenow){
17950             cfg['aria-valuenow'] = this.aria_valuenow;
17951         }
17952         
17953         if(this.aria_valuemin){
17954             cfg['aria-valuemin'] = this.aria_valuemin;
17955         }
17956         
17957         if(this.aria_valuemax){
17958             cfg['aria-valuemax'] = this.aria_valuemax;
17959         }
17960         
17961         if(this.label && !this.sr_only){
17962             cfg.html = this.label;
17963         }
17964         
17965         if(this.panel){
17966             cfg.cls += ' progress-bar-' + this.panel;
17967         }
17968         
17969         return cfg;
17970     },
17971     
17972     update : function(aria_valuenow)
17973     {
17974         this.aria_valuenow = aria_valuenow;
17975         
17976         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17977     }
17978    
17979 });
17980
17981  
17982
17983  /*
17984  * - LGPL
17985  *
17986  * column
17987  * 
17988  */
17989
17990 /**
17991  * @class Roo.bootstrap.TabGroup
17992  * @extends Roo.bootstrap.Column
17993  * Bootstrap Column class
17994  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17995  * @cfg {Boolean} carousel true to make the group behave like a carousel
17996  * @cfg {Boolean} bullets show bullets for the panels
17997  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17998  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17999  * @cfg {Boolean} showarrow (true|false) show arrow default true
18000  * 
18001  * @constructor
18002  * Create a new TabGroup
18003  * @param {Object} config The config object
18004  */
18005
18006 Roo.bootstrap.TabGroup = function(config){
18007     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18008     if (!this.navId) {
18009         this.navId = Roo.id();
18010     }
18011     this.tabs = [];
18012     Roo.bootstrap.TabGroup.register(this);
18013     
18014 };
18015
18016 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18017     
18018     carousel : false,
18019     transition : false,
18020     bullets : 0,
18021     timer : 0,
18022     autoslide : false,
18023     slideFn : false,
18024     slideOnTouch : false,
18025     showarrow : true,
18026     
18027     getAutoCreate : function()
18028     {
18029         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18030         
18031         cfg.cls += ' tab-content';
18032         
18033         if (this.carousel) {
18034             cfg.cls += ' carousel slide';
18035             
18036             cfg.cn = [{
18037                cls : 'carousel-inner',
18038                cn : []
18039             }];
18040         
18041             if(this.bullets  && !Roo.isTouch){
18042                 
18043                 var bullets = {
18044                     cls : 'carousel-bullets',
18045                     cn : []
18046                 };
18047                
18048                 if(this.bullets_cls){
18049                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18050                 }
18051                 
18052                 bullets.cn.push({
18053                     cls : 'clear'
18054                 });
18055                 
18056                 cfg.cn[0].cn.push(bullets);
18057             }
18058             
18059             if(this.showarrow){
18060                 cfg.cn[0].cn.push({
18061                     tag : 'div',
18062                     class : 'carousel-arrow',
18063                     cn : [
18064                         {
18065                             tag : 'div',
18066                             class : 'carousel-prev',
18067                             cn : [
18068                                 {
18069                                     tag : 'i',
18070                                     class : 'fa fa-chevron-left'
18071                                 }
18072                             ]
18073                         },
18074                         {
18075                             tag : 'div',
18076                             class : 'carousel-next',
18077                             cn : [
18078                                 {
18079                                     tag : 'i',
18080                                     class : 'fa fa-chevron-right'
18081                                 }
18082                             ]
18083                         }
18084                     ]
18085                 });
18086             }
18087             
18088         }
18089         
18090         return cfg;
18091     },
18092     
18093     initEvents:  function()
18094     {
18095 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18096 //            this.el.on("touchstart", this.onTouchStart, this);
18097 //        }
18098         
18099         if(this.autoslide){
18100             var _this = this;
18101             
18102             this.slideFn = window.setInterval(function() {
18103                 _this.showPanelNext();
18104             }, this.timer);
18105         }
18106         
18107         if(this.showarrow){
18108             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18109             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18110         }
18111         
18112         
18113     },
18114     
18115 //    onTouchStart : function(e, el, o)
18116 //    {
18117 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18118 //            return;
18119 //        }
18120 //        
18121 //        this.showPanelNext();
18122 //    },
18123     
18124     
18125     getChildContainer : function()
18126     {
18127         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18128     },
18129     
18130     /**
18131     * register a Navigation item
18132     * @param {Roo.bootstrap.NavItem} the navitem to add
18133     */
18134     register : function(item)
18135     {
18136         this.tabs.push( item);
18137         item.navId = this.navId; // not really needed..
18138         this.addBullet();
18139     
18140     },
18141     
18142     getActivePanel : function()
18143     {
18144         var r = false;
18145         Roo.each(this.tabs, function(t) {
18146             if (t.active) {
18147                 r = t;
18148                 return false;
18149             }
18150             return null;
18151         });
18152         return r;
18153         
18154     },
18155     getPanelByName : function(n)
18156     {
18157         var r = false;
18158         Roo.each(this.tabs, function(t) {
18159             if (t.tabId == n) {
18160                 r = t;
18161                 return false;
18162             }
18163             return null;
18164         });
18165         return r;
18166     },
18167     indexOfPanel : function(p)
18168     {
18169         var r = false;
18170         Roo.each(this.tabs, function(t,i) {
18171             if (t.tabId == p.tabId) {
18172                 r = i;
18173                 return false;
18174             }
18175             return null;
18176         });
18177         return r;
18178     },
18179     /**
18180      * show a specific panel
18181      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18182      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18183      */
18184     showPanel : function (pan)
18185     {
18186         if(this.transition || typeof(pan) == 'undefined'){
18187             Roo.log("waiting for the transitionend");
18188             return;
18189         }
18190         
18191         if (typeof(pan) == 'number') {
18192             pan = this.tabs[pan];
18193         }
18194         
18195         if (typeof(pan) == 'string') {
18196             pan = this.getPanelByName(pan);
18197         }
18198         
18199         var cur = this.getActivePanel();
18200         
18201         if(!pan || !cur){
18202             Roo.log('pan or acitve pan is undefined');
18203             return false;
18204         }
18205         
18206         if (pan.tabId == this.getActivePanel().tabId) {
18207             return true;
18208         }
18209         
18210         if (false === cur.fireEvent('beforedeactivate')) {
18211             return false;
18212         }
18213         
18214         if(this.bullets > 0 && !Roo.isTouch){
18215             this.setActiveBullet(this.indexOfPanel(pan));
18216         }
18217         
18218         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18219             
18220             this.transition = true;
18221             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18222             var lr = dir == 'next' ? 'left' : 'right';
18223             pan.el.addClass(dir); // or prev
18224             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18225             cur.el.addClass(lr); // or right
18226             pan.el.addClass(lr);
18227             
18228             var _this = this;
18229             cur.el.on('transitionend', function() {
18230                 Roo.log("trans end?");
18231                 
18232                 pan.el.removeClass([lr,dir]);
18233                 pan.setActive(true);
18234                 
18235                 cur.el.removeClass([lr]);
18236                 cur.setActive(false);
18237                 
18238                 _this.transition = false;
18239                 
18240             }, this, { single:  true } );
18241             
18242             return true;
18243         }
18244         
18245         cur.setActive(false);
18246         pan.setActive(true);
18247         
18248         return true;
18249         
18250     },
18251     showPanelNext : function()
18252     {
18253         var i = this.indexOfPanel(this.getActivePanel());
18254         
18255         if (i >= this.tabs.length - 1 && !this.autoslide) {
18256             return;
18257         }
18258         
18259         if (i >= this.tabs.length - 1 && this.autoslide) {
18260             i = -1;
18261         }
18262         
18263         this.showPanel(this.tabs[i+1]);
18264     },
18265     
18266     showPanelPrev : function()
18267     {
18268         var i = this.indexOfPanel(this.getActivePanel());
18269         
18270         if (i  < 1 && !this.autoslide) {
18271             return;
18272         }
18273         
18274         if (i < 1 && this.autoslide) {
18275             i = this.tabs.length;
18276         }
18277         
18278         this.showPanel(this.tabs[i-1]);
18279     },
18280     
18281     
18282     addBullet: function()
18283     {
18284         if(!this.bullets || Roo.isTouch){
18285             return;
18286         }
18287         var ctr = this.el.select('.carousel-bullets',true).first();
18288         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18289         var bullet = ctr.createChild({
18290             cls : 'bullet bullet-' + i
18291         },ctr.dom.lastChild);
18292         
18293         
18294         var _this = this;
18295         
18296         bullet.on('click', (function(e, el, o, ii, t){
18297
18298             e.preventDefault();
18299
18300             this.showPanel(ii);
18301
18302             if(this.autoslide && this.slideFn){
18303                 clearInterval(this.slideFn);
18304                 this.slideFn = window.setInterval(function() {
18305                     _this.showPanelNext();
18306                 }, this.timer);
18307             }
18308
18309         }).createDelegate(this, [i, bullet], true));
18310                 
18311         
18312     },
18313      
18314     setActiveBullet : function(i)
18315     {
18316         if(Roo.isTouch){
18317             return;
18318         }
18319         
18320         Roo.each(this.el.select('.bullet', true).elements, function(el){
18321             el.removeClass('selected');
18322         });
18323
18324         var bullet = this.el.select('.bullet-' + i, true).first();
18325         
18326         if(!bullet){
18327             return;
18328         }
18329         
18330         bullet.addClass('selected');
18331     }
18332     
18333     
18334   
18335 });
18336
18337  
18338
18339  
18340  
18341 Roo.apply(Roo.bootstrap.TabGroup, {
18342     
18343     groups: {},
18344      /**
18345     * register a Navigation Group
18346     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18347     */
18348     register : function(navgrp)
18349     {
18350         this.groups[navgrp.navId] = navgrp;
18351         
18352     },
18353     /**
18354     * fetch a Navigation Group based on the navigation ID
18355     * if one does not exist , it will get created.
18356     * @param {string} the navgroup to add
18357     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18358     */
18359     get: function(navId) {
18360         if (typeof(this.groups[navId]) == 'undefined') {
18361             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18362         }
18363         return this.groups[navId] ;
18364     }
18365     
18366     
18367     
18368 });
18369
18370  /*
18371  * - LGPL
18372  *
18373  * TabPanel
18374  * 
18375  */
18376
18377 /**
18378  * @class Roo.bootstrap.TabPanel
18379  * @extends Roo.bootstrap.Component
18380  * Bootstrap TabPanel class
18381  * @cfg {Boolean} active panel active
18382  * @cfg {String} html panel content
18383  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18384  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18385  * @cfg {String} href click to link..
18386  * 
18387  * 
18388  * @constructor
18389  * Create a new TabPanel
18390  * @param {Object} config The config object
18391  */
18392
18393 Roo.bootstrap.TabPanel = function(config){
18394     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18395     this.addEvents({
18396         /**
18397              * @event changed
18398              * Fires when the active status changes
18399              * @param {Roo.bootstrap.TabPanel} this
18400              * @param {Boolean} state the new state
18401             
18402          */
18403         'changed': true,
18404         /**
18405              * @event beforedeactivate
18406              * Fires before a tab is de-activated - can be used to do validation on a form.
18407              * @param {Roo.bootstrap.TabPanel} this
18408              * @return {Boolean} false if there is an error
18409             
18410          */
18411         'beforedeactivate': true
18412      });
18413     
18414     this.tabId = this.tabId || Roo.id();
18415   
18416 };
18417
18418 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18419     
18420     active: false,
18421     html: false,
18422     tabId: false,
18423     navId : false,
18424     href : '',
18425     
18426     getAutoCreate : function(){
18427         var cfg = {
18428             tag: 'div',
18429             // item is needed for carousel - not sure if it has any effect otherwise
18430             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18431             html: this.html || ''
18432         };
18433         
18434         if(this.active){
18435             cfg.cls += ' active';
18436         }
18437         
18438         if(this.tabId){
18439             cfg.tabId = this.tabId;
18440         }
18441         
18442         
18443         return cfg;
18444     },
18445     
18446     initEvents:  function()
18447     {
18448         var p = this.parent();
18449         
18450         this.navId = this.navId || p.navId;
18451         
18452         if (typeof(this.navId) != 'undefined') {
18453             // not really needed.. but just in case.. parent should be a NavGroup.
18454             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18455             
18456             tg.register(this);
18457             
18458             var i = tg.tabs.length - 1;
18459             
18460             if(this.active && tg.bullets > 0 && i < tg.bullets){
18461                 tg.setActiveBullet(i);
18462             }
18463         }
18464         
18465         this.el.on('click', this.onClick, this);
18466         
18467         if(Roo.isTouch){
18468             this.el.on("touchstart", this.onTouchStart, this);
18469             this.el.on("touchmove", this.onTouchMove, this);
18470             this.el.on("touchend", this.onTouchEnd, this);
18471         }
18472         
18473     },
18474     
18475     onRender : function(ct, position)
18476     {
18477         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18478     },
18479     
18480     setActive : function(state)
18481     {
18482         Roo.log("panel - set active " + this.tabId + "=" + state);
18483         
18484         this.active = state;
18485         if (!state) {
18486             this.el.removeClass('active');
18487             
18488         } else  if (!this.el.hasClass('active')) {
18489             this.el.addClass('active');
18490         }
18491         
18492         this.fireEvent('changed', this, state);
18493     },
18494     
18495     onClick : function(e)
18496     {
18497         e.preventDefault();
18498         
18499         if(!this.href.length){
18500             return;
18501         }
18502         
18503         window.location.href = this.href;
18504     },
18505     
18506     startX : 0,
18507     startY : 0,
18508     endX : 0,
18509     endY : 0,
18510     swiping : false,
18511     
18512     onTouchStart : function(e)
18513     {
18514         this.swiping = false;
18515         
18516         this.startX = e.browserEvent.touches[0].clientX;
18517         this.startY = e.browserEvent.touches[0].clientY;
18518     },
18519     
18520     onTouchMove : function(e)
18521     {
18522         this.swiping = true;
18523         
18524         this.endX = e.browserEvent.touches[0].clientX;
18525         this.endY = e.browserEvent.touches[0].clientY;
18526     },
18527     
18528     onTouchEnd : function(e)
18529     {
18530         if(!this.swiping){
18531             this.onClick(e);
18532             return;
18533         }
18534         
18535         var tabGroup = this.parent();
18536         
18537         if(this.endX > this.startX){ // swiping right
18538             tabGroup.showPanelPrev();
18539             return;
18540         }
18541         
18542         if(this.startX > this.endX){ // swiping left
18543             tabGroup.showPanelNext();
18544             return;
18545         }
18546     }
18547     
18548     
18549 });
18550  
18551
18552  
18553
18554  /*
18555  * - LGPL
18556  *
18557  * DateField
18558  * 
18559  */
18560
18561 /**
18562  * @class Roo.bootstrap.DateField
18563  * @extends Roo.bootstrap.Input
18564  * Bootstrap DateField class
18565  * @cfg {Number} weekStart default 0
18566  * @cfg {String} viewMode default empty, (months|years)
18567  * @cfg {String} minViewMode default empty, (months|years)
18568  * @cfg {Number} startDate default -Infinity
18569  * @cfg {Number} endDate default Infinity
18570  * @cfg {Boolean} todayHighlight default false
18571  * @cfg {Boolean} todayBtn default false
18572  * @cfg {Boolean} calendarWeeks default false
18573  * @cfg {Object} daysOfWeekDisabled default empty
18574  * @cfg {Boolean} singleMode default false (true | false)
18575  * 
18576  * @cfg {Boolean} keyboardNavigation default true
18577  * @cfg {String} language default en
18578  * 
18579  * @constructor
18580  * Create a new DateField
18581  * @param {Object} config The config object
18582  */
18583
18584 Roo.bootstrap.DateField = function(config){
18585     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18586      this.addEvents({
18587             /**
18588              * @event show
18589              * Fires when this field show.
18590              * @param {Roo.bootstrap.DateField} this
18591              * @param {Mixed} date The date value
18592              */
18593             show : true,
18594             /**
18595              * @event show
18596              * Fires when this field hide.
18597              * @param {Roo.bootstrap.DateField} this
18598              * @param {Mixed} date The date value
18599              */
18600             hide : true,
18601             /**
18602              * @event select
18603              * Fires when select a date.
18604              * @param {Roo.bootstrap.DateField} this
18605              * @param {Mixed} date The date value
18606              */
18607             select : true,
18608             /**
18609              * @event beforeselect
18610              * Fires when before select a date.
18611              * @param {Roo.bootstrap.DateField} this
18612              * @param {Mixed} date The date value
18613              */
18614             beforeselect : true
18615         });
18616 };
18617
18618 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18619     
18620     /**
18621      * @cfg {String} format
18622      * The default date format string which can be overriden for localization support.  The format must be
18623      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18624      */
18625     format : "m/d/y",
18626     /**
18627      * @cfg {String} altFormats
18628      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18629      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18630      */
18631     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18632     
18633     weekStart : 0,
18634     
18635     viewMode : '',
18636     
18637     minViewMode : '',
18638     
18639     todayHighlight : false,
18640     
18641     todayBtn: false,
18642     
18643     language: 'en',
18644     
18645     keyboardNavigation: true,
18646     
18647     calendarWeeks: false,
18648     
18649     startDate: -Infinity,
18650     
18651     endDate: Infinity,
18652     
18653     daysOfWeekDisabled: [],
18654     
18655     _events: [],
18656     
18657     singleMode : false,
18658     
18659     UTCDate: function()
18660     {
18661         return new Date(Date.UTC.apply(Date, arguments));
18662     },
18663     
18664     UTCToday: function()
18665     {
18666         var today = new Date();
18667         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18668     },
18669     
18670     getDate: function() {
18671             var d = this.getUTCDate();
18672             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18673     },
18674     
18675     getUTCDate: function() {
18676             return this.date;
18677     },
18678     
18679     setDate: function(d) {
18680             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18681     },
18682     
18683     setUTCDate: function(d) {
18684             this.date = d;
18685             this.setValue(this.formatDate(this.date));
18686     },
18687         
18688     onRender: function(ct, position)
18689     {
18690         
18691         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18692         
18693         this.language = this.language || 'en';
18694         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18695         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18696         
18697         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18698         this.format = this.format || 'm/d/y';
18699         this.isInline = false;
18700         this.isInput = true;
18701         this.component = this.el.select('.add-on', true).first() || false;
18702         this.component = (this.component && this.component.length === 0) ? false : this.component;
18703         this.hasInput = this.component && this.inputEl().length;
18704         
18705         if (typeof(this.minViewMode === 'string')) {
18706             switch (this.minViewMode) {
18707                 case 'months':
18708                     this.minViewMode = 1;
18709                     break;
18710                 case 'years':
18711                     this.minViewMode = 2;
18712                     break;
18713                 default:
18714                     this.minViewMode = 0;
18715                     break;
18716             }
18717         }
18718         
18719         if (typeof(this.viewMode === 'string')) {
18720             switch (this.viewMode) {
18721                 case 'months':
18722                     this.viewMode = 1;
18723                     break;
18724                 case 'years':
18725                     this.viewMode = 2;
18726                     break;
18727                 default:
18728                     this.viewMode = 0;
18729                     break;
18730             }
18731         }
18732                 
18733         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18734         
18735 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18736         
18737         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18738         
18739         this.picker().on('mousedown', this.onMousedown, this);
18740         this.picker().on('click', this.onClick, this);
18741         
18742         this.picker().addClass('datepicker-dropdown');
18743         
18744         this.startViewMode = this.viewMode;
18745         
18746         if(this.singleMode){
18747             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18748                 v.setVisibilityMode(Roo.Element.DISPLAY);
18749                 v.hide();
18750             });
18751             
18752             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18753                 v.setStyle('width', '189px');
18754             });
18755         }
18756         
18757         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18758             if(!this.calendarWeeks){
18759                 v.remove();
18760                 return;
18761             }
18762             
18763             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18764             v.attr('colspan', function(i, val){
18765                 return parseInt(val) + 1;
18766             });
18767         });
18768                         
18769         
18770         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18771         
18772         this.setStartDate(this.startDate);
18773         this.setEndDate(this.endDate);
18774         
18775         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18776         
18777         this.fillDow();
18778         this.fillMonths();
18779         this.update();
18780         this.showMode();
18781         
18782         if(this.isInline) {
18783             this.showPopup();
18784         }
18785     },
18786     
18787     picker : function()
18788     {
18789         return this.pickerEl;
18790 //        return this.el.select('.datepicker', true).first();
18791     },
18792     
18793     fillDow: function()
18794     {
18795         var dowCnt = this.weekStart;
18796         
18797         var dow = {
18798             tag: 'tr',
18799             cn: [
18800                 
18801             ]
18802         };
18803         
18804         if(this.calendarWeeks){
18805             dow.cn.push({
18806                 tag: 'th',
18807                 cls: 'cw',
18808                 html: '&nbsp;'
18809             })
18810         }
18811         
18812         while (dowCnt < this.weekStart + 7) {
18813             dow.cn.push({
18814                 tag: 'th',
18815                 cls: 'dow',
18816                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18817             });
18818         }
18819         
18820         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18821     },
18822     
18823     fillMonths: function()
18824     {    
18825         var i = 0;
18826         var months = this.picker().select('>.datepicker-months td', true).first();
18827         
18828         months.dom.innerHTML = '';
18829         
18830         while (i < 12) {
18831             var month = {
18832                 tag: 'span',
18833                 cls: 'month',
18834                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18835             };
18836             
18837             months.createChild(month);
18838         }
18839         
18840     },
18841     
18842     update: function()
18843     {
18844         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;
18845         
18846         if (this.date < this.startDate) {
18847             this.viewDate = new Date(this.startDate);
18848         } else if (this.date > this.endDate) {
18849             this.viewDate = new Date(this.endDate);
18850         } else {
18851             this.viewDate = new Date(this.date);
18852         }
18853         
18854         this.fill();
18855     },
18856     
18857     fill: function() 
18858     {
18859         var d = new Date(this.viewDate),
18860                 year = d.getUTCFullYear(),
18861                 month = d.getUTCMonth(),
18862                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18863                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18864                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18865                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18866                 currentDate = this.date && this.date.valueOf(),
18867                 today = this.UTCToday();
18868         
18869         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18870         
18871 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18872         
18873 //        this.picker.select('>tfoot th.today').
18874 //                                              .text(dates[this.language].today)
18875 //                                              .toggle(this.todayBtn !== false);
18876     
18877         this.updateNavArrows();
18878         this.fillMonths();
18879                                                 
18880         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18881         
18882         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18883          
18884         prevMonth.setUTCDate(day);
18885         
18886         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18887         
18888         var nextMonth = new Date(prevMonth);
18889         
18890         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18891         
18892         nextMonth = nextMonth.valueOf();
18893         
18894         var fillMonths = false;
18895         
18896         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18897         
18898         while(prevMonth.valueOf() <= nextMonth) {
18899             var clsName = '';
18900             
18901             if (prevMonth.getUTCDay() === this.weekStart) {
18902                 if(fillMonths){
18903                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18904                 }
18905                     
18906                 fillMonths = {
18907                     tag: 'tr',
18908                     cn: []
18909                 };
18910                 
18911                 if(this.calendarWeeks){
18912                     // ISO 8601: First week contains first thursday.
18913                     // ISO also states week starts on Monday, but we can be more abstract here.
18914                     var
18915                     // Start of current week: based on weekstart/current date
18916                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18917                     // Thursday of this week
18918                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18919                     // First Thursday of year, year from thursday
18920                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18921                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18922                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18923                     
18924                     fillMonths.cn.push({
18925                         tag: 'td',
18926                         cls: 'cw',
18927                         html: calWeek
18928                     });
18929                 }
18930             }
18931             
18932             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18933                 clsName += ' old';
18934             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18935                 clsName += ' new';
18936             }
18937             if (this.todayHighlight &&
18938                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18939                 prevMonth.getUTCMonth() == today.getMonth() &&
18940                 prevMonth.getUTCDate() == today.getDate()) {
18941                 clsName += ' today';
18942             }
18943             
18944             if (currentDate && prevMonth.valueOf() === currentDate) {
18945                 clsName += ' active';
18946             }
18947             
18948             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18949                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18950                     clsName += ' disabled';
18951             }
18952             
18953             fillMonths.cn.push({
18954                 tag: 'td',
18955                 cls: 'day ' + clsName,
18956                 html: prevMonth.getDate()
18957             });
18958             
18959             prevMonth.setDate(prevMonth.getDate()+1);
18960         }
18961           
18962         var currentYear = this.date && this.date.getUTCFullYear();
18963         var currentMonth = this.date && this.date.getUTCMonth();
18964         
18965         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18966         
18967         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18968             v.removeClass('active');
18969             
18970             if(currentYear === year && k === currentMonth){
18971                 v.addClass('active');
18972             }
18973             
18974             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18975                 v.addClass('disabled');
18976             }
18977             
18978         });
18979         
18980         
18981         year = parseInt(year/10, 10) * 10;
18982         
18983         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18984         
18985         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18986         
18987         year -= 1;
18988         for (var i = -1; i < 11; i++) {
18989             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18990                 tag: 'span',
18991                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18992                 html: year
18993             });
18994             
18995             year += 1;
18996         }
18997     },
18998     
18999     showMode: function(dir) 
19000     {
19001         if (dir) {
19002             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19003         }
19004         
19005         Roo.each(this.picker().select('>div',true).elements, function(v){
19006             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19007             v.hide();
19008         });
19009         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19010     },
19011     
19012     place: function()
19013     {
19014         if(this.isInline) {
19015             return;
19016         }
19017         
19018         this.picker().removeClass(['bottom', 'top']);
19019         
19020         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19021             /*
19022              * place to the top of element!
19023              *
19024              */
19025             
19026             this.picker().addClass('top');
19027             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19028             
19029             return;
19030         }
19031         
19032         this.picker().addClass('bottom');
19033         
19034         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19035     },
19036     
19037     parseDate : function(value)
19038     {
19039         if(!value || value instanceof Date){
19040             return value;
19041         }
19042         var v = Date.parseDate(value, this.format);
19043         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19044             v = Date.parseDate(value, 'Y-m-d');
19045         }
19046         if(!v && this.altFormats){
19047             if(!this.altFormatsArray){
19048                 this.altFormatsArray = this.altFormats.split("|");
19049             }
19050             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19051                 v = Date.parseDate(value, this.altFormatsArray[i]);
19052             }
19053         }
19054         return v;
19055     },
19056     
19057     formatDate : function(date, fmt)
19058     {   
19059         return (!date || !(date instanceof Date)) ?
19060         date : date.dateFormat(fmt || this.format);
19061     },
19062     
19063     onFocus : function()
19064     {
19065         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19066         this.showPopup();
19067     },
19068     
19069     onBlur : function()
19070     {
19071         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19072         
19073         var d = this.inputEl().getValue();
19074         
19075         this.setValue(d);
19076                 
19077         this.hidePopup();
19078     },
19079     
19080     showPopup : function()
19081     {
19082         this.picker().show();
19083         this.update();
19084         this.place();
19085         
19086         this.fireEvent('showpopup', this, this.date);
19087     },
19088     
19089     hidePopup : function()
19090     {
19091         if(this.isInline) {
19092             return;
19093         }
19094         this.picker().hide();
19095         this.viewMode = this.startViewMode;
19096         this.showMode();
19097         
19098         this.fireEvent('hidepopup', this, this.date);
19099         
19100     },
19101     
19102     onMousedown: function(e)
19103     {
19104         e.stopPropagation();
19105         e.preventDefault();
19106     },
19107     
19108     keyup: function(e)
19109     {
19110         Roo.bootstrap.DateField.superclass.keyup.call(this);
19111         this.update();
19112     },
19113
19114     setValue: function(v)
19115     {
19116         if(this.fireEvent('beforeselect', this, v) !== false){
19117             var d = new Date(this.parseDate(v) ).clearTime();
19118         
19119             if(isNaN(d.getTime())){
19120                 this.date = this.viewDate = '';
19121                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19122                 return;
19123             }
19124
19125             v = this.formatDate(d);
19126
19127             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19128
19129             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19130
19131             this.update();
19132
19133             this.fireEvent('select', this, this.date);
19134         }
19135     },
19136     
19137     getValue: function()
19138     {
19139         return this.formatDate(this.date);
19140     },
19141     
19142     fireKey: function(e)
19143     {
19144         if (!this.picker().isVisible()){
19145             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19146                 this.showPopup();
19147             }
19148             return;
19149         }
19150         
19151         var dateChanged = false,
19152         dir, day, month,
19153         newDate, newViewDate;
19154         
19155         switch(e.keyCode){
19156             case 27: // escape
19157                 this.hidePopup();
19158                 e.preventDefault();
19159                 break;
19160             case 37: // left
19161             case 39: // right
19162                 if (!this.keyboardNavigation) {
19163                     break;
19164                 }
19165                 dir = e.keyCode == 37 ? -1 : 1;
19166                 
19167                 if (e.ctrlKey){
19168                     newDate = this.moveYear(this.date, dir);
19169                     newViewDate = this.moveYear(this.viewDate, dir);
19170                 } else if (e.shiftKey){
19171                     newDate = this.moveMonth(this.date, dir);
19172                     newViewDate = this.moveMonth(this.viewDate, dir);
19173                 } else {
19174                     newDate = new Date(this.date);
19175                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19176                     newViewDate = new Date(this.viewDate);
19177                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19178                 }
19179                 if (this.dateWithinRange(newDate)){
19180                     this.date = newDate;
19181                     this.viewDate = newViewDate;
19182                     this.setValue(this.formatDate(this.date));
19183 //                    this.update();
19184                     e.preventDefault();
19185                     dateChanged = true;
19186                 }
19187                 break;
19188             case 38: // up
19189             case 40: // down
19190                 if (!this.keyboardNavigation) {
19191                     break;
19192                 }
19193                 dir = e.keyCode == 38 ? -1 : 1;
19194                 if (e.ctrlKey){
19195                     newDate = this.moveYear(this.date, dir);
19196                     newViewDate = this.moveYear(this.viewDate, dir);
19197                 } else if (e.shiftKey){
19198                     newDate = this.moveMonth(this.date, dir);
19199                     newViewDate = this.moveMonth(this.viewDate, dir);
19200                 } else {
19201                     newDate = new Date(this.date);
19202                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19203                     newViewDate = new Date(this.viewDate);
19204                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19205                 }
19206                 if (this.dateWithinRange(newDate)){
19207                     this.date = newDate;
19208                     this.viewDate = newViewDate;
19209                     this.setValue(this.formatDate(this.date));
19210 //                    this.update();
19211                     e.preventDefault();
19212                     dateChanged = true;
19213                 }
19214                 break;
19215             case 13: // enter
19216                 this.setValue(this.formatDate(this.date));
19217                 this.hidePopup();
19218                 e.preventDefault();
19219                 break;
19220             case 9: // tab
19221                 this.setValue(this.formatDate(this.date));
19222                 this.hidePopup();
19223                 break;
19224             case 16: // shift
19225             case 17: // ctrl
19226             case 18: // alt
19227                 break;
19228             default :
19229                 this.hidePopup();
19230                 
19231         }
19232     },
19233     
19234     
19235     onClick: function(e) 
19236     {
19237         e.stopPropagation();
19238         e.preventDefault();
19239         
19240         var target = e.getTarget();
19241         
19242         if(target.nodeName.toLowerCase() === 'i'){
19243             target = Roo.get(target).dom.parentNode;
19244         }
19245         
19246         var nodeName = target.nodeName;
19247         var className = target.className;
19248         var html = target.innerHTML;
19249         //Roo.log(nodeName);
19250         
19251         switch(nodeName.toLowerCase()) {
19252             case 'th':
19253                 switch(className) {
19254                     case 'switch':
19255                         this.showMode(1);
19256                         break;
19257                     case 'prev':
19258                     case 'next':
19259                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19260                         switch(this.viewMode){
19261                                 case 0:
19262                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19263                                         break;
19264                                 case 1:
19265                                 case 2:
19266                                         this.viewDate = this.moveYear(this.viewDate, dir);
19267                                         break;
19268                         }
19269                         this.fill();
19270                         break;
19271                     case 'today':
19272                         var date = new Date();
19273                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19274 //                        this.fill()
19275                         this.setValue(this.formatDate(this.date));
19276                         
19277                         this.hidePopup();
19278                         break;
19279                 }
19280                 break;
19281             case 'span':
19282                 if (className.indexOf('disabled') < 0) {
19283                     this.viewDate.setUTCDate(1);
19284                     if (className.indexOf('month') > -1) {
19285                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19286                     } else {
19287                         var year = parseInt(html, 10) || 0;
19288                         this.viewDate.setUTCFullYear(year);
19289                         
19290                     }
19291                     
19292                     if(this.singleMode){
19293                         this.setValue(this.formatDate(this.viewDate));
19294                         this.hidePopup();
19295                         return;
19296                     }
19297                     
19298                     this.showMode(-1);
19299                     this.fill();
19300                 }
19301                 break;
19302                 
19303             case 'td':
19304                 //Roo.log(className);
19305                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19306                     var day = parseInt(html, 10) || 1;
19307                     var year = this.viewDate.getUTCFullYear(),
19308                         month = this.viewDate.getUTCMonth();
19309
19310                     if (className.indexOf('old') > -1) {
19311                         if(month === 0 ){
19312                             month = 11;
19313                             year -= 1;
19314                         }else{
19315                             month -= 1;
19316                         }
19317                     } else if (className.indexOf('new') > -1) {
19318                         if (month == 11) {
19319                             month = 0;
19320                             year += 1;
19321                         } else {
19322                             month += 1;
19323                         }
19324                     }
19325                     //Roo.log([year,month,day]);
19326                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19327                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19328 //                    this.fill();
19329                     //Roo.log(this.formatDate(this.date));
19330                     this.setValue(this.formatDate(this.date));
19331                     this.hidePopup();
19332                 }
19333                 break;
19334         }
19335     },
19336     
19337     setStartDate: function(startDate)
19338     {
19339         this.startDate = startDate || -Infinity;
19340         if (this.startDate !== -Infinity) {
19341             this.startDate = this.parseDate(this.startDate);
19342         }
19343         this.update();
19344         this.updateNavArrows();
19345     },
19346
19347     setEndDate: function(endDate)
19348     {
19349         this.endDate = endDate || Infinity;
19350         if (this.endDate !== Infinity) {
19351             this.endDate = this.parseDate(this.endDate);
19352         }
19353         this.update();
19354         this.updateNavArrows();
19355     },
19356     
19357     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19358     {
19359         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19360         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19361             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19362         }
19363         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19364             return parseInt(d, 10);
19365         });
19366         this.update();
19367         this.updateNavArrows();
19368     },
19369     
19370     updateNavArrows: function() 
19371     {
19372         if(this.singleMode){
19373             return;
19374         }
19375         
19376         var d = new Date(this.viewDate),
19377         year = d.getUTCFullYear(),
19378         month = d.getUTCMonth();
19379         
19380         Roo.each(this.picker().select('.prev', true).elements, function(v){
19381             v.show();
19382             switch (this.viewMode) {
19383                 case 0:
19384
19385                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19386                         v.hide();
19387                     }
19388                     break;
19389                 case 1:
19390                 case 2:
19391                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19392                         v.hide();
19393                     }
19394                     break;
19395             }
19396         });
19397         
19398         Roo.each(this.picker().select('.next', true).elements, function(v){
19399             v.show();
19400             switch (this.viewMode) {
19401                 case 0:
19402
19403                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19404                         v.hide();
19405                     }
19406                     break;
19407                 case 1:
19408                 case 2:
19409                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19410                         v.hide();
19411                     }
19412                     break;
19413             }
19414         })
19415     },
19416     
19417     moveMonth: function(date, dir)
19418     {
19419         if (!dir) {
19420             return date;
19421         }
19422         var new_date = new Date(date.valueOf()),
19423         day = new_date.getUTCDate(),
19424         month = new_date.getUTCMonth(),
19425         mag = Math.abs(dir),
19426         new_month, test;
19427         dir = dir > 0 ? 1 : -1;
19428         if (mag == 1){
19429             test = dir == -1
19430             // If going back one month, make sure month is not current month
19431             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19432             ? function(){
19433                 return new_date.getUTCMonth() == month;
19434             }
19435             // If going forward one month, make sure month is as expected
19436             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19437             : function(){
19438                 return new_date.getUTCMonth() != new_month;
19439             };
19440             new_month = month + dir;
19441             new_date.setUTCMonth(new_month);
19442             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19443             if (new_month < 0 || new_month > 11) {
19444                 new_month = (new_month + 12) % 12;
19445             }
19446         } else {
19447             // For magnitudes >1, move one month at a time...
19448             for (var i=0; i<mag; i++) {
19449                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19450                 new_date = this.moveMonth(new_date, dir);
19451             }
19452             // ...then reset the day, keeping it in the new month
19453             new_month = new_date.getUTCMonth();
19454             new_date.setUTCDate(day);
19455             test = function(){
19456                 return new_month != new_date.getUTCMonth();
19457             };
19458         }
19459         // Common date-resetting loop -- if date is beyond end of month, make it
19460         // end of month
19461         while (test()){
19462             new_date.setUTCDate(--day);
19463             new_date.setUTCMonth(new_month);
19464         }
19465         return new_date;
19466     },
19467
19468     moveYear: function(date, dir)
19469     {
19470         return this.moveMonth(date, dir*12);
19471     },
19472
19473     dateWithinRange: function(date)
19474     {
19475         return date >= this.startDate && date <= this.endDate;
19476     },
19477
19478     
19479     remove: function() 
19480     {
19481         this.picker().remove();
19482     },
19483     
19484     validateValue : function(value)
19485     {
19486         if(this.getVisibilityEl().hasClass('hidden')){
19487             return true;
19488         }
19489         
19490         if(value.length < 1)  {
19491             if(this.allowBlank){
19492                 return true;
19493             }
19494             return false;
19495         }
19496         
19497         if(value.length < this.minLength){
19498             return false;
19499         }
19500         if(value.length > this.maxLength){
19501             return false;
19502         }
19503         if(this.vtype){
19504             var vt = Roo.form.VTypes;
19505             if(!vt[this.vtype](value, this)){
19506                 return false;
19507             }
19508         }
19509         if(typeof this.validator == "function"){
19510             var msg = this.validator(value);
19511             if(msg !== true){
19512                 return false;
19513             }
19514         }
19515         
19516         if(this.regex && !this.regex.test(value)){
19517             return false;
19518         }
19519         
19520         if(typeof(this.parseDate(value)) == 'undefined'){
19521             return false;
19522         }
19523         
19524         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19525             return false;
19526         }      
19527         
19528         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19529             return false;
19530         } 
19531         
19532         
19533         return true;
19534     },
19535     
19536     reset : function()
19537     {
19538         this.date = this.viewDate = '';
19539         
19540         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19541     }
19542    
19543 });
19544
19545 Roo.apply(Roo.bootstrap.DateField,  {
19546     
19547     head : {
19548         tag: 'thead',
19549         cn: [
19550         {
19551             tag: 'tr',
19552             cn: [
19553             {
19554                 tag: 'th',
19555                 cls: 'prev',
19556                 html: '<i class="fa fa-arrow-left"/>'
19557             },
19558             {
19559                 tag: 'th',
19560                 cls: 'switch',
19561                 colspan: '5'
19562             },
19563             {
19564                 tag: 'th',
19565                 cls: 'next',
19566                 html: '<i class="fa fa-arrow-right"/>'
19567             }
19568
19569             ]
19570         }
19571         ]
19572     },
19573     
19574     content : {
19575         tag: 'tbody',
19576         cn: [
19577         {
19578             tag: 'tr',
19579             cn: [
19580             {
19581                 tag: 'td',
19582                 colspan: '7'
19583             }
19584             ]
19585         }
19586         ]
19587     },
19588     
19589     footer : {
19590         tag: 'tfoot',
19591         cn: [
19592         {
19593             tag: 'tr',
19594             cn: [
19595             {
19596                 tag: 'th',
19597                 colspan: '7',
19598                 cls: 'today'
19599             }
19600                     
19601             ]
19602         }
19603         ]
19604     },
19605     
19606     dates:{
19607         en: {
19608             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19609             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19610             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19611             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19612             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19613             today: "Today"
19614         }
19615     },
19616     
19617     modes: [
19618     {
19619         clsName: 'days',
19620         navFnc: 'Month',
19621         navStep: 1
19622     },
19623     {
19624         clsName: 'months',
19625         navFnc: 'FullYear',
19626         navStep: 1
19627     },
19628     {
19629         clsName: 'years',
19630         navFnc: 'FullYear',
19631         navStep: 10
19632     }]
19633 });
19634
19635 Roo.apply(Roo.bootstrap.DateField,  {
19636   
19637     template : {
19638         tag: 'div',
19639         cls: 'datepicker dropdown-menu roo-dynamic',
19640         cn: [
19641         {
19642             tag: 'div',
19643             cls: 'datepicker-days',
19644             cn: [
19645             {
19646                 tag: 'table',
19647                 cls: 'table-condensed',
19648                 cn:[
19649                 Roo.bootstrap.DateField.head,
19650                 {
19651                     tag: 'tbody'
19652                 },
19653                 Roo.bootstrap.DateField.footer
19654                 ]
19655             }
19656             ]
19657         },
19658         {
19659             tag: 'div',
19660             cls: 'datepicker-months',
19661             cn: [
19662             {
19663                 tag: 'table',
19664                 cls: 'table-condensed',
19665                 cn:[
19666                 Roo.bootstrap.DateField.head,
19667                 Roo.bootstrap.DateField.content,
19668                 Roo.bootstrap.DateField.footer
19669                 ]
19670             }
19671             ]
19672         },
19673         {
19674             tag: 'div',
19675             cls: 'datepicker-years',
19676             cn: [
19677             {
19678                 tag: 'table',
19679                 cls: 'table-condensed',
19680                 cn:[
19681                 Roo.bootstrap.DateField.head,
19682                 Roo.bootstrap.DateField.content,
19683                 Roo.bootstrap.DateField.footer
19684                 ]
19685             }
19686             ]
19687         }
19688         ]
19689     }
19690 });
19691
19692  
19693
19694  /*
19695  * - LGPL
19696  *
19697  * TimeField
19698  * 
19699  */
19700
19701 /**
19702  * @class Roo.bootstrap.TimeField
19703  * @extends Roo.bootstrap.Input
19704  * Bootstrap DateField class
19705  * 
19706  * 
19707  * @constructor
19708  * Create a new TimeField
19709  * @param {Object} config The config object
19710  */
19711
19712 Roo.bootstrap.TimeField = function(config){
19713     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19714     this.addEvents({
19715             /**
19716              * @event show
19717              * Fires when this field show.
19718              * @param {Roo.bootstrap.DateField} thisthis
19719              * @param {Mixed} date The date value
19720              */
19721             show : true,
19722             /**
19723              * @event show
19724              * Fires when this field hide.
19725              * @param {Roo.bootstrap.DateField} this
19726              * @param {Mixed} date The date value
19727              */
19728             hide : true,
19729             /**
19730              * @event select
19731              * Fires when select a date.
19732              * @param {Roo.bootstrap.DateField} this
19733              * @param {Mixed} date The date value
19734              */
19735             select : true
19736         });
19737 };
19738
19739 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19740     
19741     /**
19742      * @cfg {String} format
19743      * The default time format string which can be overriden for localization support.  The format must be
19744      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19745      */
19746     format : "H:i",
19747        
19748     onRender: function(ct, position)
19749     {
19750         
19751         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19752                 
19753         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19754         
19755         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19756         
19757         this.pop = this.picker().select('>.datepicker-time',true).first();
19758         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19759         
19760         this.picker().on('mousedown', this.onMousedown, this);
19761         this.picker().on('click', this.onClick, this);
19762         
19763         this.picker().addClass('datepicker-dropdown');
19764     
19765         this.fillTime();
19766         this.update();
19767             
19768         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19769         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19770         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19771         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19772         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19773         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19774
19775     },
19776     
19777     fireKey: function(e){
19778         if (!this.picker().isVisible()){
19779             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19780                 this.show();
19781             }
19782             return;
19783         }
19784
19785         e.preventDefault();
19786         
19787         switch(e.keyCode){
19788             case 27: // escape
19789                 this.hide();
19790                 break;
19791             case 37: // left
19792             case 39: // right
19793                 this.onTogglePeriod();
19794                 break;
19795             case 38: // up
19796                 this.onIncrementMinutes();
19797                 break;
19798             case 40: // down
19799                 this.onDecrementMinutes();
19800                 break;
19801             case 13: // enter
19802             case 9: // tab
19803                 this.setTime();
19804                 break;
19805         }
19806     },
19807     
19808     onClick: function(e) {
19809         e.stopPropagation();
19810         e.preventDefault();
19811     },
19812     
19813     picker : function()
19814     {
19815         return this.el.select('.datepicker', true).first();
19816     },
19817     
19818     fillTime: function()
19819     {    
19820         var time = this.pop.select('tbody', true).first();
19821         
19822         time.dom.innerHTML = '';
19823         
19824         time.createChild({
19825             tag: 'tr',
19826             cn: [
19827                 {
19828                     tag: 'td',
19829                     cn: [
19830                         {
19831                             tag: 'a',
19832                             href: '#',
19833                             cls: 'btn',
19834                             cn: [
19835                                 {
19836                                     tag: 'span',
19837                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19838                                 }
19839                             ]
19840                         } 
19841                     ]
19842                 },
19843                 {
19844                     tag: 'td',
19845                     cls: 'separator'
19846                 },
19847                 {
19848                     tag: 'td',
19849                     cn: [
19850                         {
19851                             tag: 'a',
19852                             href: '#',
19853                             cls: 'btn',
19854                             cn: [
19855                                 {
19856                                     tag: 'span',
19857                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19858                                 }
19859                             ]
19860                         }
19861                     ]
19862                 },
19863                 {
19864                     tag: 'td',
19865                     cls: 'separator'
19866                 }
19867             ]
19868         });
19869         
19870         time.createChild({
19871             tag: 'tr',
19872             cn: [
19873                 {
19874                     tag: 'td',
19875                     cn: [
19876                         {
19877                             tag: 'span',
19878                             cls: 'timepicker-hour',
19879                             html: '00'
19880                         }  
19881                     ]
19882                 },
19883                 {
19884                     tag: 'td',
19885                     cls: 'separator',
19886                     html: ':'
19887                 },
19888                 {
19889                     tag: 'td',
19890                     cn: [
19891                         {
19892                             tag: 'span',
19893                             cls: 'timepicker-minute',
19894                             html: '00'
19895                         }  
19896                     ]
19897                 },
19898                 {
19899                     tag: 'td',
19900                     cls: 'separator'
19901                 },
19902                 {
19903                     tag: 'td',
19904                     cn: [
19905                         {
19906                             tag: 'button',
19907                             type: 'button',
19908                             cls: 'btn btn-primary period',
19909                             html: 'AM'
19910                             
19911                         }
19912                     ]
19913                 }
19914             ]
19915         });
19916         
19917         time.createChild({
19918             tag: 'tr',
19919             cn: [
19920                 {
19921                     tag: 'td',
19922                     cn: [
19923                         {
19924                             tag: 'a',
19925                             href: '#',
19926                             cls: 'btn',
19927                             cn: [
19928                                 {
19929                                     tag: 'span',
19930                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19931                                 }
19932                             ]
19933                         }
19934                     ]
19935                 },
19936                 {
19937                     tag: 'td',
19938                     cls: 'separator'
19939                 },
19940                 {
19941                     tag: 'td',
19942                     cn: [
19943                         {
19944                             tag: 'a',
19945                             href: '#',
19946                             cls: 'btn',
19947                             cn: [
19948                                 {
19949                                     tag: 'span',
19950                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19951                                 }
19952                             ]
19953                         }
19954                     ]
19955                 },
19956                 {
19957                     tag: 'td',
19958                     cls: 'separator'
19959                 }
19960             ]
19961         });
19962         
19963     },
19964     
19965     update: function()
19966     {
19967         
19968         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19969         
19970         this.fill();
19971     },
19972     
19973     fill: function() 
19974     {
19975         var hours = this.time.getHours();
19976         var minutes = this.time.getMinutes();
19977         var period = 'AM';
19978         
19979         if(hours > 11){
19980             period = 'PM';
19981         }
19982         
19983         if(hours == 0){
19984             hours = 12;
19985         }
19986         
19987         
19988         if(hours > 12){
19989             hours = hours - 12;
19990         }
19991         
19992         if(hours < 10){
19993             hours = '0' + hours;
19994         }
19995         
19996         if(minutes < 10){
19997             minutes = '0' + minutes;
19998         }
19999         
20000         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20001         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20002         this.pop.select('button', true).first().dom.innerHTML = period;
20003         
20004     },
20005     
20006     place: function()
20007     {   
20008         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20009         
20010         var cls = ['bottom'];
20011         
20012         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20013             cls.pop();
20014             cls.push('top');
20015         }
20016         
20017         cls.push('right');
20018         
20019         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20020             cls.pop();
20021             cls.push('left');
20022         }
20023         
20024         this.picker().addClass(cls.join('-'));
20025         
20026         var _this = this;
20027         
20028         Roo.each(cls, function(c){
20029             if(c == 'bottom'){
20030                 _this.picker().setTop(_this.inputEl().getHeight());
20031                 return;
20032             }
20033             if(c == 'top'){
20034                 _this.picker().setTop(0 - _this.picker().getHeight());
20035                 return;
20036             }
20037             
20038             if(c == 'left'){
20039                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20040                 return;
20041             }
20042             if(c == 'right'){
20043                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20044                 return;
20045             }
20046         });
20047         
20048     },
20049   
20050     onFocus : function()
20051     {
20052         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20053         this.show();
20054     },
20055     
20056     onBlur : function()
20057     {
20058         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20059         this.hide();
20060     },
20061     
20062     show : function()
20063     {
20064         this.picker().show();
20065         this.pop.show();
20066         this.update();
20067         this.place();
20068         
20069         this.fireEvent('show', this, this.date);
20070     },
20071     
20072     hide : function()
20073     {
20074         this.picker().hide();
20075         this.pop.hide();
20076         
20077         this.fireEvent('hide', this, this.date);
20078     },
20079     
20080     setTime : function()
20081     {
20082         this.hide();
20083         this.setValue(this.time.format(this.format));
20084         
20085         this.fireEvent('select', this, this.date);
20086         
20087         
20088     },
20089     
20090     onMousedown: function(e){
20091         e.stopPropagation();
20092         e.preventDefault();
20093     },
20094     
20095     onIncrementHours: function()
20096     {
20097         Roo.log('onIncrementHours');
20098         this.time = this.time.add(Date.HOUR, 1);
20099         this.update();
20100         
20101     },
20102     
20103     onDecrementHours: function()
20104     {
20105         Roo.log('onDecrementHours');
20106         this.time = this.time.add(Date.HOUR, -1);
20107         this.update();
20108     },
20109     
20110     onIncrementMinutes: function()
20111     {
20112         Roo.log('onIncrementMinutes');
20113         this.time = this.time.add(Date.MINUTE, 1);
20114         this.update();
20115     },
20116     
20117     onDecrementMinutes: function()
20118     {
20119         Roo.log('onDecrementMinutes');
20120         this.time = this.time.add(Date.MINUTE, -1);
20121         this.update();
20122     },
20123     
20124     onTogglePeriod: function()
20125     {
20126         Roo.log('onTogglePeriod');
20127         this.time = this.time.add(Date.HOUR, 12);
20128         this.update();
20129     }
20130     
20131    
20132 });
20133
20134 Roo.apply(Roo.bootstrap.TimeField,  {
20135     
20136     content : {
20137         tag: 'tbody',
20138         cn: [
20139             {
20140                 tag: 'tr',
20141                 cn: [
20142                 {
20143                     tag: 'td',
20144                     colspan: '7'
20145                 }
20146                 ]
20147             }
20148         ]
20149     },
20150     
20151     footer : {
20152         tag: 'tfoot',
20153         cn: [
20154             {
20155                 tag: 'tr',
20156                 cn: [
20157                 {
20158                     tag: 'th',
20159                     colspan: '7',
20160                     cls: '',
20161                     cn: [
20162                         {
20163                             tag: 'button',
20164                             cls: 'btn btn-info ok',
20165                             html: 'OK'
20166                         }
20167                     ]
20168                 }
20169
20170                 ]
20171             }
20172         ]
20173     }
20174 });
20175
20176 Roo.apply(Roo.bootstrap.TimeField,  {
20177   
20178     template : {
20179         tag: 'div',
20180         cls: 'datepicker dropdown-menu',
20181         cn: [
20182             {
20183                 tag: 'div',
20184                 cls: 'datepicker-time',
20185                 cn: [
20186                 {
20187                     tag: 'table',
20188                     cls: 'table-condensed',
20189                     cn:[
20190                     Roo.bootstrap.TimeField.content,
20191                     Roo.bootstrap.TimeField.footer
20192                     ]
20193                 }
20194                 ]
20195             }
20196         ]
20197     }
20198 });
20199
20200  
20201
20202  /*
20203  * - LGPL
20204  *
20205  * MonthField
20206  * 
20207  */
20208
20209 /**
20210  * @class Roo.bootstrap.MonthField
20211  * @extends Roo.bootstrap.Input
20212  * Bootstrap MonthField class
20213  * 
20214  * @cfg {String} language default en
20215  * 
20216  * @constructor
20217  * Create a new MonthField
20218  * @param {Object} config The config object
20219  */
20220
20221 Roo.bootstrap.MonthField = function(config){
20222     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20223     
20224     this.addEvents({
20225         /**
20226          * @event show
20227          * Fires when this field show.
20228          * @param {Roo.bootstrap.MonthField} this
20229          * @param {Mixed} date The date value
20230          */
20231         show : true,
20232         /**
20233          * @event show
20234          * Fires when this field hide.
20235          * @param {Roo.bootstrap.MonthField} this
20236          * @param {Mixed} date The date value
20237          */
20238         hide : true,
20239         /**
20240          * @event select
20241          * Fires when select a date.
20242          * @param {Roo.bootstrap.MonthField} this
20243          * @param {String} oldvalue The old value
20244          * @param {String} newvalue The new value
20245          */
20246         select : true
20247     });
20248 };
20249
20250 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20251     
20252     onRender: function(ct, position)
20253     {
20254         
20255         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20256         
20257         this.language = this.language || 'en';
20258         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20259         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20260         
20261         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20262         this.isInline = false;
20263         this.isInput = true;
20264         this.component = this.el.select('.add-on', true).first() || false;
20265         this.component = (this.component && this.component.length === 0) ? false : this.component;
20266         this.hasInput = this.component && this.inputEL().length;
20267         
20268         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20269         
20270         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20271         
20272         this.picker().on('mousedown', this.onMousedown, this);
20273         this.picker().on('click', this.onClick, this);
20274         
20275         this.picker().addClass('datepicker-dropdown');
20276         
20277         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20278             v.setStyle('width', '189px');
20279         });
20280         
20281         this.fillMonths();
20282         
20283         this.update();
20284         
20285         if(this.isInline) {
20286             this.show();
20287         }
20288         
20289     },
20290     
20291     setValue: function(v, suppressEvent)
20292     {   
20293         var o = this.getValue();
20294         
20295         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20296         
20297         this.update();
20298
20299         if(suppressEvent !== true){
20300             this.fireEvent('select', this, o, v);
20301         }
20302         
20303     },
20304     
20305     getValue: function()
20306     {
20307         return this.value;
20308     },
20309     
20310     onClick: function(e) 
20311     {
20312         e.stopPropagation();
20313         e.preventDefault();
20314         
20315         var target = e.getTarget();
20316         
20317         if(target.nodeName.toLowerCase() === 'i'){
20318             target = Roo.get(target).dom.parentNode;
20319         }
20320         
20321         var nodeName = target.nodeName;
20322         var className = target.className;
20323         var html = target.innerHTML;
20324         
20325         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20326             return;
20327         }
20328         
20329         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20330         
20331         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20332         
20333         this.hide();
20334                         
20335     },
20336     
20337     picker : function()
20338     {
20339         return this.pickerEl;
20340     },
20341     
20342     fillMonths: function()
20343     {    
20344         var i = 0;
20345         var months = this.picker().select('>.datepicker-months td', true).first();
20346         
20347         months.dom.innerHTML = '';
20348         
20349         while (i < 12) {
20350             var month = {
20351                 tag: 'span',
20352                 cls: 'month',
20353                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20354             };
20355             
20356             months.createChild(month);
20357         }
20358         
20359     },
20360     
20361     update: function()
20362     {
20363         var _this = this;
20364         
20365         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20366             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20367         }
20368         
20369         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20370             e.removeClass('active');
20371             
20372             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20373                 e.addClass('active');
20374             }
20375         })
20376     },
20377     
20378     place: function()
20379     {
20380         if(this.isInline) {
20381             return;
20382         }
20383         
20384         this.picker().removeClass(['bottom', 'top']);
20385         
20386         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20387             /*
20388              * place to the top of element!
20389              *
20390              */
20391             
20392             this.picker().addClass('top');
20393             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20394             
20395             return;
20396         }
20397         
20398         this.picker().addClass('bottom');
20399         
20400         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20401     },
20402     
20403     onFocus : function()
20404     {
20405         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20406         this.show();
20407     },
20408     
20409     onBlur : function()
20410     {
20411         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20412         
20413         var d = this.inputEl().getValue();
20414         
20415         this.setValue(d);
20416                 
20417         this.hide();
20418     },
20419     
20420     show : function()
20421     {
20422         this.picker().show();
20423         this.picker().select('>.datepicker-months', true).first().show();
20424         this.update();
20425         this.place();
20426         
20427         this.fireEvent('show', this, this.date);
20428     },
20429     
20430     hide : function()
20431     {
20432         if(this.isInline) {
20433             return;
20434         }
20435         this.picker().hide();
20436         this.fireEvent('hide', this, this.date);
20437         
20438     },
20439     
20440     onMousedown: function(e)
20441     {
20442         e.stopPropagation();
20443         e.preventDefault();
20444     },
20445     
20446     keyup: function(e)
20447     {
20448         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20449         this.update();
20450     },
20451
20452     fireKey: function(e)
20453     {
20454         if (!this.picker().isVisible()){
20455             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20456                 this.show();
20457             }
20458             return;
20459         }
20460         
20461         var dir;
20462         
20463         switch(e.keyCode){
20464             case 27: // escape
20465                 this.hide();
20466                 e.preventDefault();
20467                 break;
20468             case 37: // left
20469             case 39: // right
20470                 dir = e.keyCode == 37 ? -1 : 1;
20471                 
20472                 this.vIndex = this.vIndex + dir;
20473                 
20474                 if(this.vIndex < 0){
20475                     this.vIndex = 0;
20476                 }
20477                 
20478                 if(this.vIndex > 11){
20479                     this.vIndex = 11;
20480                 }
20481                 
20482                 if(isNaN(this.vIndex)){
20483                     this.vIndex = 0;
20484                 }
20485                 
20486                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20487                 
20488                 break;
20489             case 38: // up
20490             case 40: // down
20491                 
20492                 dir = e.keyCode == 38 ? -1 : 1;
20493                 
20494                 this.vIndex = this.vIndex + dir * 4;
20495                 
20496                 if(this.vIndex < 0){
20497                     this.vIndex = 0;
20498                 }
20499                 
20500                 if(this.vIndex > 11){
20501                     this.vIndex = 11;
20502                 }
20503                 
20504                 if(isNaN(this.vIndex)){
20505                     this.vIndex = 0;
20506                 }
20507                 
20508                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20509                 break;
20510                 
20511             case 13: // enter
20512                 
20513                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20514                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20515                 }
20516                 
20517                 this.hide();
20518                 e.preventDefault();
20519                 break;
20520             case 9: // tab
20521                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20522                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20523                 }
20524                 this.hide();
20525                 break;
20526             case 16: // shift
20527             case 17: // ctrl
20528             case 18: // alt
20529                 break;
20530             default :
20531                 this.hide();
20532                 
20533         }
20534     },
20535     
20536     remove: function() 
20537     {
20538         this.picker().remove();
20539     }
20540    
20541 });
20542
20543 Roo.apply(Roo.bootstrap.MonthField,  {
20544     
20545     content : {
20546         tag: 'tbody',
20547         cn: [
20548         {
20549             tag: 'tr',
20550             cn: [
20551             {
20552                 tag: 'td',
20553                 colspan: '7'
20554             }
20555             ]
20556         }
20557         ]
20558     },
20559     
20560     dates:{
20561         en: {
20562             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20563             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20564         }
20565     }
20566 });
20567
20568 Roo.apply(Roo.bootstrap.MonthField,  {
20569   
20570     template : {
20571         tag: 'div',
20572         cls: 'datepicker dropdown-menu roo-dynamic',
20573         cn: [
20574             {
20575                 tag: 'div',
20576                 cls: 'datepicker-months',
20577                 cn: [
20578                 {
20579                     tag: 'table',
20580                     cls: 'table-condensed',
20581                     cn:[
20582                         Roo.bootstrap.DateField.content
20583                     ]
20584                 }
20585                 ]
20586             }
20587         ]
20588     }
20589 });
20590
20591  
20592
20593  
20594  /*
20595  * - LGPL
20596  *
20597  * CheckBox
20598  * 
20599  */
20600
20601 /**
20602  * @class Roo.bootstrap.CheckBox
20603  * @extends Roo.bootstrap.Input
20604  * Bootstrap CheckBox class
20605  * 
20606  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20607  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20608  * @cfg {String} boxLabel The text that appears beside the checkbox
20609  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20610  * @cfg {Boolean} checked initnal the element
20611  * @cfg {Boolean} inline inline the element (default false)
20612  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20613  * @cfg {String} tooltip label tooltip
20614  * 
20615  * @constructor
20616  * Create a new CheckBox
20617  * @param {Object} config The config object
20618  */
20619
20620 Roo.bootstrap.CheckBox = function(config){
20621     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20622    
20623     this.addEvents({
20624         /**
20625         * @event check
20626         * Fires when the element is checked or unchecked.
20627         * @param {Roo.bootstrap.CheckBox} this This input
20628         * @param {Boolean} checked The new checked value
20629         */
20630        check : true,
20631        /**
20632         * @event click
20633         * Fires when the element is click.
20634         * @param {Roo.bootstrap.CheckBox} this This input
20635         */
20636        click : true
20637     });
20638     
20639 };
20640
20641 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20642   
20643     inputType: 'checkbox',
20644     inputValue: 1,
20645     valueOff: 0,
20646     boxLabel: false,
20647     checked: false,
20648     weight : false,
20649     inline: false,
20650     tooltip : '',
20651     
20652     getAutoCreate : function()
20653     {
20654         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20655         
20656         var id = Roo.id();
20657         
20658         var cfg = {};
20659         
20660         cfg.cls = 'form-group ' + this.inputType; //input-group
20661         
20662         if(this.inline){
20663             cfg.cls += ' ' + this.inputType + '-inline';
20664         }
20665         
20666         var input =  {
20667             tag: 'input',
20668             id : id,
20669             type : this.inputType,
20670             value : this.inputValue,
20671             cls : 'roo-' + this.inputType, //'form-box',
20672             placeholder : this.placeholder || ''
20673             
20674         };
20675         
20676         if(this.inputType != 'radio'){
20677             var hidden =  {
20678                 tag: 'input',
20679                 type : 'hidden',
20680                 cls : 'roo-hidden-value',
20681                 value : this.checked ? this.inputValue : this.valueOff
20682             };
20683         }
20684         
20685             
20686         if (this.weight) { // Validity check?
20687             cfg.cls += " " + this.inputType + "-" + this.weight;
20688         }
20689         
20690         if (this.disabled) {
20691             input.disabled=true;
20692         }
20693         
20694         if(this.checked){
20695             input.checked = this.checked;
20696         }
20697         
20698         if (this.name) {
20699             
20700             input.name = this.name;
20701             
20702             if(this.inputType != 'radio'){
20703                 hidden.name = this.name;
20704                 input.name = '_hidden_' + this.name;
20705             }
20706         }
20707         
20708         if (this.size) {
20709             input.cls += ' input-' + this.size;
20710         }
20711         
20712         var settings=this;
20713         
20714         ['xs','sm','md','lg'].map(function(size){
20715             if (settings[size]) {
20716                 cfg.cls += ' col-' + size + '-' + settings[size];
20717             }
20718         });
20719         
20720         var inputblock = input;
20721          
20722         if (this.before || this.after) {
20723             
20724             inputblock = {
20725                 cls : 'input-group',
20726                 cn :  [] 
20727             };
20728             
20729             if (this.before) {
20730                 inputblock.cn.push({
20731                     tag :'span',
20732                     cls : 'input-group-addon',
20733                     html : this.before
20734                 });
20735             }
20736             
20737             inputblock.cn.push(input);
20738             
20739             if(this.inputType != 'radio'){
20740                 inputblock.cn.push(hidden);
20741             }
20742             
20743             if (this.after) {
20744                 inputblock.cn.push({
20745                     tag :'span',
20746                     cls : 'input-group-addon',
20747                     html : this.after
20748                 });
20749             }
20750             
20751         }
20752         
20753         if (align ==='left' && this.fieldLabel.length) {
20754 //                Roo.log("left and has label");
20755             cfg.cn = [
20756                 {
20757                     tag: 'label',
20758                     'for' :  id,
20759                     cls : 'control-label',
20760                     html : this.fieldLabel
20761                 },
20762                 {
20763                     cls : "", 
20764                     cn: [
20765                         inputblock
20766                     ]
20767                 }
20768             ];
20769             
20770             if(this.labelWidth > 12){
20771                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20772             }
20773             
20774             if(this.labelWidth < 13 && this.labelmd == 0){
20775                 this.labelmd = this.labelWidth;
20776             }
20777             
20778             if(this.labellg > 0){
20779                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20780                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20781             }
20782             
20783             if(this.labelmd > 0){
20784                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20785                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20786             }
20787             
20788             if(this.labelsm > 0){
20789                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20790                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20791             }
20792             
20793             if(this.labelxs > 0){
20794                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20795                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20796             }
20797             
20798         } else if ( this.fieldLabel.length) {
20799 //                Roo.log(" label");
20800                 cfg.cn = [
20801                    
20802                     {
20803                         tag: this.boxLabel ? 'span' : 'label',
20804                         'for': id,
20805                         cls: 'control-label box-input-label',
20806                         //cls : 'input-group-addon',
20807                         html : this.fieldLabel
20808                     },
20809                     
20810                     inputblock
20811                     
20812                 ];
20813
20814         } else {
20815             
20816 //                Roo.log(" no label && no align");
20817                 cfg.cn = [  inputblock ] ;
20818                 
20819                 
20820         }
20821         
20822         if(this.boxLabel){
20823              var boxLabelCfg = {
20824                 tag: 'label',
20825                 //'for': id, // box label is handled by onclick - so no for...
20826                 cls: 'box-label',
20827                 html: this.boxLabel
20828             };
20829             
20830             if(this.tooltip){
20831                 boxLabelCfg.tooltip = this.tooltip;
20832             }
20833              
20834             cfg.cn.push(boxLabelCfg);
20835         }
20836         
20837         if(this.inputType != 'radio'){
20838             cfg.cn.push(hidden);
20839         }
20840         
20841         return cfg;
20842         
20843     },
20844     
20845     /**
20846      * return the real input element.
20847      */
20848     inputEl: function ()
20849     {
20850         return this.el.select('input.roo-' + this.inputType,true).first();
20851     },
20852     hiddenEl: function ()
20853     {
20854         return this.el.select('input.roo-hidden-value',true).first();
20855     },
20856     
20857     labelEl: function()
20858     {
20859         return this.el.select('label.control-label',true).first();
20860     },
20861     /* depricated... */
20862     
20863     label: function()
20864     {
20865         return this.labelEl();
20866     },
20867     
20868     boxLabelEl: function()
20869     {
20870         return this.el.select('label.box-label',true).first();
20871     },
20872     
20873     initEvents : function()
20874     {
20875 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20876         
20877         this.inputEl().on('click', this.onClick,  this);
20878         
20879         if (this.boxLabel) { 
20880             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20881         }
20882         
20883         this.startValue = this.getValue();
20884         
20885         if(this.groupId){
20886             Roo.bootstrap.CheckBox.register(this);
20887         }
20888     },
20889     
20890     onClick : function(e)
20891     {   
20892         if(this.fireEvent('click', this, e) !== false){
20893             this.setChecked(!this.checked);
20894         }
20895         
20896     },
20897     
20898     setChecked : function(state,suppressEvent)
20899     {
20900         this.startValue = this.getValue();
20901
20902         if(this.inputType == 'radio'){
20903             
20904             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20905                 e.dom.checked = false;
20906             });
20907             
20908             this.inputEl().dom.checked = true;
20909             
20910             this.inputEl().dom.value = this.inputValue;
20911             
20912             if(suppressEvent !== true){
20913                 this.fireEvent('check', this, true);
20914             }
20915             
20916             this.validate();
20917             
20918             return;
20919         }
20920         
20921         this.checked = state;
20922         
20923         this.inputEl().dom.checked = state;
20924         
20925         
20926         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20927         
20928         if(suppressEvent !== true){
20929             this.fireEvent('check', this, state);
20930         }
20931         
20932         this.validate();
20933     },
20934     
20935     getValue : function()
20936     {
20937         if(this.inputType == 'radio'){
20938             return this.getGroupValue();
20939         }
20940         
20941         return this.hiddenEl().dom.value;
20942         
20943     },
20944     
20945     getGroupValue : function()
20946     {
20947         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20948             return '';
20949         }
20950         
20951         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20952     },
20953     
20954     setValue : function(v,suppressEvent)
20955     {
20956         if(this.inputType == 'radio'){
20957             this.setGroupValue(v, suppressEvent);
20958             return;
20959         }
20960         
20961         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20962         
20963         this.validate();
20964     },
20965     
20966     setGroupValue : function(v, suppressEvent)
20967     {
20968         this.startValue = this.getValue();
20969         
20970         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20971             e.dom.checked = false;
20972             
20973             if(e.dom.value == v){
20974                 e.dom.checked = true;
20975             }
20976         });
20977         
20978         if(suppressEvent !== true){
20979             this.fireEvent('check', this, true);
20980         }
20981
20982         this.validate();
20983         
20984         return;
20985     },
20986     
20987     validate : function()
20988     {
20989         if(this.getVisibilityEl().hasClass('hidden')){
20990             return true;
20991         }
20992         
20993         if(
20994                 this.disabled || 
20995                 (this.inputType == 'radio' && this.validateRadio()) ||
20996                 (this.inputType == 'checkbox' && this.validateCheckbox())
20997         ){
20998             this.markValid();
20999             return true;
21000         }
21001         
21002         this.markInvalid();
21003         return false;
21004     },
21005     
21006     validateRadio : function()
21007     {
21008         if(this.getVisibilityEl().hasClass('hidden')){
21009             return true;
21010         }
21011         
21012         if(this.allowBlank){
21013             return true;
21014         }
21015         
21016         var valid = false;
21017         
21018         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21019             if(!e.dom.checked){
21020                 return;
21021             }
21022             
21023             valid = true;
21024             
21025             return false;
21026         });
21027         
21028         return valid;
21029     },
21030     
21031     validateCheckbox : function()
21032     {
21033         if(!this.groupId){
21034             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21035             //return (this.getValue() == this.inputValue) ? true : false;
21036         }
21037         
21038         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21039         
21040         if(!group){
21041             return false;
21042         }
21043         
21044         var r = false;
21045         
21046         for(var i in group){
21047             if(group[i].el.isVisible(true)){
21048                 r = false;
21049                 break;
21050             }
21051             
21052             r = true;
21053         }
21054         
21055         for(var i in group){
21056             if(r){
21057                 break;
21058             }
21059             
21060             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21061         }
21062         
21063         return r;
21064     },
21065     
21066     /**
21067      * Mark this field as valid
21068      */
21069     markValid : function()
21070     {
21071         var _this = this;
21072         
21073         this.fireEvent('valid', this);
21074         
21075         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21076         
21077         if(this.groupId){
21078             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21079         }
21080         
21081         if(label){
21082             label.markValid();
21083         }
21084
21085         if(this.inputType == 'radio'){
21086             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21087                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21088                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21089             });
21090             
21091             return;
21092         }
21093
21094         if(!this.groupId){
21095             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21096             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21097             return;
21098         }
21099         
21100         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21101         
21102         if(!group){
21103             return;
21104         }
21105         
21106         for(var i in group){
21107             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21108             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21109         }
21110     },
21111     
21112      /**
21113      * Mark this field as invalid
21114      * @param {String} msg The validation message
21115      */
21116     markInvalid : function(msg)
21117     {
21118         if(this.allowBlank){
21119             return;
21120         }
21121         
21122         var _this = this;
21123         
21124         this.fireEvent('invalid', this, msg);
21125         
21126         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21127         
21128         if(this.groupId){
21129             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21130         }
21131         
21132         if(label){
21133             label.markInvalid();
21134         }
21135             
21136         if(this.inputType == 'radio'){
21137             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21138                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21139                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21140             });
21141             
21142             return;
21143         }
21144         
21145         if(!this.groupId){
21146             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21147             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21148             return;
21149         }
21150         
21151         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21152         
21153         if(!group){
21154             return;
21155         }
21156         
21157         for(var i in group){
21158             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21159             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21160         }
21161         
21162     },
21163     
21164     clearInvalid : function()
21165     {
21166         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21167         
21168         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21169         
21170         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21171         
21172         if (label && label.iconEl) {
21173             label.iconEl.removeClass(label.validClass);
21174             label.iconEl.removeClass(label.invalidClass);
21175         }
21176     },
21177     
21178     disable : function()
21179     {
21180         if(this.inputType != 'radio'){
21181             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21182             return;
21183         }
21184         
21185         var _this = this;
21186         
21187         if(this.rendered){
21188             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21189                 _this.getActionEl().addClass(this.disabledClass);
21190                 e.dom.disabled = true;
21191             });
21192         }
21193         
21194         this.disabled = true;
21195         this.fireEvent("disable", this);
21196         return this;
21197     },
21198
21199     enable : function()
21200     {
21201         if(this.inputType != 'radio'){
21202             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21203             return;
21204         }
21205         
21206         var _this = this;
21207         
21208         if(this.rendered){
21209             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21210                 _this.getActionEl().removeClass(this.disabledClass);
21211                 e.dom.disabled = false;
21212             });
21213         }
21214         
21215         this.disabled = false;
21216         this.fireEvent("enable", this);
21217         return this;
21218     },
21219     
21220     setBoxLabel : function(v)
21221     {
21222         this.boxLabel = v;
21223         
21224         if(this.rendered){
21225             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21226         }
21227     }
21228
21229 });
21230
21231 Roo.apply(Roo.bootstrap.CheckBox, {
21232     
21233     groups: {},
21234     
21235      /**
21236     * register a CheckBox Group
21237     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21238     */
21239     register : function(checkbox)
21240     {
21241         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21242             this.groups[checkbox.groupId] = {};
21243         }
21244         
21245         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21246             return;
21247         }
21248         
21249         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21250         
21251     },
21252     /**
21253     * fetch a CheckBox Group based on the group ID
21254     * @param {string} the group ID
21255     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21256     */
21257     get: function(groupId) {
21258         if (typeof(this.groups[groupId]) == 'undefined') {
21259             return false;
21260         }
21261         
21262         return this.groups[groupId] ;
21263     }
21264     
21265     
21266 });
21267 /*
21268  * - LGPL
21269  *
21270  * RadioItem
21271  * 
21272  */
21273
21274 /**
21275  * @class Roo.bootstrap.Radio
21276  * @extends Roo.bootstrap.Component
21277  * Bootstrap Radio class
21278  * @cfg {String} boxLabel - the label associated
21279  * @cfg {String} value - the value of radio
21280  * 
21281  * @constructor
21282  * Create a new Radio
21283  * @param {Object} config The config object
21284  */
21285 Roo.bootstrap.Radio = function(config){
21286     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21287     
21288 };
21289
21290 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21291     
21292     boxLabel : '',
21293     
21294     value : '',
21295     
21296     getAutoCreate : function()
21297     {
21298         var cfg = {
21299             tag : 'div',
21300             cls : 'form-group radio',
21301             cn : [
21302                 {
21303                     tag : 'label',
21304                     cls : 'box-label',
21305                     html : this.boxLabel
21306                 }
21307             ]
21308         };
21309         
21310         return cfg;
21311     },
21312     
21313     initEvents : function() 
21314     {
21315         this.parent().register(this);
21316         
21317         this.el.on('click', this.onClick, this);
21318         
21319     },
21320     
21321     onClick : function(e)
21322     {
21323         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21324             this.setChecked(true);
21325         }
21326     },
21327     
21328     setChecked : function(state, suppressEvent)
21329     {
21330         this.parent().setValue(this.value, suppressEvent);
21331         
21332     },
21333     
21334     setBoxLabel : function(v)
21335     {
21336         this.boxLabel = v;
21337         
21338         if(this.rendered){
21339             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21340         }
21341     }
21342     
21343 });
21344  
21345
21346  /*
21347  * - LGPL
21348  *
21349  * Input
21350  * 
21351  */
21352
21353 /**
21354  * @class Roo.bootstrap.SecurePass
21355  * @extends Roo.bootstrap.Input
21356  * Bootstrap SecurePass class
21357  *
21358  * 
21359  * @constructor
21360  * Create a new SecurePass
21361  * @param {Object} config The config object
21362  */
21363  
21364 Roo.bootstrap.SecurePass = function (config) {
21365     // these go here, so the translation tool can replace them..
21366     this.errors = {
21367         PwdEmpty: "Please type a password, and then retype it to confirm.",
21368         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21369         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21370         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21371         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21372         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21373         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21374         TooWeak: "Your password is Too Weak."
21375     },
21376     this.meterLabel = "Password strength:";
21377     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21378     this.meterClass = [
21379         "roo-password-meter-tooweak", 
21380         "roo-password-meter-weak", 
21381         "roo-password-meter-medium", 
21382         "roo-password-meter-strong", 
21383         "roo-password-meter-grey"
21384     ];
21385     
21386     this.errors = {};
21387     
21388     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21389 }
21390
21391 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21392     /**
21393      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21394      * {
21395      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21396      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21397      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21398      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21399      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21400      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21401      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21402      * })
21403      */
21404     // private
21405     
21406     meterWidth: 300,
21407     errorMsg :'',    
21408     errors: false,
21409     imageRoot: '/',
21410     /**
21411      * @cfg {String/Object} Label for the strength meter (defaults to
21412      * 'Password strength:')
21413      */
21414     // private
21415     meterLabel: '',
21416     /**
21417      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21418      * ['Weak', 'Medium', 'Strong'])
21419      */
21420     // private    
21421     pwdStrengths: false,    
21422     // private
21423     strength: 0,
21424     // private
21425     _lastPwd: null,
21426     // private
21427     kCapitalLetter: 0,
21428     kSmallLetter: 1,
21429     kDigit: 2,
21430     kPunctuation: 3,
21431     
21432     insecure: false,
21433     // private
21434     initEvents: function ()
21435     {
21436         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21437
21438         if (this.el.is('input[type=password]') && Roo.isSafari) {
21439             this.el.on('keydown', this.SafariOnKeyDown, this);
21440         }
21441
21442         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21443     },
21444     // private
21445     onRender: function (ct, position)
21446     {
21447         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21448         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21449         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21450
21451         this.trigger.createChild({
21452                    cn: [
21453                     {
21454                     //id: 'PwdMeter',
21455                     tag: 'div',
21456                     cls: 'roo-password-meter-grey col-xs-12',
21457                     style: {
21458                         //width: 0,
21459                         //width: this.meterWidth + 'px'                                                
21460                         }
21461                     },
21462                     {                            
21463                          cls: 'roo-password-meter-text'                          
21464                     }
21465                 ]            
21466         });
21467
21468          
21469         if (this.hideTrigger) {
21470             this.trigger.setDisplayed(false);
21471         }
21472         this.setSize(this.width || '', this.height || '');
21473     },
21474     // private
21475     onDestroy: function ()
21476     {
21477         if (this.trigger) {
21478             this.trigger.removeAllListeners();
21479             this.trigger.remove();
21480         }
21481         if (this.wrap) {
21482             this.wrap.remove();
21483         }
21484         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21485     },
21486     // private
21487     checkStrength: function ()
21488     {
21489         var pwd = this.inputEl().getValue();
21490         if (pwd == this._lastPwd) {
21491             return;
21492         }
21493
21494         var strength;
21495         if (this.ClientSideStrongPassword(pwd)) {
21496             strength = 3;
21497         } else if (this.ClientSideMediumPassword(pwd)) {
21498             strength = 2;
21499         } else if (this.ClientSideWeakPassword(pwd)) {
21500             strength = 1;
21501         } else {
21502             strength = 0;
21503         }
21504         
21505         Roo.log('strength1: ' + strength);
21506         
21507         //var pm = this.trigger.child('div/div/div').dom;
21508         var pm = this.trigger.child('div/div');
21509         pm.removeClass(this.meterClass);
21510         pm.addClass(this.meterClass[strength]);
21511                 
21512         
21513         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21514                 
21515         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21516         
21517         this._lastPwd = pwd;
21518     },
21519     reset: function ()
21520     {
21521         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21522         
21523         this._lastPwd = '';
21524         
21525         var pm = this.trigger.child('div/div');
21526         pm.removeClass(this.meterClass);
21527         pm.addClass('roo-password-meter-grey');        
21528         
21529         
21530         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21531         
21532         pt.innerHTML = '';
21533         this.inputEl().dom.type='password';
21534     },
21535     // private
21536     validateValue: function (value)
21537     {
21538         
21539         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21540             return false;
21541         }
21542         if (value.length == 0) {
21543             if (this.allowBlank) {
21544                 this.clearInvalid();
21545                 return true;
21546             }
21547
21548             this.markInvalid(this.errors.PwdEmpty);
21549             this.errorMsg = this.errors.PwdEmpty;
21550             return false;
21551         }
21552         
21553         if(this.insecure){
21554             return true;
21555         }
21556         
21557         if ('[\x21-\x7e]*'.match(value)) {
21558             this.markInvalid(this.errors.PwdBadChar);
21559             this.errorMsg = this.errors.PwdBadChar;
21560             return false;
21561         }
21562         if (value.length < 6) {
21563             this.markInvalid(this.errors.PwdShort);
21564             this.errorMsg = this.errors.PwdShort;
21565             return false;
21566         }
21567         if (value.length > 16) {
21568             this.markInvalid(this.errors.PwdLong);
21569             this.errorMsg = this.errors.PwdLong;
21570             return false;
21571         }
21572         var strength;
21573         if (this.ClientSideStrongPassword(value)) {
21574             strength = 3;
21575         } else if (this.ClientSideMediumPassword(value)) {
21576             strength = 2;
21577         } else if (this.ClientSideWeakPassword(value)) {
21578             strength = 1;
21579         } else {
21580             strength = 0;
21581         }
21582
21583         
21584         if (strength < 2) {
21585             //this.markInvalid(this.errors.TooWeak);
21586             this.errorMsg = this.errors.TooWeak;
21587             //return false;
21588         }
21589         
21590         
21591         console.log('strength2: ' + strength);
21592         
21593         //var pm = this.trigger.child('div/div/div').dom;
21594         
21595         var pm = this.trigger.child('div/div');
21596         pm.removeClass(this.meterClass);
21597         pm.addClass(this.meterClass[strength]);
21598                 
21599         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21600                 
21601         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21602         
21603         this.errorMsg = ''; 
21604         return true;
21605     },
21606     // private
21607     CharacterSetChecks: function (type)
21608     {
21609         this.type = type;
21610         this.fResult = false;
21611     },
21612     // private
21613     isctype: function (character, type)
21614     {
21615         switch (type) {  
21616             case this.kCapitalLetter:
21617                 if (character >= 'A' && character <= 'Z') {
21618                     return true;
21619                 }
21620                 break;
21621             
21622             case this.kSmallLetter:
21623                 if (character >= 'a' && character <= 'z') {
21624                     return true;
21625                 }
21626                 break;
21627             
21628             case this.kDigit:
21629                 if (character >= '0' && character <= '9') {
21630                     return true;
21631                 }
21632                 break;
21633             
21634             case this.kPunctuation:
21635                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21636                     return true;
21637                 }
21638                 break;
21639             
21640             default:
21641                 return false;
21642         }
21643
21644     },
21645     // private
21646     IsLongEnough: function (pwd, size)
21647     {
21648         return !(pwd == null || isNaN(size) || pwd.length < size);
21649     },
21650     // private
21651     SpansEnoughCharacterSets: function (word, nb)
21652     {
21653         if (!this.IsLongEnough(word, nb))
21654         {
21655             return false;
21656         }
21657
21658         var characterSetChecks = new Array(
21659             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21660             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21661         );
21662         
21663         for (var index = 0; index < word.length; ++index) {
21664             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21665                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21666                     characterSetChecks[nCharSet].fResult = true;
21667                     break;
21668                 }
21669             }
21670         }
21671
21672         var nCharSets = 0;
21673         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21674             if (characterSetChecks[nCharSet].fResult) {
21675                 ++nCharSets;
21676             }
21677         }
21678
21679         if (nCharSets < nb) {
21680             return false;
21681         }
21682         return true;
21683     },
21684     // private
21685     ClientSideStrongPassword: function (pwd)
21686     {
21687         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21688     },
21689     // private
21690     ClientSideMediumPassword: function (pwd)
21691     {
21692         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21693     },
21694     // private
21695     ClientSideWeakPassword: function (pwd)
21696     {
21697         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21698     }
21699           
21700 })//<script type="text/javascript">
21701
21702 /*
21703  * Based  Ext JS Library 1.1.1
21704  * Copyright(c) 2006-2007, Ext JS, LLC.
21705  * LGPL
21706  *
21707  */
21708  
21709 /**
21710  * @class Roo.HtmlEditorCore
21711  * @extends Roo.Component
21712  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21713  *
21714  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21715  */
21716
21717 Roo.HtmlEditorCore = function(config){
21718     
21719     
21720     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21721     
21722     
21723     this.addEvents({
21724         /**
21725          * @event initialize
21726          * Fires when the editor is fully initialized (including the iframe)
21727          * @param {Roo.HtmlEditorCore} this
21728          */
21729         initialize: true,
21730         /**
21731          * @event activate
21732          * Fires when the editor is first receives the focus. Any insertion must wait
21733          * until after this event.
21734          * @param {Roo.HtmlEditorCore} this
21735          */
21736         activate: true,
21737          /**
21738          * @event beforesync
21739          * Fires before the textarea is updated with content from the editor iframe. Return false
21740          * to cancel the sync.
21741          * @param {Roo.HtmlEditorCore} this
21742          * @param {String} html
21743          */
21744         beforesync: true,
21745          /**
21746          * @event beforepush
21747          * Fires before the iframe editor is updated with content from the textarea. Return false
21748          * to cancel the push.
21749          * @param {Roo.HtmlEditorCore} this
21750          * @param {String} html
21751          */
21752         beforepush: true,
21753          /**
21754          * @event sync
21755          * Fires when the textarea is updated with content from the editor iframe.
21756          * @param {Roo.HtmlEditorCore} this
21757          * @param {String} html
21758          */
21759         sync: true,
21760          /**
21761          * @event push
21762          * Fires when the iframe editor is updated with content from the textarea.
21763          * @param {Roo.HtmlEditorCore} this
21764          * @param {String} html
21765          */
21766         push: true,
21767         
21768         /**
21769          * @event editorevent
21770          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21771          * @param {Roo.HtmlEditorCore} this
21772          */
21773         editorevent: true
21774         
21775     });
21776     
21777     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21778     
21779     // defaults : white / black...
21780     this.applyBlacklists();
21781     
21782     
21783     
21784 };
21785
21786
21787 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21788
21789
21790      /**
21791      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21792      */
21793     
21794     owner : false,
21795     
21796      /**
21797      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21798      *                        Roo.resizable.
21799      */
21800     resizable : false,
21801      /**
21802      * @cfg {Number} height (in pixels)
21803      */   
21804     height: 300,
21805    /**
21806      * @cfg {Number} width (in pixels)
21807      */   
21808     width: 500,
21809     
21810     /**
21811      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21812      * 
21813      */
21814     stylesheets: false,
21815     
21816     // id of frame..
21817     frameId: false,
21818     
21819     // private properties
21820     validationEvent : false,
21821     deferHeight: true,
21822     initialized : false,
21823     activated : false,
21824     sourceEditMode : false,
21825     onFocus : Roo.emptyFn,
21826     iframePad:3,
21827     hideMode:'offsets',
21828     
21829     clearUp: true,
21830     
21831     // blacklist + whitelisted elements..
21832     black: false,
21833     white: false,
21834      
21835     bodyCls : '',
21836
21837     /**
21838      * Protected method that will not generally be called directly. It
21839      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21840      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21841      */
21842     getDocMarkup : function(){
21843         // body styles..
21844         var st = '';
21845         
21846         // inherit styels from page...?? 
21847         if (this.stylesheets === false) {
21848             
21849             Roo.get(document.head).select('style').each(function(node) {
21850                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21851             });
21852             
21853             Roo.get(document.head).select('link').each(function(node) { 
21854                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21855             });
21856             
21857         } else if (!this.stylesheets.length) {
21858                 // simple..
21859                 st = '<style type="text/css">' +
21860                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21861                    '</style>';
21862         } else { 
21863             st = '<style type="text/css">' +
21864                     this.stylesheets +
21865                 '</style>';
21866         }
21867         
21868         st +=  '<style type="text/css">' +
21869             'IMG { cursor: pointer } ' +
21870         '</style>';
21871
21872         var cls = 'roo-htmleditor-body';
21873         
21874         if(this.bodyCls.length){
21875             cls += ' ' + this.bodyCls;
21876         }
21877         
21878         return '<html><head>' + st  +
21879             //<style type="text/css">' +
21880             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21881             //'</style>' +
21882             ' </head><body class="' +  cls + '"></body></html>';
21883     },
21884
21885     // private
21886     onRender : function(ct, position)
21887     {
21888         var _t = this;
21889         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21890         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21891         
21892         
21893         this.el.dom.style.border = '0 none';
21894         this.el.dom.setAttribute('tabIndex', -1);
21895         this.el.addClass('x-hidden hide');
21896         
21897         
21898         
21899         if(Roo.isIE){ // fix IE 1px bogus margin
21900             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21901         }
21902        
21903         
21904         this.frameId = Roo.id();
21905         
21906          
21907         
21908         var iframe = this.owner.wrap.createChild({
21909             tag: 'iframe',
21910             cls: 'form-control', // bootstrap..
21911             id: this.frameId,
21912             name: this.frameId,
21913             frameBorder : 'no',
21914             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21915         }, this.el
21916         );
21917         
21918         
21919         this.iframe = iframe.dom;
21920
21921          this.assignDocWin();
21922         
21923         this.doc.designMode = 'on';
21924        
21925         this.doc.open();
21926         this.doc.write(this.getDocMarkup());
21927         this.doc.close();
21928
21929         
21930         var task = { // must defer to wait for browser to be ready
21931             run : function(){
21932                 //console.log("run task?" + this.doc.readyState);
21933                 this.assignDocWin();
21934                 if(this.doc.body || this.doc.readyState == 'complete'){
21935                     try {
21936                         this.doc.designMode="on";
21937                     } catch (e) {
21938                         return;
21939                     }
21940                     Roo.TaskMgr.stop(task);
21941                     this.initEditor.defer(10, this);
21942                 }
21943             },
21944             interval : 10,
21945             duration: 10000,
21946             scope: this
21947         };
21948         Roo.TaskMgr.start(task);
21949
21950     },
21951
21952     // private
21953     onResize : function(w, h)
21954     {
21955          Roo.log('resize: ' +w + ',' + h );
21956         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21957         if(!this.iframe){
21958             return;
21959         }
21960         if(typeof w == 'number'){
21961             
21962             this.iframe.style.width = w + 'px';
21963         }
21964         if(typeof h == 'number'){
21965             
21966             this.iframe.style.height = h + 'px';
21967             if(this.doc){
21968                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21969             }
21970         }
21971         
21972     },
21973
21974     /**
21975      * Toggles the editor between standard and source edit mode.
21976      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21977      */
21978     toggleSourceEdit : function(sourceEditMode){
21979         
21980         this.sourceEditMode = sourceEditMode === true;
21981         
21982         if(this.sourceEditMode){
21983  
21984             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21985             
21986         }else{
21987             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21988             //this.iframe.className = '';
21989             this.deferFocus();
21990         }
21991         //this.setSize(this.owner.wrap.getSize());
21992         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21993     },
21994
21995     
21996   
21997
21998     /**
21999      * Protected method that will not generally be called directly. If you need/want
22000      * custom HTML cleanup, this is the method you should override.
22001      * @param {String} html The HTML to be cleaned
22002      * return {String} The cleaned HTML
22003      */
22004     cleanHtml : function(html){
22005         html = String(html);
22006         if(html.length > 5){
22007             if(Roo.isSafari){ // strip safari nonsense
22008                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22009             }
22010         }
22011         if(html == '&nbsp;'){
22012             html = '';
22013         }
22014         return html;
22015     },
22016
22017     /**
22018      * HTML Editor -> Textarea
22019      * Protected method that will not generally be called directly. Syncs the contents
22020      * of the editor iframe with the textarea.
22021      */
22022     syncValue : function(){
22023         if(this.initialized){
22024             var bd = (this.doc.body || this.doc.documentElement);
22025             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22026             var html = bd.innerHTML;
22027             if(Roo.isSafari){
22028                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22029                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22030                 if(m && m[1]){
22031                     html = '<div style="'+m[0]+'">' + html + '</div>';
22032                 }
22033             }
22034             html = this.cleanHtml(html);
22035             // fix up the special chars.. normaly like back quotes in word...
22036             // however we do not want to do this with chinese..
22037             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22038                 var cc = b.charCodeAt();
22039                 if (
22040                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22041                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22042                     (cc >= 0xf900 && cc < 0xfb00 )
22043                 ) {
22044                         return b;
22045                 }
22046                 return "&#"+cc+";" 
22047             });
22048             if(this.owner.fireEvent('beforesync', this, html) !== false){
22049                 this.el.dom.value = html;
22050                 this.owner.fireEvent('sync', this, html);
22051             }
22052         }
22053     },
22054
22055     /**
22056      * Protected method that will not generally be called directly. Pushes the value of the textarea
22057      * into the iframe editor.
22058      */
22059     pushValue : function(){
22060         if(this.initialized){
22061             var v = this.el.dom.value.trim();
22062             
22063 //            if(v.length < 1){
22064 //                v = '&#160;';
22065 //            }
22066             
22067             if(this.owner.fireEvent('beforepush', this, v) !== false){
22068                 var d = (this.doc.body || this.doc.documentElement);
22069                 d.innerHTML = v;
22070                 this.cleanUpPaste();
22071                 this.el.dom.value = d.innerHTML;
22072                 this.owner.fireEvent('push', this, v);
22073             }
22074         }
22075     },
22076
22077     // private
22078     deferFocus : function(){
22079         this.focus.defer(10, this);
22080     },
22081
22082     // doc'ed in Field
22083     focus : function(){
22084         if(this.win && !this.sourceEditMode){
22085             this.win.focus();
22086         }else{
22087             this.el.focus();
22088         }
22089     },
22090     
22091     assignDocWin: function()
22092     {
22093         var iframe = this.iframe;
22094         
22095          if(Roo.isIE){
22096             this.doc = iframe.contentWindow.document;
22097             this.win = iframe.contentWindow;
22098         } else {
22099 //            if (!Roo.get(this.frameId)) {
22100 //                return;
22101 //            }
22102 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22103 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22104             
22105             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22106                 return;
22107             }
22108             
22109             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22110             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22111         }
22112     },
22113     
22114     // private
22115     initEditor : function(){
22116         //console.log("INIT EDITOR");
22117         this.assignDocWin();
22118         
22119         
22120         
22121         this.doc.designMode="on";
22122         this.doc.open();
22123         this.doc.write(this.getDocMarkup());
22124         this.doc.close();
22125         
22126         var dbody = (this.doc.body || this.doc.documentElement);
22127         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22128         // this copies styles from the containing element into thsi one..
22129         // not sure why we need all of this..
22130         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22131         
22132         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22133         //ss['background-attachment'] = 'fixed'; // w3c
22134         dbody.bgProperties = 'fixed'; // ie
22135         //Roo.DomHelper.applyStyles(dbody, ss);
22136         Roo.EventManager.on(this.doc, {
22137             //'mousedown': this.onEditorEvent,
22138             'mouseup': this.onEditorEvent,
22139             'dblclick': this.onEditorEvent,
22140             'click': this.onEditorEvent,
22141             'keyup': this.onEditorEvent,
22142             buffer:100,
22143             scope: this
22144         });
22145         if(Roo.isGecko){
22146             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22147         }
22148         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22149             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22150         }
22151         this.initialized = true;
22152
22153         this.owner.fireEvent('initialize', this);
22154         this.pushValue();
22155     },
22156
22157     // private
22158     onDestroy : function(){
22159         
22160         
22161         
22162         if(this.rendered){
22163             
22164             //for (var i =0; i < this.toolbars.length;i++) {
22165             //    // fixme - ask toolbars for heights?
22166             //    this.toolbars[i].onDestroy();
22167            // }
22168             
22169             //this.wrap.dom.innerHTML = '';
22170             //this.wrap.remove();
22171         }
22172     },
22173
22174     // private
22175     onFirstFocus : function(){
22176         
22177         this.assignDocWin();
22178         
22179         
22180         this.activated = true;
22181          
22182     
22183         if(Roo.isGecko){ // prevent silly gecko errors
22184             this.win.focus();
22185             var s = this.win.getSelection();
22186             if(!s.focusNode || s.focusNode.nodeType != 3){
22187                 var r = s.getRangeAt(0);
22188                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22189                 r.collapse(true);
22190                 this.deferFocus();
22191             }
22192             try{
22193                 this.execCmd('useCSS', true);
22194                 this.execCmd('styleWithCSS', false);
22195             }catch(e){}
22196         }
22197         this.owner.fireEvent('activate', this);
22198     },
22199
22200     // private
22201     adjustFont: function(btn){
22202         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22203         //if(Roo.isSafari){ // safari
22204         //    adjust *= 2;
22205        // }
22206         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22207         if(Roo.isSafari){ // safari
22208             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22209             v =  (v < 10) ? 10 : v;
22210             v =  (v > 48) ? 48 : v;
22211             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22212             
22213         }
22214         
22215         
22216         v = Math.max(1, v+adjust);
22217         
22218         this.execCmd('FontSize', v  );
22219     },
22220
22221     onEditorEvent : function(e)
22222     {
22223         this.owner.fireEvent('editorevent', this, e);
22224       //  this.updateToolbar();
22225         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22226     },
22227
22228     insertTag : function(tg)
22229     {
22230         // could be a bit smarter... -> wrap the current selected tRoo..
22231         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22232             
22233             range = this.createRange(this.getSelection());
22234             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22235             wrappingNode.appendChild(range.extractContents());
22236             range.insertNode(wrappingNode);
22237
22238             return;
22239             
22240             
22241             
22242         }
22243         this.execCmd("formatblock",   tg);
22244         
22245     },
22246     
22247     insertText : function(txt)
22248     {
22249         
22250         
22251         var range = this.createRange();
22252         range.deleteContents();
22253                //alert(Sender.getAttribute('label'));
22254                
22255         range.insertNode(this.doc.createTextNode(txt));
22256     } ,
22257     
22258      
22259
22260     /**
22261      * Executes a Midas editor command on the editor document and performs necessary focus and
22262      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22263      * @param {String} cmd The Midas command
22264      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22265      */
22266     relayCmd : function(cmd, value){
22267         this.win.focus();
22268         this.execCmd(cmd, value);
22269         this.owner.fireEvent('editorevent', this);
22270         //this.updateToolbar();
22271         this.owner.deferFocus();
22272     },
22273
22274     /**
22275      * Executes a Midas editor command directly on the editor document.
22276      * For visual commands, you should use {@link #relayCmd} instead.
22277      * <b>This should only be called after the editor is initialized.</b>
22278      * @param {String} cmd The Midas command
22279      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22280      */
22281     execCmd : function(cmd, value){
22282         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22283         this.syncValue();
22284     },
22285  
22286  
22287    
22288     /**
22289      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22290      * to insert tRoo.
22291      * @param {String} text | dom node.. 
22292      */
22293     insertAtCursor : function(text)
22294     {
22295         
22296         if(!this.activated){
22297             return;
22298         }
22299         /*
22300         if(Roo.isIE){
22301             this.win.focus();
22302             var r = this.doc.selection.createRange();
22303             if(r){
22304                 r.collapse(true);
22305                 r.pasteHTML(text);
22306                 this.syncValue();
22307                 this.deferFocus();
22308             
22309             }
22310             return;
22311         }
22312         */
22313         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22314             this.win.focus();
22315             
22316             
22317             // from jquery ui (MIT licenced)
22318             var range, node;
22319             var win = this.win;
22320             
22321             if (win.getSelection && win.getSelection().getRangeAt) {
22322                 range = win.getSelection().getRangeAt(0);
22323                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22324                 range.insertNode(node);
22325             } else if (win.document.selection && win.document.selection.createRange) {
22326                 // no firefox support
22327                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22328                 win.document.selection.createRange().pasteHTML(txt);
22329             } else {
22330                 // no firefox support
22331                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22332                 this.execCmd('InsertHTML', txt);
22333             } 
22334             
22335             this.syncValue();
22336             
22337             this.deferFocus();
22338         }
22339     },
22340  // private
22341     mozKeyPress : function(e){
22342         if(e.ctrlKey){
22343             var c = e.getCharCode(), cmd;
22344           
22345             if(c > 0){
22346                 c = String.fromCharCode(c).toLowerCase();
22347                 switch(c){
22348                     case 'b':
22349                         cmd = 'bold';
22350                         break;
22351                     case 'i':
22352                         cmd = 'italic';
22353                         break;
22354                     
22355                     case 'u':
22356                         cmd = 'underline';
22357                         break;
22358                     
22359                     case 'v':
22360                         this.cleanUpPaste.defer(100, this);
22361                         return;
22362                         
22363                 }
22364                 if(cmd){
22365                     this.win.focus();
22366                     this.execCmd(cmd);
22367                     this.deferFocus();
22368                     e.preventDefault();
22369                 }
22370                 
22371             }
22372         }
22373     },
22374
22375     // private
22376     fixKeys : function(){ // load time branching for fastest keydown performance
22377         if(Roo.isIE){
22378             return function(e){
22379                 var k = e.getKey(), r;
22380                 if(k == e.TAB){
22381                     e.stopEvent();
22382                     r = this.doc.selection.createRange();
22383                     if(r){
22384                         r.collapse(true);
22385                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22386                         this.deferFocus();
22387                     }
22388                     return;
22389                 }
22390                 
22391                 if(k == e.ENTER){
22392                     r = this.doc.selection.createRange();
22393                     if(r){
22394                         var target = r.parentElement();
22395                         if(!target || target.tagName.toLowerCase() != 'li'){
22396                             e.stopEvent();
22397                             r.pasteHTML('<br />');
22398                             r.collapse(false);
22399                             r.select();
22400                         }
22401                     }
22402                 }
22403                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22404                     this.cleanUpPaste.defer(100, this);
22405                     return;
22406                 }
22407                 
22408                 
22409             };
22410         }else if(Roo.isOpera){
22411             return function(e){
22412                 var k = e.getKey();
22413                 if(k == e.TAB){
22414                     e.stopEvent();
22415                     this.win.focus();
22416                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22417                     this.deferFocus();
22418                 }
22419                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22420                     this.cleanUpPaste.defer(100, this);
22421                     return;
22422                 }
22423                 
22424             };
22425         }else if(Roo.isSafari){
22426             return function(e){
22427                 var k = e.getKey();
22428                 
22429                 if(k == e.TAB){
22430                     e.stopEvent();
22431                     this.execCmd('InsertText','\t');
22432                     this.deferFocus();
22433                     return;
22434                 }
22435                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22436                     this.cleanUpPaste.defer(100, this);
22437                     return;
22438                 }
22439                 
22440              };
22441         }
22442     }(),
22443     
22444     getAllAncestors: function()
22445     {
22446         var p = this.getSelectedNode();
22447         var a = [];
22448         if (!p) {
22449             a.push(p); // push blank onto stack..
22450             p = this.getParentElement();
22451         }
22452         
22453         
22454         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22455             a.push(p);
22456             p = p.parentNode;
22457         }
22458         a.push(this.doc.body);
22459         return a;
22460     },
22461     lastSel : false,
22462     lastSelNode : false,
22463     
22464     
22465     getSelection : function() 
22466     {
22467         this.assignDocWin();
22468         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22469     },
22470     
22471     getSelectedNode: function() 
22472     {
22473         // this may only work on Gecko!!!
22474         
22475         // should we cache this!!!!
22476         
22477         
22478         
22479          
22480         var range = this.createRange(this.getSelection()).cloneRange();
22481         
22482         if (Roo.isIE) {
22483             var parent = range.parentElement();
22484             while (true) {
22485                 var testRange = range.duplicate();
22486                 testRange.moveToElementText(parent);
22487                 if (testRange.inRange(range)) {
22488                     break;
22489                 }
22490                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22491                     break;
22492                 }
22493                 parent = parent.parentElement;
22494             }
22495             return parent;
22496         }
22497         
22498         // is ancestor a text element.
22499         var ac =  range.commonAncestorContainer;
22500         if (ac.nodeType == 3) {
22501             ac = ac.parentNode;
22502         }
22503         
22504         var ar = ac.childNodes;
22505          
22506         var nodes = [];
22507         var other_nodes = [];
22508         var has_other_nodes = false;
22509         for (var i=0;i<ar.length;i++) {
22510             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22511                 continue;
22512             }
22513             // fullly contained node.
22514             
22515             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22516                 nodes.push(ar[i]);
22517                 continue;
22518             }
22519             
22520             // probably selected..
22521             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22522                 other_nodes.push(ar[i]);
22523                 continue;
22524             }
22525             // outer..
22526             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22527                 continue;
22528             }
22529             
22530             
22531             has_other_nodes = true;
22532         }
22533         if (!nodes.length && other_nodes.length) {
22534             nodes= other_nodes;
22535         }
22536         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22537             return false;
22538         }
22539         
22540         return nodes[0];
22541     },
22542     createRange: function(sel)
22543     {
22544         // this has strange effects when using with 
22545         // top toolbar - not sure if it's a great idea.
22546         //this.editor.contentWindow.focus();
22547         if (typeof sel != "undefined") {
22548             try {
22549                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22550             } catch(e) {
22551                 return this.doc.createRange();
22552             }
22553         } else {
22554             return this.doc.createRange();
22555         }
22556     },
22557     getParentElement: function()
22558     {
22559         
22560         this.assignDocWin();
22561         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22562         
22563         var range = this.createRange(sel);
22564          
22565         try {
22566             var p = range.commonAncestorContainer;
22567             while (p.nodeType == 3) { // text node
22568                 p = p.parentNode;
22569             }
22570             return p;
22571         } catch (e) {
22572             return null;
22573         }
22574     
22575     },
22576     /***
22577      *
22578      * Range intersection.. the hard stuff...
22579      *  '-1' = before
22580      *  '0' = hits..
22581      *  '1' = after.
22582      *         [ -- selected range --- ]
22583      *   [fail]                        [fail]
22584      *
22585      *    basically..
22586      *      if end is before start or  hits it. fail.
22587      *      if start is after end or hits it fail.
22588      *
22589      *   if either hits (but other is outside. - then it's not 
22590      *   
22591      *    
22592      **/
22593     
22594     
22595     // @see http://www.thismuchiknow.co.uk/?p=64.
22596     rangeIntersectsNode : function(range, node)
22597     {
22598         var nodeRange = node.ownerDocument.createRange();
22599         try {
22600             nodeRange.selectNode(node);
22601         } catch (e) {
22602             nodeRange.selectNodeContents(node);
22603         }
22604     
22605         var rangeStartRange = range.cloneRange();
22606         rangeStartRange.collapse(true);
22607     
22608         var rangeEndRange = range.cloneRange();
22609         rangeEndRange.collapse(false);
22610     
22611         var nodeStartRange = nodeRange.cloneRange();
22612         nodeStartRange.collapse(true);
22613     
22614         var nodeEndRange = nodeRange.cloneRange();
22615         nodeEndRange.collapse(false);
22616     
22617         return rangeStartRange.compareBoundaryPoints(
22618                  Range.START_TO_START, nodeEndRange) == -1 &&
22619                rangeEndRange.compareBoundaryPoints(
22620                  Range.START_TO_START, nodeStartRange) == 1;
22621         
22622          
22623     },
22624     rangeCompareNode : function(range, node)
22625     {
22626         var nodeRange = node.ownerDocument.createRange();
22627         try {
22628             nodeRange.selectNode(node);
22629         } catch (e) {
22630             nodeRange.selectNodeContents(node);
22631         }
22632         
22633         
22634         range.collapse(true);
22635     
22636         nodeRange.collapse(true);
22637      
22638         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22639         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22640          
22641         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22642         
22643         var nodeIsBefore   =  ss == 1;
22644         var nodeIsAfter    = ee == -1;
22645         
22646         if (nodeIsBefore && nodeIsAfter) {
22647             return 0; // outer
22648         }
22649         if (!nodeIsBefore && nodeIsAfter) {
22650             return 1; //right trailed.
22651         }
22652         
22653         if (nodeIsBefore && !nodeIsAfter) {
22654             return 2;  // left trailed.
22655         }
22656         // fully contined.
22657         return 3;
22658     },
22659
22660     // private? - in a new class?
22661     cleanUpPaste :  function()
22662     {
22663         // cleans up the whole document..
22664         Roo.log('cleanuppaste');
22665         
22666         this.cleanUpChildren(this.doc.body);
22667         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22668         if (clean != this.doc.body.innerHTML) {
22669             this.doc.body.innerHTML = clean;
22670         }
22671         
22672     },
22673     
22674     cleanWordChars : function(input) {// change the chars to hex code
22675         var he = Roo.HtmlEditorCore;
22676         
22677         var output = input;
22678         Roo.each(he.swapCodes, function(sw) { 
22679             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22680             
22681             output = output.replace(swapper, sw[1]);
22682         });
22683         
22684         return output;
22685     },
22686     
22687     
22688     cleanUpChildren : function (n)
22689     {
22690         if (!n.childNodes.length) {
22691             return;
22692         }
22693         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22694            this.cleanUpChild(n.childNodes[i]);
22695         }
22696     },
22697     
22698     
22699         
22700     
22701     cleanUpChild : function (node)
22702     {
22703         var ed = this;
22704         //console.log(node);
22705         if (node.nodeName == "#text") {
22706             // clean up silly Windows -- stuff?
22707             return; 
22708         }
22709         if (node.nodeName == "#comment") {
22710             node.parentNode.removeChild(node);
22711             // clean up silly Windows -- stuff?
22712             return; 
22713         }
22714         var lcname = node.tagName.toLowerCase();
22715         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22716         // whitelist of tags..
22717         
22718         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22719             // remove node.
22720             node.parentNode.removeChild(node);
22721             return;
22722             
22723         }
22724         
22725         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22726         
22727         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22728         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22729         
22730         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22731         //    remove_keep_children = true;
22732         //}
22733         
22734         if (remove_keep_children) {
22735             this.cleanUpChildren(node);
22736             // inserts everything just before this node...
22737             while (node.childNodes.length) {
22738                 var cn = node.childNodes[0];
22739                 node.removeChild(cn);
22740                 node.parentNode.insertBefore(cn, node);
22741             }
22742             node.parentNode.removeChild(node);
22743             return;
22744         }
22745         
22746         if (!node.attributes || !node.attributes.length) {
22747             this.cleanUpChildren(node);
22748             return;
22749         }
22750         
22751         function cleanAttr(n,v)
22752         {
22753             
22754             if (v.match(/^\./) || v.match(/^\//)) {
22755                 return;
22756             }
22757             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22758                 return;
22759             }
22760             if (v.match(/^#/)) {
22761                 return;
22762             }
22763 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22764             node.removeAttribute(n);
22765             
22766         }
22767         
22768         var cwhite = this.cwhite;
22769         var cblack = this.cblack;
22770             
22771         function cleanStyle(n,v)
22772         {
22773             if (v.match(/expression/)) { //XSS?? should we even bother..
22774                 node.removeAttribute(n);
22775                 return;
22776             }
22777             
22778             var parts = v.split(/;/);
22779             var clean = [];
22780             
22781             Roo.each(parts, function(p) {
22782                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22783                 if (!p.length) {
22784                     return true;
22785                 }
22786                 var l = p.split(':').shift().replace(/\s+/g,'');
22787                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22788                 
22789                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22790 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22791                     //node.removeAttribute(n);
22792                     return true;
22793                 }
22794                 //Roo.log()
22795                 // only allow 'c whitelisted system attributes'
22796                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22797 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22798                     //node.removeAttribute(n);
22799                     return true;
22800                 }
22801                 
22802                 
22803                  
22804                 
22805                 clean.push(p);
22806                 return true;
22807             });
22808             if (clean.length) { 
22809                 node.setAttribute(n, clean.join(';'));
22810             } else {
22811                 node.removeAttribute(n);
22812             }
22813             
22814         }
22815         
22816         
22817         for (var i = node.attributes.length-1; i > -1 ; i--) {
22818             var a = node.attributes[i];
22819             //console.log(a);
22820             
22821             if (a.name.toLowerCase().substr(0,2)=='on')  {
22822                 node.removeAttribute(a.name);
22823                 continue;
22824             }
22825             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22826                 node.removeAttribute(a.name);
22827                 continue;
22828             }
22829             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22830                 cleanAttr(a.name,a.value); // fixme..
22831                 continue;
22832             }
22833             if (a.name == 'style') {
22834                 cleanStyle(a.name,a.value);
22835                 continue;
22836             }
22837             /// clean up MS crap..
22838             // tecnically this should be a list of valid class'es..
22839             
22840             
22841             if (a.name == 'class') {
22842                 if (a.value.match(/^Mso/)) {
22843                     node.className = '';
22844                 }
22845                 
22846                 if (a.value.match(/^body$/)) {
22847                     node.className = '';
22848                 }
22849                 continue;
22850             }
22851             
22852             // style cleanup!?
22853             // class cleanup?
22854             
22855         }
22856         
22857         
22858         this.cleanUpChildren(node);
22859         
22860         
22861     },
22862     
22863     /**
22864      * Clean up MS wordisms...
22865      */
22866     cleanWord : function(node)
22867     {
22868         
22869         
22870         if (!node) {
22871             this.cleanWord(this.doc.body);
22872             return;
22873         }
22874         if (node.nodeName == "#text") {
22875             // clean up silly Windows -- stuff?
22876             return; 
22877         }
22878         if (node.nodeName == "#comment") {
22879             node.parentNode.removeChild(node);
22880             // clean up silly Windows -- stuff?
22881             return; 
22882         }
22883         
22884         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22885             node.parentNode.removeChild(node);
22886             return;
22887         }
22888         
22889         // remove - but keep children..
22890         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22891             while (node.childNodes.length) {
22892                 var cn = node.childNodes[0];
22893                 node.removeChild(cn);
22894                 node.parentNode.insertBefore(cn, node);
22895             }
22896             node.parentNode.removeChild(node);
22897             this.iterateChildren(node, this.cleanWord);
22898             return;
22899         }
22900         // clean styles
22901         if (node.className.length) {
22902             
22903             var cn = node.className.split(/\W+/);
22904             var cna = [];
22905             Roo.each(cn, function(cls) {
22906                 if (cls.match(/Mso[a-zA-Z]+/)) {
22907                     return;
22908                 }
22909                 cna.push(cls);
22910             });
22911             node.className = cna.length ? cna.join(' ') : '';
22912             if (!cna.length) {
22913                 node.removeAttribute("class");
22914             }
22915         }
22916         
22917         if (node.hasAttribute("lang")) {
22918             node.removeAttribute("lang");
22919         }
22920         
22921         if (node.hasAttribute("style")) {
22922             
22923             var styles = node.getAttribute("style").split(";");
22924             var nstyle = [];
22925             Roo.each(styles, function(s) {
22926                 if (!s.match(/:/)) {
22927                     return;
22928                 }
22929                 var kv = s.split(":");
22930                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22931                     return;
22932                 }
22933                 // what ever is left... we allow.
22934                 nstyle.push(s);
22935             });
22936             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22937             if (!nstyle.length) {
22938                 node.removeAttribute('style');
22939             }
22940         }
22941         this.iterateChildren(node, this.cleanWord);
22942         
22943         
22944         
22945     },
22946     /**
22947      * iterateChildren of a Node, calling fn each time, using this as the scole..
22948      * @param {DomNode} node node to iterate children of.
22949      * @param {Function} fn method of this class to call on each item.
22950      */
22951     iterateChildren : function(node, fn)
22952     {
22953         if (!node.childNodes.length) {
22954                 return;
22955         }
22956         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22957            fn.call(this, node.childNodes[i])
22958         }
22959     },
22960     
22961     
22962     /**
22963      * cleanTableWidths.
22964      *
22965      * Quite often pasting from word etc.. results in tables with column and widths.
22966      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22967      *
22968      */
22969     cleanTableWidths : function(node)
22970     {
22971          
22972          
22973         if (!node) {
22974             this.cleanTableWidths(this.doc.body);
22975             return;
22976         }
22977         
22978         // ignore list...
22979         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22980             return; 
22981         }
22982         Roo.log(node.tagName);
22983         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22984             this.iterateChildren(node, this.cleanTableWidths);
22985             return;
22986         }
22987         if (node.hasAttribute('width')) {
22988             node.removeAttribute('width');
22989         }
22990         
22991          
22992         if (node.hasAttribute("style")) {
22993             // pretty basic...
22994             
22995             var styles = node.getAttribute("style").split(";");
22996             var nstyle = [];
22997             Roo.each(styles, function(s) {
22998                 if (!s.match(/:/)) {
22999                     return;
23000                 }
23001                 var kv = s.split(":");
23002                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23003                     return;
23004                 }
23005                 // what ever is left... we allow.
23006                 nstyle.push(s);
23007             });
23008             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23009             if (!nstyle.length) {
23010                 node.removeAttribute('style');
23011             }
23012         }
23013         
23014         this.iterateChildren(node, this.cleanTableWidths);
23015         
23016         
23017     },
23018     
23019     
23020     
23021     
23022     domToHTML : function(currentElement, depth, nopadtext) {
23023         
23024         depth = depth || 0;
23025         nopadtext = nopadtext || false;
23026     
23027         if (!currentElement) {
23028             return this.domToHTML(this.doc.body);
23029         }
23030         
23031         //Roo.log(currentElement);
23032         var j;
23033         var allText = false;
23034         var nodeName = currentElement.nodeName;
23035         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23036         
23037         if  (nodeName == '#text') {
23038             
23039             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23040         }
23041         
23042         
23043         var ret = '';
23044         if (nodeName != 'BODY') {
23045              
23046             var i = 0;
23047             // Prints the node tagName, such as <A>, <IMG>, etc
23048             if (tagName) {
23049                 var attr = [];
23050                 for(i = 0; i < currentElement.attributes.length;i++) {
23051                     // quoting?
23052                     var aname = currentElement.attributes.item(i).name;
23053                     if (!currentElement.attributes.item(i).value.length) {
23054                         continue;
23055                     }
23056                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23057                 }
23058                 
23059                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23060             } 
23061             else {
23062                 
23063                 // eack
23064             }
23065         } else {
23066             tagName = false;
23067         }
23068         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23069             return ret;
23070         }
23071         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23072             nopadtext = true;
23073         }
23074         
23075         
23076         // Traverse the tree
23077         i = 0;
23078         var currentElementChild = currentElement.childNodes.item(i);
23079         var allText = true;
23080         var innerHTML  = '';
23081         lastnode = '';
23082         while (currentElementChild) {
23083             // Formatting code (indent the tree so it looks nice on the screen)
23084             var nopad = nopadtext;
23085             if (lastnode == 'SPAN') {
23086                 nopad  = true;
23087             }
23088             // text
23089             if  (currentElementChild.nodeName == '#text') {
23090                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23091                 toadd = nopadtext ? toadd : toadd.trim();
23092                 if (!nopad && toadd.length > 80) {
23093                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23094                 }
23095                 innerHTML  += toadd;
23096                 
23097                 i++;
23098                 currentElementChild = currentElement.childNodes.item(i);
23099                 lastNode = '';
23100                 continue;
23101             }
23102             allText = false;
23103             
23104             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23105                 
23106             // Recursively traverse the tree structure of the child node
23107             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23108             lastnode = currentElementChild.nodeName;
23109             i++;
23110             currentElementChild=currentElement.childNodes.item(i);
23111         }
23112         
23113         ret += innerHTML;
23114         
23115         if (!allText) {
23116                 // The remaining code is mostly for formatting the tree
23117             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23118         }
23119         
23120         
23121         if (tagName) {
23122             ret+= "</"+tagName+">";
23123         }
23124         return ret;
23125         
23126     },
23127         
23128     applyBlacklists : function()
23129     {
23130         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23131         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23132         
23133         this.white = [];
23134         this.black = [];
23135         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23136             if (b.indexOf(tag) > -1) {
23137                 return;
23138             }
23139             this.white.push(tag);
23140             
23141         }, this);
23142         
23143         Roo.each(w, function(tag) {
23144             if (b.indexOf(tag) > -1) {
23145                 return;
23146             }
23147             if (this.white.indexOf(tag) > -1) {
23148                 return;
23149             }
23150             this.white.push(tag);
23151             
23152         }, this);
23153         
23154         
23155         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23156             if (w.indexOf(tag) > -1) {
23157                 return;
23158             }
23159             this.black.push(tag);
23160             
23161         }, this);
23162         
23163         Roo.each(b, function(tag) {
23164             if (w.indexOf(tag) > -1) {
23165                 return;
23166             }
23167             if (this.black.indexOf(tag) > -1) {
23168                 return;
23169             }
23170             this.black.push(tag);
23171             
23172         }, this);
23173         
23174         
23175         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23176         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23177         
23178         this.cwhite = [];
23179         this.cblack = [];
23180         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23181             if (b.indexOf(tag) > -1) {
23182                 return;
23183             }
23184             this.cwhite.push(tag);
23185             
23186         }, this);
23187         
23188         Roo.each(w, function(tag) {
23189             if (b.indexOf(tag) > -1) {
23190                 return;
23191             }
23192             if (this.cwhite.indexOf(tag) > -1) {
23193                 return;
23194             }
23195             this.cwhite.push(tag);
23196             
23197         }, this);
23198         
23199         
23200         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23201             if (w.indexOf(tag) > -1) {
23202                 return;
23203             }
23204             this.cblack.push(tag);
23205             
23206         }, this);
23207         
23208         Roo.each(b, function(tag) {
23209             if (w.indexOf(tag) > -1) {
23210                 return;
23211             }
23212             if (this.cblack.indexOf(tag) > -1) {
23213                 return;
23214             }
23215             this.cblack.push(tag);
23216             
23217         }, this);
23218     },
23219     
23220     setStylesheets : function(stylesheets)
23221     {
23222         if(typeof(stylesheets) == 'string'){
23223             Roo.get(this.iframe.contentDocument.head).createChild({
23224                 tag : 'link',
23225                 rel : 'stylesheet',
23226                 type : 'text/css',
23227                 href : stylesheets
23228             });
23229             
23230             return;
23231         }
23232         var _this = this;
23233      
23234         Roo.each(stylesheets, function(s) {
23235             if(!s.length){
23236                 return;
23237             }
23238             
23239             Roo.get(_this.iframe.contentDocument.head).createChild({
23240                 tag : 'link',
23241                 rel : 'stylesheet',
23242                 type : 'text/css',
23243                 href : s
23244             });
23245         });
23246
23247         
23248     },
23249     
23250     removeStylesheets : function()
23251     {
23252         var _this = this;
23253         
23254         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23255             s.remove();
23256         });
23257     },
23258     
23259     setStyle : function(style)
23260     {
23261         Roo.get(this.iframe.contentDocument.head).createChild({
23262             tag : 'style',
23263             type : 'text/css',
23264             html : style
23265         });
23266
23267         return;
23268     }
23269     
23270     // hide stuff that is not compatible
23271     /**
23272      * @event blur
23273      * @hide
23274      */
23275     /**
23276      * @event change
23277      * @hide
23278      */
23279     /**
23280      * @event focus
23281      * @hide
23282      */
23283     /**
23284      * @event specialkey
23285      * @hide
23286      */
23287     /**
23288      * @cfg {String} fieldClass @hide
23289      */
23290     /**
23291      * @cfg {String} focusClass @hide
23292      */
23293     /**
23294      * @cfg {String} autoCreate @hide
23295      */
23296     /**
23297      * @cfg {String} inputType @hide
23298      */
23299     /**
23300      * @cfg {String} invalidClass @hide
23301      */
23302     /**
23303      * @cfg {String} invalidText @hide
23304      */
23305     /**
23306      * @cfg {String} msgFx @hide
23307      */
23308     /**
23309      * @cfg {String} validateOnBlur @hide
23310      */
23311 });
23312
23313 Roo.HtmlEditorCore.white = [
23314         'area', 'br', 'img', 'input', 'hr', 'wbr',
23315         
23316        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23317        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23318        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23319        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23320        'table',   'ul',         'xmp', 
23321        
23322        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23323       'thead',   'tr', 
23324      
23325       'dir', 'menu', 'ol', 'ul', 'dl',
23326        
23327       'embed',  'object'
23328 ];
23329
23330
23331 Roo.HtmlEditorCore.black = [
23332     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23333         'applet', // 
23334         'base',   'basefont', 'bgsound', 'blink',  'body', 
23335         'frame',  'frameset', 'head',    'html',   'ilayer', 
23336         'iframe', 'layer',  'link',     'meta',    'object',   
23337         'script', 'style' ,'title',  'xml' // clean later..
23338 ];
23339 Roo.HtmlEditorCore.clean = [
23340     'script', 'style', 'title', 'xml'
23341 ];
23342 Roo.HtmlEditorCore.remove = [
23343     'font'
23344 ];
23345 // attributes..
23346
23347 Roo.HtmlEditorCore.ablack = [
23348     'on'
23349 ];
23350     
23351 Roo.HtmlEditorCore.aclean = [ 
23352     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23353 ];
23354
23355 // protocols..
23356 Roo.HtmlEditorCore.pwhite= [
23357         'http',  'https',  'mailto'
23358 ];
23359
23360 // white listed style attributes.
23361 Roo.HtmlEditorCore.cwhite= [
23362       //  'text-align', /// default is to allow most things..
23363       
23364          
23365 //        'font-size'//??
23366 ];
23367
23368 // black listed style attributes.
23369 Roo.HtmlEditorCore.cblack= [
23370       //  'font-size' -- this can be set by the project 
23371 ];
23372
23373
23374 Roo.HtmlEditorCore.swapCodes   =[ 
23375     [    8211, "--" ], 
23376     [    8212, "--" ], 
23377     [    8216,  "'" ],  
23378     [    8217, "'" ],  
23379     [    8220, '"' ],  
23380     [    8221, '"' ],  
23381     [    8226, "*" ],  
23382     [    8230, "..." ]
23383 ]; 
23384
23385     /*
23386  * - LGPL
23387  *
23388  * HtmlEditor
23389  * 
23390  */
23391
23392 /**
23393  * @class Roo.bootstrap.HtmlEditor
23394  * @extends Roo.bootstrap.TextArea
23395  * Bootstrap HtmlEditor class
23396
23397  * @constructor
23398  * Create a new HtmlEditor
23399  * @param {Object} config The config object
23400  */
23401
23402 Roo.bootstrap.HtmlEditor = function(config){
23403     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23404     if (!this.toolbars) {
23405         this.toolbars = [];
23406     }
23407     
23408     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23409     this.addEvents({
23410             /**
23411              * @event initialize
23412              * Fires when the editor is fully initialized (including the iframe)
23413              * @param {HtmlEditor} this
23414              */
23415             initialize: true,
23416             /**
23417              * @event activate
23418              * Fires when the editor is first receives the focus. Any insertion must wait
23419              * until after this event.
23420              * @param {HtmlEditor} this
23421              */
23422             activate: true,
23423              /**
23424              * @event beforesync
23425              * Fires before the textarea is updated with content from the editor iframe. Return false
23426              * to cancel the sync.
23427              * @param {HtmlEditor} this
23428              * @param {String} html
23429              */
23430             beforesync: true,
23431              /**
23432              * @event beforepush
23433              * Fires before the iframe editor is updated with content from the textarea. Return false
23434              * to cancel the push.
23435              * @param {HtmlEditor} this
23436              * @param {String} html
23437              */
23438             beforepush: true,
23439              /**
23440              * @event sync
23441              * Fires when the textarea is updated with content from the editor iframe.
23442              * @param {HtmlEditor} this
23443              * @param {String} html
23444              */
23445             sync: true,
23446              /**
23447              * @event push
23448              * Fires when the iframe editor is updated with content from the textarea.
23449              * @param {HtmlEditor} this
23450              * @param {String} html
23451              */
23452             push: true,
23453              /**
23454              * @event editmodechange
23455              * Fires when the editor switches edit modes
23456              * @param {HtmlEditor} this
23457              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23458              */
23459             editmodechange: true,
23460             /**
23461              * @event editorevent
23462              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23463              * @param {HtmlEditor} this
23464              */
23465             editorevent: true,
23466             /**
23467              * @event firstfocus
23468              * Fires when on first focus - needed by toolbars..
23469              * @param {HtmlEditor} this
23470              */
23471             firstfocus: true,
23472             /**
23473              * @event autosave
23474              * Auto save the htmlEditor value as a file into Events
23475              * @param {HtmlEditor} this
23476              */
23477             autosave: true,
23478             /**
23479              * @event savedpreview
23480              * preview the saved version of htmlEditor
23481              * @param {HtmlEditor} this
23482              */
23483             savedpreview: true
23484         });
23485 };
23486
23487
23488 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23489     
23490     
23491       /**
23492      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23493      */
23494     toolbars : false,
23495     
23496      /**
23497     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23498     */
23499     btns : [],
23500    
23501      /**
23502      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23503      *                        Roo.resizable.
23504      */
23505     resizable : false,
23506      /**
23507      * @cfg {Number} height (in pixels)
23508      */   
23509     height: 300,
23510    /**
23511      * @cfg {Number} width (in pixels)
23512      */   
23513     width: false,
23514     
23515     /**
23516      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23517      * 
23518      */
23519     stylesheets: false,
23520     
23521     // id of frame..
23522     frameId: false,
23523     
23524     // private properties
23525     validationEvent : false,
23526     deferHeight: true,
23527     initialized : false,
23528     activated : false,
23529     
23530     onFocus : Roo.emptyFn,
23531     iframePad:3,
23532     hideMode:'offsets',
23533     
23534     tbContainer : false,
23535     
23536     bodyCls : '',
23537     
23538     toolbarContainer :function() {
23539         return this.wrap.select('.x-html-editor-tb',true).first();
23540     },
23541
23542     /**
23543      * Protected method that will not generally be called directly. It
23544      * is called when the editor creates its toolbar. Override this method if you need to
23545      * add custom toolbar buttons.
23546      * @param {HtmlEditor} editor
23547      */
23548     createToolbar : function(){
23549         Roo.log('renewing');
23550         Roo.log("create toolbars");
23551         
23552         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23553         this.toolbars[0].render(this.toolbarContainer());
23554         
23555         return;
23556         
23557 //        if (!editor.toolbars || !editor.toolbars.length) {
23558 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23559 //        }
23560 //        
23561 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23562 //            editor.toolbars[i] = Roo.factory(
23563 //                    typeof(editor.toolbars[i]) == 'string' ?
23564 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23565 //                Roo.bootstrap.HtmlEditor);
23566 //            editor.toolbars[i].init(editor);
23567 //        }
23568     },
23569
23570      
23571     // private
23572     onRender : function(ct, position)
23573     {
23574        // Roo.log("Call onRender: " + this.xtype);
23575         var _t = this;
23576         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23577       
23578         this.wrap = this.inputEl().wrap({
23579             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23580         });
23581         
23582         this.editorcore.onRender(ct, position);
23583          
23584         if (this.resizable) {
23585             this.resizeEl = new Roo.Resizable(this.wrap, {
23586                 pinned : true,
23587                 wrap: true,
23588                 dynamic : true,
23589                 minHeight : this.height,
23590                 height: this.height,
23591                 handles : this.resizable,
23592                 width: this.width,
23593                 listeners : {
23594                     resize : function(r, w, h) {
23595                         _t.onResize(w,h); // -something
23596                     }
23597                 }
23598             });
23599             
23600         }
23601         this.createToolbar(this);
23602        
23603         
23604         if(!this.width && this.resizable){
23605             this.setSize(this.wrap.getSize());
23606         }
23607         if (this.resizeEl) {
23608             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23609             // should trigger onReize..
23610         }
23611         
23612     },
23613
23614     // private
23615     onResize : function(w, h)
23616     {
23617         Roo.log('resize: ' +w + ',' + h );
23618         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23619         var ew = false;
23620         var eh = false;
23621         
23622         if(this.inputEl() ){
23623             if(typeof w == 'number'){
23624                 var aw = w - this.wrap.getFrameWidth('lr');
23625                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23626                 ew = aw;
23627             }
23628             if(typeof h == 'number'){
23629                  var tbh = -11;  // fixme it needs to tool bar size!
23630                 for (var i =0; i < this.toolbars.length;i++) {
23631                     // fixme - ask toolbars for heights?
23632                     tbh += this.toolbars[i].el.getHeight();
23633                     //if (this.toolbars[i].footer) {
23634                     //    tbh += this.toolbars[i].footer.el.getHeight();
23635                     //}
23636                 }
23637               
23638                 
23639                 
23640                 
23641                 
23642                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23643                 ah -= 5; // knock a few pixes off for look..
23644                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23645                 var eh = ah;
23646             }
23647         }
23648         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23649         this.editorcore.onResize(ew,eh);
23650         
23651     },
23652
23653     /**
23654      * Toggles the editor between standard and source edit mode.
23655      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23656      */
23657     toggleSourceEdit : function(sourceEditMode)
23658     {
23659         this.editorcore.toggleSourceEdit(sourceEditMode);
23660         
23661         if(this.editorcore.sourceEditMode){
23662             Roo.log('editor - showing textarea');
23663             
23664 //            Roo.log('in');
23665 //            Roo.log(this.syncValue());
23666             this.syncValue();
23667             this.inputEl().removeClass(['hide', 'x-hidden']);
23668             this.inputEl().dom.removeAttribute('tabIndex');
23669             this.inputEl().focus();
23670         }else{
23671             Roo.log('editor - hiding textarea');
23672 //            Roo.log('out')
23673 //            Roo.log(this.pushValue()); 
23674             this.pushValue();
23675             
23676             this.inputEl().addClass(['hide', 'x-hidden']);
23677             this.inputEl().dom.setAttribute('tabIndex', -1);
23678             //this.deferFocus();
23679         }
23680          
23681         if(this.resizable){
23682             this.setSize(this.wrap.getSize());
23683         }
23684         
23685         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23686     },
23687  
23688     // private (for BoxComponent)
23689     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23690
23691     // private (for BoxComponent)
23692     getResizeEl : function(){
23693         return this.wrap;
23694     },
23695
23696     // private (for BoxComponent)
23697     getPositionEl : function(){
23698         return this.wrap;
23699     },
23700
23701     // private
23702     initEvents : function(){
23703         this.originalValue = this.getValue();
23704     },
23705
23706 //    /**
23707 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23708 //     * @method
23709 //     */
23710 //    markInvalid : Roo.emptyFn,
23711 //    /**
23712 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23713 //     * @method
23714 //     */
23715 //    clearInvalid : Roo.emptyFn,
23716
23717     setValue : function(v){
23718         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23719         this.editorcore.pushValue();
23720     },
23721
23722      
23723     // private
23724     deferFocus : function(){
23725         this.focus.defer(10, this);
23726     },
23727
23728     // doc'ed in Field
23729     focus : function(){
23730         this.editorcore.focus();
23731         
23732     },
23733       
23734
23735     // private
23736     onDestroy : function(){
23737         
23738         
23739         
23740         if(this.rendered){
23741             
23742             for (var i =0; i < this.toolbars.length;i++) {
23743                 // fixme - ask toolbars for heights?
23744                 this.toolbars[i].onDestroy();
23745             }
23746             
23747             this.wrap.dom.innerHTML = '';
23748             this.wrap.remove();
23749         }
23750     },
23751
23752     // private
23753     onFirstFocus : function(){
23754         //Roo.log("onFirstFocus");
23755         this.editorcore.onFirstFocus();
23756          for (var i =0; i < this.toolbars.length;i++) {
23757             this.toolbars[i].onFirstFocus();
23758         }
23759         
23760     },
23761     
23762     // private
23763     syncValue : function()
23764     {   
23765         this.editorcore.syncValue();
23766     },
23767     
23768     pushValue : function()
23769     {   
23770         this.editorcore.pushValue();
23771     }
23772      
23773     
23774     // hide stuff that is not compatible
23775     /**
23776      * @event blur
23777      * @hide
23778      */
23779     /**
23780      * @event change
23781      * @hide
23782      */
23783     /**
23784      * @event focus
23785      * @hide
23786      */
23787     /**
23788      * @event specialkey
23789      * @hide
23790      */
23791     /**
23792      * @cfg {String} fieldClass @hide
23793      */
23794     /**
23795      * @cfg {String} focusClass @hide
23796      */
23797     /**
23798      * @cfg {String} autoCreate @hide
23799      */
23800     /**
23801      * @cfg {String} inputType @hide
23802      */
23803     /**
23804      * @cfg {String} invalidClass @hide
23805      */
23806     /**
23807      * @cfg {String} invalidText @hide
23808      */
23809     /**
23810      * @cfg {String} msgFx @hide
23811      */
23812     /**
23813      * @cfg {String} validateOnBlur @hide
23814      */
23815 });
23816  
23817     
23818    
23819    
23820    
23821       
23822 Roo.namespace('Roo.bootstrap.htmleditor');
23823 /**
23824  * @class Roo.bootstrap.HtmlEditorToolbar1
23825  * Basic Toolbar
23826  * 
23827  * Usage:
23828  *
23829  new Roo.bootstrap.HtmlEditor({
23830     ....
23831     toolbars : [
23832         new Roo.bootstrap.HtmlEditorToolbar1({
23833             disable : { fonts: 1 , format: 1, ..., ... , ...],
23834             btns : [ .... ]
23835         })
23836     }
23837      
23838  * 
23839  * @cfg {Object} disable List of elements to disable..
23840  * @cfg {Array} btns List of additional buttons.
23841  * 
23842  * 
23843  * NEEDS Extra CSS? 
23844  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23845  */
23846  
23847 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23848 {
23849     
23850     Roo.apply(this, config);
23851     
23852     // default disabled, based on 'good practice'..
23853     this.disable = this.disable || {};
23854     Roo.applyIf(this.disable, {
23855         fontSize : true,
23856         colors : true,
23857         specialElements : true
23858     });
23859     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23860     
23861     this.editor = config.editor;
23862     this.editorcore = config.editor.editorcore;
23863     
23864     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23865     
23866     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23867     // dont call parent... till later.
23868 }
23869 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23870      
23871     bar : true,
23872     
23873     editor : false,
23874     editorcore : false,
23875     
23876     
23877     formats : [
23878         "p" ,  
23879         "h1","h2","h3","h4","h5","h6", 
23880         "pre", "code", 
23881         "abbr", "acronym", "address", "cite", "samp", "var",
23882         'div','span'
23883     ],
23884     
23885     onRender : function(ct, position)
23886     {
23887        // Roo.log("Call onRender: " + this.xtype);
23888         
23889        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23890        Roo.log(this.el);
23891        this.el.dom.style.marginBottom = '0';
23892        var _this = this;
23893        var editorcore = this.editorcore;
23894        var editor= this.editor;
23895        
23896        var children = [];
23897        var btn = function(id,cmd , toggle, handler, html){
23898        
23899             var  event = toggle ? 'toggle' : 'click';
23900        
23901             var a = {
23902                 size : 'sm',
23903                 xtype: 'Button',
23904                 xns: Roo.bootstrap,
23905                 glyphicon : id,
23906                 cmd : id || cmd,
23907                 enableToggle:toggle !== false,
23908                 html : html || '',
23909                 pressed : toggle ? false : null,
23910                 listeners : {}
23911             };
23912             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23913                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23914             };
23915             children.push(a);
23916             return a;
23917        }
23918        
23919     //    var cb_box = function...
23920         
23921         var style = {
23922                 xtype: 'Button',
23923                 size : 'sm',
23924                 xns: Roo.bootstrap,
23925                 glyphicon : 'font',
23926                 //html : 'submit'
23927                 menu : {
23928                     xtype: 'Menu',
23929                     xns: Roo.bootstrap,
23930                     items:  []
23931                 }
23932         };
23933         Roo.each(this.formats, function(f) {
23934             style.menu.items.push({
23935                 xtype :'MenuItem',
23936                 xns: Roo.bootstrap,
23937                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23938                 tagname : f,
23939                 listeners : {
23940                     click : function()
23941                     {
23942                         editorcore.insertTag(this.tagname);
23943                         editor.focus();
23944                     }
23945                 }
23946                 
23947             });
23948         });
23949         children.push(style);   
23950         
23951         btn('bold',false,true);
23952         btn('italic',false,true);
23953         btn('align-left', 'justifyleft',true);
23954         btn('align-center', 'justifycenter',true);
23955         btn('align-right' , 'justifyright',true);
23956         btn('link', false, false, function(btn) {
23957             //Roo.log("create link?");
23958             var url = prompt(this.createLinkText, this.defaultLinkValue);
23959             if(url && url != 'http:/'+'/'){
23960                 this.editorcore.relayCmd('createlink', url);
23961             }
23962         }),
23963         btn('list','insertunorderedlist',true);
23964         btn('pencil', false,true, function(btn){
23965                 Roo.log(this);
23966                 this.toggleSourceEdit(btn.pressed);
23967         });
23968         
23969         if (this.editor.btns.length > 0) {
23970             for (var i = 0; i<this.editor.btns.length; i++) {
23971                 children.push(this.editor.btns[i]);
23972             }
23973         }
23974         
23975         /*
23976         var cog = {
23977                 xtype: 'Button',
23978                 size : 'sm',
23979                 xns: Roo.bootstrap,
23980                 glyphicon : 'cog',
23981                 //html : 'submit'
23982                 menu : {
23983                     xtype: 'Menu',
23984                     xns: Roo.bootstrap,
23985                     items:  []
23986                 }
23987         };
23988         
23989         cog.menu.items.push({
23990             xtype :'MenuItem',
23991             xns: Roo.bootstrap,
23992             html : Clean styles,
23993             tagname : f,
23994             listeners : {
23995                 click : function()
23996                 {
23997                     editorcore.insertTag(this.tagname);
23998                     editor.focus();
23999                 }
24000             }
24001             
24002         });
24003        */
24004         
24005          
24006        this.xtype = 'NavSimplebar';
24007         
24008         for(var i=0;i< children.length;i++) {
24009             
24010             this.buttons.add(this.addxtypeChild(children[i]));
24011             
24012         }
24013         
24014         editor.on('editorevent', this.updateToolbar, this);
24015     },
24016     onBtnClick : function(id)
24017     {
24018        this.editorcore.relayCmd(id);
24019        this.editorcore.focus();
24020     },
24021     
24022     /**
24023      * Protected method that will not generally be called directly. It triggers
24024      * a toolbar update by reading the markup state of the current selection in the editor.
24025      */
24026     updateToolbar: function(){
24027
24028         if(!this.editorcore.activated){
24029             this.editor.onFirstFocus(); // is this neeed?
24030             return;
24031         }
24032
24033         var btns = this.buttons; 
24034         var doc = this.editorcore.doc;
24035         btns.get('bold').setActive(doc.queryCommandState('bold'));
24036         btns.get('italic').setActive(doc.queryCommandState('italic'));
24037         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24038         
24039         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24040         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24041         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24042         
24043         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24044         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24045          /*
24046         
24047         var ans = this.editorcore.getAllAncestors();
24048         if (this.formatCombo) {
24049             
24050             
24051             var store = this.formatCombo.store;
24052             this.formatCombo.setValue("");
24053             for (var i =0; i < ans.length;i++) {
24054                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24055                     // select it..
24056                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24057                     break;
24058                 }
24059             }
24060         }
24061         
24062         
24063         
24064         // hides menus... - so this cant be on a menu...
24065         Roo.bootstrap.MenuMgr.hideAll();
24066         */
24067         Roo.bootstrap.MenuMgr.hideAll();
24068         //this.editorsyncValue();
24069     },
24070     onFirstFocus: function() {
24071         this.buttons.each(function(item){
24072            item.enable();
24073         });
24074     },
24075     toggleSourceEdit : function(sourceEditMode){
24076         
24077           
24078         if(sourceEditMode){
24079             Roo.log("disabling buttons");
24080            this.buttons.each( function(item){
24081                 if(item.cmd != 'pencil'){
24082                     item.disable();
24083                 }
24084             });
24085           
24086         }else{
24087             Roo.log("enabling buttons");
24088             if(this.editorcore.initialized){
24089                 this.buttons.each( function(item){
24090                     item.enable();
24091                 });
24092             }
24093             
24094         }
24095         Roo.log("calling toggole on editor");
24096         // tell the editor that it's been pressed..
24097         this.editor.toggleSourceEdit(sourceEditMode);
24098        
24099     }
24100 });
24101
24102
24103
24104
24105
24106 /**
24107  * @class Roo.bootstrap.Table.AbstractSelectionModel
24108  * @extends Roo.util.Observable
24109  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24110  * implemented by descendant classes.  This class should not be directly instantiated.
24111  * @constructor
24112  */
24113 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24114     this.locked = false;
24115     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24116 };
24117
24118
24119 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24120     /** @ignore Called by the grid automatically. Do not call directly. */
24121     init : function(grid){
24122         this.grid = grid;
24123         this.initEvents();
24124     },
24125
24126     /**
24127      * Locks the selections.
24128      */
24129     lock : function(){
24130         this.locked = true;
24131     },
24132
24133     /**
24134      * Unlocks the selections.
24135      */
24136     unlock : function(){
24137         this.locked = false;
24138     },
24139
24140     /**
24141      * Returns true if the selections are locked.
24142      * @return {Boolean}
24143      */
24144     isLocked : function(){
24145         return this.locked;
24146     }
24147 });
24148 /**
24149  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24150  * @class Roo.bootstrap.Table.RowSelectionModel
24151  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24152  * It supports multiple selections and keyboard selection/navigation. 
24153  * @constructor
24154  * @param {Object} config
24155  */
24156
24157 Roo.bootstrap.Table.RowSelectionModel = function(config){
24158     Roo.apply(this, config);
24159     this.selections = new Roo.util.MixedCollection(false, function(o){
24160         return o.id;
24161     });
24162
24163     this.last = false;
24164     this.lastActive = false;
24165
24166     this.addEvents({
24167         /**
24168              * @event selectionchange
24169              * Fires when the selection changes
24170              * @param {SelectionModel} this
24171              */
24172             "selectionchange" : true,
24173         /**
24174              * @event afterselectionchange
24175              * Fires after the selection changes (eg. by key press or clicking)
24176              * @param {SelectionModel} this
24177              */
24178             "afterselectionchange" : true,
24179         /**
24180              * @event beforerowselect
24181              * Fires when a row is selected being selected, return false to cancel.
24182              * @param {SelectionModel} this
24183              * @param {Number} rowIndex The selected index
24184              * @param {Boolean} keepExisting False if other selections will be cleared
24185              */
24186             "beforerowselect" : true,
24187         /**
24188              * @event rowselect
24189              * Fires when a row is selected.
24190              * @param {SelectionModel} this
24191              * @param {Number} rowIndex The selected index
24192              * @param {Roo.data.Record} r The record
24193              */
24194             "rowselect" : true,
24195         /**
24196              * @event rowdeselect
24197              * Fires when a row is deselected.
24198              * @param {SelectionModel} this
24199              * @param {Number} rowIndex The selected index
24200              */
24201         "rowdeselect" : true
24202     });
24203     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24204     this.locked = false;
24205  };
24206
24207 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24208     /**
24209      * @cfg {Boolean} singleSelect
24210      * True to allow selection of only one row at a time (defaults to false)
24211      */
24212     singleSelect : false,
24213
24214     // private
24215     initEvents : function()
24216     {
24217
24218         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24219         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24220         //}else{ // allow click to work like normal
24221          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24222         //}
24223         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24224         this.grid.on("rowclick", this.handleMouseDown, this);
24225         
24226         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24227             "up" : function(e){
24228                 if(!e.shiftKey){
24229                     this.selectPrevious(e.shiftKey);
24230                 }else if(this.last !== false && this.lastActive !== false){
24231                     var last = this.last;
24232                     this.selectRange(this.last,  this.lastActive-1);
24233                     this.grid.getView().focusRow(this.lastActive);
24234                     if(last !== false){
24235                         this.last = last;
24236                     }
24237                 }else{
24238                     this.selectFirstRow();
24239                 }
24240                 this.fireEvent("afterselectionchange", this);
24241             },
24242             "down" : function(e){
24243                 if(!e.shiftKey){
24244                     this.selectNext(e.shiftKey);
24245                 }else if(this.last !== false && this.lastActive !== false){
24246                     var last = this.last;
24247                     this.selectRange(this.last,  this.lastActive+1);
24248                     this.grid.getView().focusRow(this.lastActive);
24249                     if(last !== false){
24250                         this.last = last;
24251                     }
24252                 }else{
24253                     this.selectFirstRow();
24254                 }
24255                 this.fireEvent("afterselectionchange", this);
24256             },
24257             scope: this
24258         });
24259         this.grid.store.on('load', function(){
24260             this.selections.clear();
24261         },this);
24262         /*
24263         var view = this.grid.view;
24264         view.on("refresh", this.onRefresh, this);
24265         view.on("rowupdated", this.onRowUpdated, this);
24266         view.on("rowremoved", this.onRemove, this);
24267         */
24268     },
24269
24270     // private
24271     onRefresh : function()
24272     {
24273         var ds = this.grid.store, i, v = this.grid.view;
24274         var s = this.selections;
24275         s.each(function(r){
24276             if((i = ds.indexOfId(r.id)) != -1){
24277                 v.onRowSelect(i);
24278             }else{
24279                 s.remove(r);
24280             }
24281         });
24282     },
24283
24284     // private
24285     onRemove : function(v, index, r){
24286         this.selections.remove(r);
24287     },
24288
24289     // private
24290     onRowUpdated : function(v, index, r){
24291         if(this.isSelected(r)){
24292             v.onRowSelect(index);
24293         }
24294     },
24295
24296     /**
24297      * Select records.
24298      * @param {Array} records The records to select
24299      * @param {Boolean} keepExisting (optional) True to keep existing selections
24300      */
24301     selectRecords : function(records, keepExisting)
24302     {
24303         if(!keepExisting){
24304             this.clearSelections();
24305         }
24306             var ds = this.grid.store;
24307         for(var i = 0, len = records.length; i < len; i++){
24308             this.selectRow(ds.indexOf(records[i]), true);
24309         }
24310     },
24311
24312     /**
24313      * Gets the number of selected rows.
24314      * @return {Number}
24315      */
24316     getCount : function(){
24317         return this.selections.length;
24318     },
24319
24320     /**
24321      * Selects the first row in the grid.
24322      */
24323     selectFirstRow : function(){
24324         this.selectRow(0);
24325     },
24326
24327     /**
24328      * Select the last row.
24329      * @param {Boolean} keepExisting (optional) True to keep existing selections
24330      */
24331     selectLastRow : function(keepExisting){
24332         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24333         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24334     },
24335
24336     /**
24337      * Selects the row immediately following the last selected row.
24338      * @param {Boolean} keepExisting (optional) True to keep existing selections
24339      */
24340     selectNext : function(keepExisting)
24341     {
24342             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24343             this.selectRow(this.last+1, keepExisting);
24344             this.grid.getView().focusRow(this.last);
24345         }
24346     },
24347
24348     /**
24349      * Selects the row that precedes the last selected row.
24350      * @param {Boolean} keepExisting (optional) True to keep existing selections
24351      */
24352     selectPrevious : function(keepExisting){
24353         if(this.last){
24354             this.selectRow(this.last-1, keepExisting);
24355             this.grid.getView().focusRow(this.last);
24356         }
24357     },
24358
24359     /**
24360      * Returns the selected records
24361      * @return {Array} Array of selected records
24362      */
24363     getSelections : function(){
24364         return [].concat(this.selections.items);
24365     },
24366
24367     /**
24368      * Returns the first selected record.
24369      * @return {Record}
24370      */
24371     getSelected : function(){
24372         return this.selections.itemAt(0);
24373     },
24374
24375
24376     /**
24377      * Clears all selections.
24378      */
24379     clearSelections : function(fast)
24380     {
24381         if(this.locked) {
24382             return;
24383         }
24384         if(fast !== true){
24385                 var ds = this.grid.store;
24386             var s = this.selections;
24387             s.each(function(r){
24388                 this.deselectRow(ds.indexOfId(r.id));
24389             }, this);
24390             s.clear();
24391         }else{
24392             this.selections.clear();
24393         }
24394         this.last = false;
24395     },
24396
24397
24398     /**
24399      * Selects all rows.
24400      */
24401     selectAll : function(){
24402         if(this.locked) {
24403             return;
24404         }
24405         this.selections.clear();
24406         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24407             this.selectRow(i, true);
24408         }
24409     },
24410
24411     /**
24412      * Returns True if there is a selection.
24413      * @return {Boolean}
24414      */
24415     hasSelection : function(){
24416         return this.selections.length > 0;
24417     },
24418
24419     /**
24420      * Returns True if the specified row is selected.
24421      * @param {Number/Record} record The record or index of the record to check
24422      * @return {Boolean}
24423      */
24424     isSelected : function(index){
24425             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24426         return (r && this.selections.key(r.id) ? true : false);
24427     },
24428
24429     /**
24430      * Returns True if the specified record id is selected.
24431      * @param {String} id The id of record to check
24432      * @return {Boolean}
24433      */
24434     isIdSelected : function(id){
24435         return (this.selections.key(id) ? true : false);
24436     },
24437
24438
24439     // private
24440     handleMouseDBClick : function(e, t){
24441         
24442     },
24443     // private
24444     handleMouseDown : function(e, t)
24445     {
24446             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24447         if(this.isLocked() || rowIndex < 0 ){
24448             return;
24449         };
24450         if(e.shiftKey && this.last !== false){
24451             var last = this.last;
24452             this.selectRange(last, rowIndex, e.ctrlKey);
24453             this.last = last; // reset the last
24454             t.focus();
24455     
24456         }else{
24457             var isSelected = this.isSelected(rowIndex);
24458             //Roo.log("select row:" + rowIndex);
24459             if(isSelected){
24460                 this.deselectRow(rowIndex);
24461             } else {
24462                         this.selectRow(rowIndex, true);
24463             }
24464     
24465             /*
24466                 if(e.button !== 0 && isSelected){
24467                 alert('rowIndex 2: ' + rowIndex);
24468                     view.focusRow(rowIndex);
24469                 }else if(e.ctrlKey && isSelected){
24470                     this.deselectRow(rowIndex);
24471                 }else if(!isSelected){
24472                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24473                     view.focusRow(rowIndex);
24474                 }
24475             */
24476         }
24477         this.fireEvent("afterselectionchange", this);
24478     },
24479     // private
24480     handleDragableRowClick :  function(grid, rowIndex, e) 
24481     {
24482         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24483             this.selectRow(rowIndex, false);
24484             grid.view.focusRow(rowIndex);
24485              this.fireEvent("afterselectionchange", this);
24486         }
24487     },
24488     
24489     /**
24490      * Selects multiple rows.
24491      * @param {Array} rows Array of the indexes of the row to select
24492      * @param {Boolean} keepExisting (optional) True to keep existing selections
24493      */
24494     selectRows : function(rows, keepExisting){
24495         if(!keepExisting){
24496             this.clearSelections();
24497         }
24498         for(var i = 0, len = rows.length; i < len; i++){
24499             this.selectRow(rows[i], true);
24500         }
24501     },
24502
24503     /**
24504      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24505      * @param {Number} startRow The index of the first row in the range
24506      * @param {Number} endRow The index of the last row in the range
24507      * @param {Boolean} keepExisting (optional) True to retain existing selections
24508      */
24509     selectRange : function(startRow, endRow, keepExisting){
24510         if(this.locked) {
24511             return;
24512         }
24513         if(!keepExisting){
24514             this.clearSelections();
24515         }
24516         if(startRow <= endRow){
24517             for(var i = startRow; i <= endRow; i++){
24518                 this.selectRow(i, true);
24519             }
24520         }else{
24521             for(var i = startRow; i >= endRow; i--){
24522                 this.selectRow(i, true);
24523             }
24524         }
24525     },
24526
24527     /**
24528      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24529      * @param {Number} startRow The index of the first row in the range
24530      * @param {Number} endRow The index of the last row in the range
24531      */
24532     deselectRange : function(startRow, endRow, preventViewNotify){
24533         if(this.locked) {
24534             return;
24535         }
24536         for(var i = startRow; i <= endRow; i++){
24537             this.deselectRow(i, preventViewNotify);
24538         }
24539     },
24540
24541     /**
24542      * Selects a row.
24543      * @param {Number} row The index of the row to select
24544      * @param {Boolean} keepExisting (optional) True to keep existing selections
24545      */
24546     selectRow : function(index, keepExisting, preventViewNotify)
24547     {
24548             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24549             return;
24550         }
24551         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24552             if(!keepExisting || this.singleSelect){
24553                 this.clearSelections();
24554             }
24555             
24556             var r = this.grid.store.getAt(index);
24557             //console.log('selectRow - record id :' + r.id);
24558             
24559             this.selections.add(r);
24560             this.last = this.lastActive = index;
24561             if(!preventViewNotify){
24562                 var proxy = new Roo.Element(
24563                                 this.grid.getRowDom(index)
24564                 );
24565                 proxy.addClass('bg-info info');
24566             }
24567             this.fireEvent("rowselect", this, index, r);
24568             this.fireEvent("selectionchange", this);
24569         }
24570     },
24571
24572     /**
24573      * Deselects a row.
24574      * @param {Number} row The index of the row to deselect
24575      */
24576     deselectRow : function(index, preventViewNotify)
24577     {
24578         if(this.locked) {
24579             return;
24580         }
24581         if(this.last == index){
24582             this.last = false;
24583         }
24584         if(this.lastActive == index){
24585             this.lastActive = false;
24586         }
24587         
24588         var r = this.grid.store.getAt(index);
24589         if (!r) {
24590             return;
24591         }
24592         
24593         this.selections.remove(r);
24594         //.console.log('deselectRow - record id :' + r.id);
24595         if(!preventViewNotify){
24596         
24597             var proxy = new Roo.Element(
24598                 this.grid.getRowDom(index)
24599             );
24600             proxy.removeClass('bg-info info');
24601         }
24602         this.fireEvent("rowdeselect", this, index);
24603         this.fireEvent("selectionchange", this);
24604     },
24605
24606     // private
24607     restoreLast : function(){
24608         if(this._last){
24609             this.last = this._last;
24610         }
24611     },
24612
24613     // private
24614     acceptsNav : function(row, col, cm){
24615         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24616     },
24617
24618     // private
24619     onEditorKey : function(field, e){
24620         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24621         if(k == e.TAB){
24622             e.stopEvent();
24623             ed.completeEdit();
24624             if(e.shiftKey){
24625                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24626             }else{
24627                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24628             }
24629         }else if(k == e.ENTER && !e.ctrlKey){
24630             e.stopEvent();
24631             ed.completeEdit();
24632             if(e.shiftKey){
24633                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24634             }else{
24635                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24636             }
24637         }else if(k == e.ESC){
24638             ed.cancelEdit();
24639         }
24640         if(newCell){
24641             g.startEditing(newCell[0], newCell[1]);
24642         }
24643     }
24644 });
24645 /*
24646  * Based on:
24647  * Ext JS Library 1.1.1
24648  * Copyright(c) 2006-2007, Ext JS, LLC.
24649  *
24650  * Originally Released Under LGPL - original licence link has changed is not relivant.
24651  *
24652  * Fork - LGPL
24653  * <script type="text/javascript">
24654  */
24655  
24656 /**
24657  * @class Roo.bootstrap.PagingToolbar
24658  * @extends Roo.bootstrap.NavSimplebar
24659  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24660  * @constructor
24661  * Create a new PagingToolbar
24662  * @param {Object} config The config object
24663  * @param {Roo.data.Store} store
24664  */
24665 Roo.bootstrap.PagingToolbar = function(config)
24666 {
24667     // old args format still supported... - xtype is prefered..
24668         // created from xtype...
24669     
24670     this.ds = config.dataSource;
24671     
24672     if (config.store && !this.ds) {
24673         this.store= Roo.factory(config.store, Roo.data);
24674         this.ds = this.store;
24675         this.ds.xmodule = this.xmodule || false;
24676     }
24677     
24678     this.toolbarItems = [];
24679     if (config.items) {
24680         this.toolbarItems = config.items;
24681     }
24682     
24683     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24684     
24685     this.cursor = 0;
24686     
24687     if (this.ds) { 
24688         this.bind(this.ds);
24689     }
24690     
24691     if (Roo.bootstrap.version == 4) {
24692         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24693     } else {
24694         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24695     }
24696     
24697 };
24698
24699 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24700     /**
24701      * @cfg {Roo.data.Store} dataSource
24702      * The underlying data store providing the paged data
24703      */
24704     /**
24705      * @cfg {String/HTMLElement/Element} container
24706      * container The id or element that will contain the toolbar
24707      */
24708     /**
24709      * @cfg {Boolean} displayInfo
24710      * True to display the displayMsg (defaults to false)
24711      */
24712     /**
24713      * @cfg {Number} pageSize
24714      * The number of records to display per page (defaults to 20)
24715      */
24716     pageSize: 20,
24717     /**
24718      * @cfg {String} displayMsg
24719      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24720      */
24721     displayMsg : 'Displaying {0} - {1} of {2}',
24722     /**
24723      * @cfg {String} emptyMsg
24724      * The message to display when no records are found (defaults to "No data to display")
24725      */
24726     emptyMsg : 'No data to display',
24727     /**
24728      * Customizable piece of the default paging text (defaults to "Page")
24729      * @type String
24730      */
24731     beforePageText : "Page",
24732     /**
24733      * Customizable piece of the default paging text (defaults to "of %0")
24734      * @type String
24735      */
24736     afterPageText : "of {0}",
24737     /**
24738      * Customizable piece of the default paging text (defaults to "First Page")
24739      * @type String
24740      */
24741     firstText : "First Page",
24742     /**
24743      * Customizable piece of the default paging text (defaults to "Previous Page")
24744      * @type String
24745      */
24746     prevText : "Previous Page",
24747     /**
24748      * Customizable piece of the default paging text (defaults to "Next Page")
24749      * @type String
24750      */
24751     nextText : "Next Page",
24752     /**
24753      * Customizable piece of the default paging text (defaults to "Last Page")
24754      * @type String
24755      */
24756     lastText : "Last Page",
24757     /**
24758      * Customizable piece of the default paging text (defaults to "Refresh")
24759      * @type String
24760      */
24761     refreshText : "Refresh",
24762
24763     buttons : false,
24764     // private
24765     onRender : function(ct, position) 
24766     {
24767         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24768         this.navgroup.parentId = this.id;
24769         this.navgroup.onRender(this.el, null);
24770         // add the buttons to the navgroup
24771         
24772         if(this.displayInfo){
24773             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24774             this.displayEl = this.el.select('.x-paging-info', true).first();
24775 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24776 //            this.displayEl = navel.el.select('span',true).first();
24777         }
24778         
24779         var _this = this;
24780         
24781         if(this.buttons){
24782             Roo.each(_this.buttons, function(e){ // this might need to use render????
24783                Roo.factory(e).render(_this.el);
24784             });
24785         }
24786             
24787         Roo.each(_this.toolbarItems, function(e) {
24788             _this.navgroup.addItem(e);
24789         });
24790         
24791         
24792         this.first = this.navgroup.addItem({
24793             tooltip: this.firstText,
24794             cls: "prev btn-outline-secondary",
24795             html : ' <i class="fa fa-step-backward"></i>',
24796             disabled: true,
24797             preventDefault: true,
24798             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24799         });
24800         
24801         this.prev =  this.navgroup.addItem({
24802             tooltip: this.prevText,
24803             cls: "prev btn-outline-secondary",
24804             html : ' <i class="fa fa-backward"></i>',
24805             disabled: true,
24806             preventDefault: true,
24807             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24808         });
24809     //this.addSeparator();
24810         
24811         
24812         var field = this.navgroup.addItem( {
24813             tagtype : 'span',
24814             cls : 'x-paging-position  btn-outline-secondary',
24815              disabled: true,
24816             html : this.beforePageText  +
24817                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24818                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24819          } ); //?? escaped?
24820         
24821         this.field = field.el.select('input', true).first();
24822         this.field.on("keydown", this.onPagingKeydown, this);
24823         this.field.on("focus", function(){this.dom.select();});
24824     
24825     
24826         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24827         //this.field.setHeight(18);
24828         //this.addSeparator();
24829         this.next = this.navgroup.addItem({
24830             tooltip: this.nextText,
24831             cls: "next btn-outline-secondary",
24832             html : ' <i class="fa fa-forward"></i>',
24833             disabled: true,
24834             preventDefault: true,
24835             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24836         });
24837         this.last = this.navgroup.addItem({
24838             tooltip: this.lastText,
24839             html : ' <i class="fa fa-step-forward"></i>',
24840             cls: "next btn-outline-secondary",
24841             disabled: true,
24842             preventDefault: true,
24843             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24844         });
24845     //this.addSeparator();
24846         this.loading = this.navgroup.addItem({
24847             tooltip: this.refreshText,
24848             cls: "btn-outline-secondary",
24849             html : ' <i class="fa fa-refresh"></i>',
24850             preventDefault: true,
24851             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24852         });
24853         
24854     },
24855
24856     // private
24857     updateInfo : function(){
24858         if(this.displayEl){
24859             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24860             var msg = count == 0 ?
24861                 this.emptyMsg :
24862                 String.format(
24863                     this.displayMsg,
24864                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24865                 );
24866             this.displayEl.update(msg);
24867         }
24868     },
24869
24870     // private
24871     onLoad : function(ds, r, o)
24872     {
24873         this.cursor = o.params.start ? o.params.start : 0;
24874         
24875         var d = this.getPageData(),
24876             ap = d.activePage,
24877             ps = d.pages;
24878         
24879         
24880         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24881         this.field.dom.value = ap;
24882         this.first.setDisabled(ap == 1);
24883         this.prev.setDisabled(ap == 1);
24884         this.next.setDisabled(ap == ps);
24885         this.last.setDisabled(ap == ps);
24886         this.loading.enable();
24887         this.updateInfo();
24888     },
24889
24890     // private
24891     getPageData : function(){
24892         var total = this.ds.getTotalCount();
24893         return {
24894             total : total,
24895             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24896             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24897         };
24898     },
24899
24900     // private
24901     onLoadError : function(){
24902         this.loading.enable();
24903     },
24904
24905     // private
24906     onPagingKeydown : function(e){
24907         var k = e.getKey();
24908         var d = this.getPageData();
24909         if(k == e.RETURN){
24910             var v = this.field.dom.value, pageNum;
24911             if(!v || isNaN(pageNum = parseInt(v, 10))){
24912                 this.field.dom.value = d.activePage;
24913                 return;
24914             }
24915             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24916             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24917             e.stopEvent();
24918         }
24919         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))
24920         {
24921           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24922           this.field.dom.value = pageNum;
24923           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24924           e.stopEvent();
24925         }
24926         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24927         {
24928           var v = this.field.dom.value, pageNum; 
24929           var increment = (e.shiftKey) ? 10 : 1;
24930           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24931                 increment *= -1;
24932           }
24933           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24934             this.field.dom.value = d.activePage;
24935             return;
24936           }
24937           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24938           {
24939             this.field.dom.value = parseInt(v, 10) + increment;
24940             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24941             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24942           }
24943           e.stopEvent();
24944         }
24945     },
24946
24947     // private
24948     beforeLoad : function(){
24949         if(this.loading){
24950             this.loading.disable();
24951         }
24952     },
24953
24954     // private
24955     onClick : function(which){
24956         
24957         var ds = this.ds;
24958         if (!ds) {
24959             return;
24960         }
24961         
24962         switch(which){
24963             case "first":
24964                 ds.load({params:{start: 0, limit: this.pageSize}});
24965             break;
24966             case "prev":
24967                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24968             break;
24969             case "next":
24970                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24971             break;
24972             case "last":
24973                 var total = ds.getTotalCount();
24974                 var extra = total % this.pageSize;
24975                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24976                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24977             break;
24978             case "refresh":
24979                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24980             break;
24981         }
24982     },
24983
24984     /**
24985      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24986      * @param {Roo.data.Store} store The data store to unbind
24987      */
24988     unbind : function(ds){
24989         ds.un("beforeload", this.beforeLoad, this);
24990         ds.un("load", this.onLoad, this);
24991         ds.un("loadexception", this.onLoadError, this);
24992         ds.un("remove", this.updateInfo, this);
24993         ds.un("add", this.updateInfo, this);
24994         this.ds = undefined;
24995     },
24996
24997     /**
24998      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24999      * @param {Roo.data.Store} store The data store to bind
25000      */
25001     bind : function(ds){
25002         ds.on("beforeload", this.beforeLoad, this);
25003         ds.on("load", this.onLoad, this);
25004         ds.on("loadexception", this.onLoadError, this);
25005         ds.on("remove", this.updateInfo, this);
25006         ds.on("add", this.updateInfo, this);
25007         this.ds = ds;
25008     }
25009 });/*
25010  * - LGPL
25011  *
25012  * element
25013  * 
25014  */
25015
25016 /**
25017  * @class Roo.bootstrap.MessageBar
25018  * @extends Roo.bootstrap.Component
25019  * Bootstrap MessageBar class
25020  * @cfg {String} html contents of the MessageBar
25021  * @cfg {String} weight (info | success | warning | danger) default info
25022  * @cfg {String} beforeClass insert the bar before the given class
25023  * @cfg {Boolean} closable (true | false) default false
25024  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25025  * 
25026  * @constructor
25027  * Create a new Element
25028  * @param {Object} config The config object
25029  */
25030
25031 Roo.bootstrap.MessageBar = function(config){
25032     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25033 };
25034
25035 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25036     
25037     html: '',
25038     weight: 'info',
25039     closable: false,
25040     fixed: false,
25041     beforeClass: 'bootstrap-sticky-wrap',
25042     
25043     getAutoCreate : function(){
25044         
25045         var cfg = {
25046             tag: 'div',
25047             cls: 'alert alert-dismissable alert-' + this.weight,
25048             cn: [
25049                 {
25050                     tag: 'span',
25051                     cls: 'message',
25052                     html: this.html || ''
25053                 }
25054             ]
25055         };
25056         
25057         if(this.fixed){
25058             cfg.cls += ' alert-messages-fixed';
25059         }
25060         
25061         if(this.closable){
25062             cfg.cn.push({
25063                 tag: 'button',
25064                 cls: 'close',
25065                 html: 'x'
25066             });
25067         }
25068         
25069         return cfg;
25070     },
25071     
25072     onRender : function(ct, position)
25073     {
25074         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25075         
25076         if(!this.el){
25077             var cfg = Roo.apply({},  this.getAutoCreate());
25078             cfg.id = Roo.id();
25079             
25080             if (this.cls) {
25081                 cfg.cls += ' ' + this.cls;
25082             }
25083             if (this.style) {
25084                 cfg.style = this.style;
25085             }
25086             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25087             
25088             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25089         }
25090         
25091         this.el.select('>button.close').on('click', this.hide, this);
25092         
25093     },
25094     
25095     show : function()
25096     {
25097         if (!this.rendered) {
25098             this.render();
25099         }
25100         
25101         this.el.show();
25102         
25103         this.fireEvent('show', this);
25104         
25105     },
25106     
25107     hide : function()
25108     {
25109         if (!this.rendered) {
25110             this.render();
25111         }
25112         
25113         this.el.hide();
25114         
25115         this.fireEvent('hide', this);
25116     },
25117     
25118     update : function()
25119     {
25120 //        var e = this.el.dom.firstChild;
25121 //        
25122 //        if(this.closable){
25123 //            e = e.nextSibling;
25124 //        }
25125 //        
25126 //        e.data = this.html || '';
25127
25128         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25129     }
25130    
25131 });
25132
25133  
25134
25135      /*
25136  * - LGPL
25137  *
25138  * Graph
25139  * 
25140  */
25141
25142
25143 /**
25144  * @class Roo.bootstrap.Graph
25145  * @extends Roo.bootstrap.Component
25146  * Bootstrap Graph class
25147 > Prameters
25148  -sm {number} sm 4
25149  -md {number} md 5
25150  @cfg {String} graphtype  bar | vbar | pie
25151  @cfg {number} g_x coodinator | centre x (pie)
25152  @cfg {number} g_y coodinator | centre y (pie)
25153  @cfg {number} g_r radius (pie)
25154  @cfg {number} g_height height of the chart (respected by all elements in the set)
25155  @cfg {number} g_width width of the chart (respected by all elements in the set)
25156  @cfg {Object} title The title of the chart
25157     
25158  -{Array}  values
25159  -opts (object) options for the chart 
25160      o {
25161      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25162      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25163      o vgutter (number)
25164      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.
25165      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25166      o to
25167      o stretch (boolean)
25168      o }
25169  -opts (object) options for the pie
25170      o{
25171      o cut
25172      o startAngle (number)
25173      o endAngle (number)
25174      } 
25175  *
25176  * @constructor
25177  * Create a new Input
25178  * @param {Object} config The config object
25179  */
25180
25181 Roo.bootstrap.Graph = function(config){
25182     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25183     
25184     this.addEvents({
25185         // img events
25186         /**
25187          * @event click
25188          * The img click event for the img.
25189          * @param {Roo.EventObject} e
25190          */
25191         "click" : true
25192     });
25193 };
25194
25195 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25196     
25197     sm: 4,
25198     md: 5,
25199     graphtype: 'bar',
25200     g_height: 250,
25201     g_width: 400,
25202     g_x: 50,
25203     g_y: 50,
25204     g_r: 30,
25205     opts:{
25206         //g_colors: this.colors,
25207         g_type: 'soft',
25208         g_gutter: '20%'
25209
25210     },
25211     title : false,
25212
25213     getAutoCreate : function(){
25214         
25215         var cfg = {
25216             tag: 'div',
25217             html : null
25218         };
25219         
25220         
25221         return  cfg;
25222     },
25223
25224     onRender : function(ct,position){
25225         
25226         
25227         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25228         
25229         if (typeof(Raphael) == 'undefined') {
25230             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25231             return;
25232         }
25233         
25234         this.raphael = Raphael(this.el.dom);
25235         
25236                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25237                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25238                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25239                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25240                 /*
25241                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25242                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25243                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25244                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25245                 
25246                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25247                 r.barchart(330, 10, 300, 220, data1);
25248                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25249                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25250                 */
25251                 
25252                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25253                 // r.barchart(30, 30, 560, 250,  xdata, {
25254                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25255                 //     axis : "0 0 1 1",
25256                 //     axisxlabels :  xdata
25257                 //     //yvalues : cols,
25258                    
25259                 // });
25260 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25261 //        
25262 //        this.load(null,xdata,{
25263 //                axis : "0 0 1 1",
25264 //                axisxlabels :  xdata
25265 //                });
25266
25267     },
25268
25269     load : function(graphtype,xdata,opts)
25270     {
25271         this.raphael.clear();
25272         if(!graphtype) {
25273             graphtype = this.graphtype;
25274         }
25275         if(!opts){
25276             opts = this.opts;
25277         }
25278         var r = this.raphael,
25279             fin = function () {
25280                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25281             },
25282             fout = function () {
25283                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25284             },
25285             pfin = function() {
25286                 this.sector.stop();
25287                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25288
25289                 if (this.label) {
25290                     this.label[0].stop();
25291                     this.label[0].attr({ r: 7.5 });
25292                     this.label[1].attr({ "font-weight": 800 });
25293                 }
25294             },
25295             pfout = function() {
25296                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25297
25298                 if (this.label) {
25299                     this.label[0].animate({ r: 5 }, 500, "bounce");
25300                     this.label[1].attr({ "font-weight": 400 });
25301                 }
25302             };
25303
25304         switch(graphtype){
25305             case 'bar':
25306                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25307                 break;
25308             case 'hbar':
25309                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25310                 break;
25311             case 'pie':
25312 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25313 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25314 //            
25315                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25316                 
25317                 break;
25318
25319         }
25320         
25321         if(this.title){
25322             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25323         }
25324         
25325     },
25326     
25327     setTitle: function(o)
25328     {
25329         this.title = o;
25330     },
25331     
25332     initEvents: function() {
25333         
25334         if(!this.href){
25335             this.el.on('click', this.onClick, this);
25336         }
25337     },
25338     
25339     onClick : function(e)
25340     {
25341         Roo.log('img onclick');
25342         this.fireEvent('click', this, e);
25343     }
25344    
25345 });
25346
25347  
25348 /*
25349  * - LGPL
25350  *
25351  * numberBox
25352  * 
25353  */
25354 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25355
25356 /**
25357  * @class Roo.bootstrap.dash.NumberBox
25358  * @extends Roo.bootstrap.Component
25359  * Bootstrap NumberBox class
25360  * @cfg {String} headline Box headline
25361  * @cfg {String} content Box content
25362  * @cfg {String} icon Box icon
25363  * @cfg {String} footer Footer text
25364  * @cfg {String} fhref Footer href
25365  * 
25366  * @constructor
25367  * Create a new NumberBox
25368  * @param {Object} config The config object
25369  */
25370
25371
25372 Roo.bootstrap.dash.NumberBox = function(config){
25373     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25374     
25375 };
25376
25377 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25378     
25379     headline : '',
25380     content : '',
25381     icon : '',
25382     footer : '',
25383     fhref : '',
25384     ficon : '',
25385     
25386     getAutoCreate : function(){
25387         
25388         var cfg = {
25389             tag : 'div',
25390             cls : 'small-box ',
25391             cn : [
25392                 {
25393                     tag : 'div',
25394                     cls : 'inner',
25395                     cn :[
25396                         {
25397                             tag : 'h3',
25398                             cls : 'roo-headline',
25399                             html : this.headline
25400                         },
25401                         {
25402                             tag : 'p',
25403                             cls : 'roo-content',
25404                             html : this.content
25405                         }
25406                     ]
25407                 }
25408             ]
25409         };
25410         
25411         if(this.icon){
25412             cfg.cn.push({
25413                 tag : 'div',
25414                 cls : 'icon',
25415                 cn :[
25416                     {
25417                         tag : 'i',
25418                         cls : 'ion ' + this.icon
25419                     }
25420                 ]
25421             });
25422         }
25423         
25424         if(this.footer){
25425             var footer = {
25426                 tag : 'a',
25427                 cls : 'small-box-footer',
25428                 href : this.fhref || '#',
25429                 html : this.footer
25430             };
25431             
25432             cfg.cn.push(footer);
25433             
25434         }
25435         
25436         return  cfg;
25437     },
25438
25439     onRender : function(ct,position){
25440         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25441
25442
25443        
25444                 
25445     },
25446
25447     setHeadline: function (value)
25448     {
25449         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25450     },
25451     
25452     setFooter: function (value, href)
25453     {
25454         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25455         
25456         if(href){
25457             this.el.select('a.small-box-footer',true).first().attr('href', href);
25458         }
25459         
25460     },
25461
25462     setContent: function (value)
25463     {
25464         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25465     },
25466
25467     initEvents: function() 
25468     {   
25469         
25470     }
25471     
25472 });
25473
25474  
25475 /*
25476  * - LGPL
25477  *
25478  * TabBox
25479  * 
25480  */
25481 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25482
25483 /**
25484  * @class Roo.bootstrap.dash.TabBox
25485  * @extends Roo.bootstrap.Component
25486  * Bootstrap TabBox class
25487  * @cfg {String} title Title of the TabBox
25488  * @cfg {String} icon Icon of the TabBox
25489  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25490  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25491  * 
25492  * @constructor
25493  * Create a new TabBox
25494  * @param {Object} config The config object
25495  */
25496
25497
25498 Roo.bootstrap.dash.TabBox = function(config){
25499     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25500     this.addEvents({
25501         // raw events
25502         /**
25503          * @event addpane
25504          * When a pane is added
25505          * @param {Roo.bootstrap.dash.TabPane} pane
25506          */
25507         "addpane" : true,
25508         /**
25509          * @event activatepane
25510          * When a pane is activated
25511          * @param {Roo.bootstrap.dash.TabPane} pane
25512          */
25513         "activatepane" : true
25514         
25515          
25516     });
25517     
25518     this.panes = [];
25519 };
25520
25521 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25522
25523     title : '',
25524     icon : false,
25525     showtabs : true,
25526     tabScrollable : false,
25527     
25528     getChildContainer : function()
25529     {
25530         return this.el.select('.tab-content', true).first();
25531     },
25532     
25533     getAutoCreate : function(){
25534         
25535         var header = {
25536             tag: 'li',
25537             cls: 'pull-left header',
25538             html: this.title,
25539             cn : []
25540         };
25541         
25542         if(this.icon){
25543             header.cn.push({
25544                 tag: 'i',
25545                 cls: 'fa ' + this.icon
25546             });
25547         }
25548         
25549         var h = {
25550             tag: 'ul',
25551             cls: 'nav nav-tabs pull-right',
25552             cn: [
25553                 header
25554             ]
25555         };
25556         
25557         if(this.tabScrollable){
25558             h = {
25559                 tag: 'div',
25560                 cls: 'tab-header',
25561                 cn: [
25562                     {
25563                         tag: 'ul',
25564                         cls: 'nav nav-tabs pull-right',
25565                         cn: [
25566                             header
25567                         ]
25568                     }
25569                 ]
25570             };
25571         }
25572         
25573         var cfg = {
25574             tag: 'div',
25575             cls: 'nav-tabs-custom',
25576             cn: [
25577                 h,
25578                 {
25579                     tag: 'div',
25580                     cls: 'tab-content no-padding',
25581                     cn: []
25582                 }
25583             ]
25584         };
25585
25586         return  cfg;
25587     },
25588     initEvents : function()
25589     {
25590         //Roo.log('add add pane handler');
25591         this.on('addpane', this.onAddPane, this);
25592     },
25593      /**
25594      * Updates the box title
25595      * @param {String} html to set the title to.
25596      */
25597     setTitle : function(value)
25598     {
25599         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25600     },
25601     onAddPane : function(pane)
25602     {
25603         this.panes.push(pane);
25604         //Roo.log('addpane');
25605         //Roo.log(pane);
25606         // tabs are rendere left to right..
25607         if(!this.showtabs){
25608             return;
25609         }
25610         
25611         var ctr = this.el.select('.nav-tabs', true).first();
25612          
25613          
25614         var existing = ctr.select('.nav-tab',true);
25615         var qty = existing.getCount();;
25616         
25617         
25618         var tab = ctr.createChild({
25619             tag : 'li',
25620             cls : 'nav-tab' + (qty ? '' : ' active'),
25621             cn : [
25622                 {
25623                     tag : 'a',
25624                     href:'#',
25625                     html : pane.title
25626                 }
25627             ]
25628         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25629         pane.tab = tab;
25630         
25631         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25632         if (!qty) {
25633             pane.el.addClass('active');
25634         }
25635         
25636                 
25637     },
25638     onTabClick : function(ev,un,ob,pane)
25639     {
25640         //Roo.log('tab - prev default');
25641         ev.preventDefault();
25642         
25643         
25644         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25645         pane.tab.addClass('active');
25646         //Roo.log(pane.title);
25647         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25648         // technically we should have a deactivate event.. but maybe add later.
25649         // and it should not de-activate the selected tab...
25650         this.fireEvent('activatepane', pane);
25651         pane.el.addClass('active');
25652         pane.fireEvent('activate');
25653         
25654         
25655     },
25656     
25657     getActivePane : function()
25658     {
25659         var r = false;
25660         Roo.each(this.panes, function(p) {
25661             if(p.el.hasClass('active')){
25662                 r = p;
25663                 return false;
25664             }
25665             
25666             return;
25667         });
25668         
25669         return r;
25670     }
25671     
25672     
25673 });
25674
25675  
25676 /*
25677  * - LGPL
25678  *
25679  * Tab pane
25680  * 
25681  */
25682 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25683 /**
25684  * @class Roo.bootstrap.TabPane
25685  * @extends Roo.bootstrap.Component
25686  * Bootstrap TabPane class
25687  * @cfg {Boolean} active (false | true) Default false
25688  * @cfg {String} title title of panel
25689
25690  * 
25691  * @constructor
25692  * Create a new TabPane
25693  * @param {Object} config The config object
25694  */
25695
25696 Roo.bootstrap.dash.TabPane = function(config){
25697     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25698     
25699     this.addEvents({
25700         // raw events
25701         /**
25702          * @event activate
25703          * When a pane is activated
25704          * @param {Roo.bootstrap.dash.TabPane} pane
25705          */
25706         "activate" : true
25707          
25708     });
25709 };
25710
25711 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25712     
25713     active : false,
25714     title : '',
25715     
25716     // the tabBox that this is attached to.
25717     tab : false,
25718      
25719     getAutoCreate : function() 
25720     {
25721         var cfg = {
25722             tag: 'div',
25723             cls: 'tab-pane'
25724         };
25725         
25726         if(this.active){
25727             cfg.cls += ' active';
25728         }
25729         
25730         return cfg;
25731     },
25732     initEvents  : function()
25733     {
25734         //Roo.log('trigger add pane handler');
25735         this.parent().fireEvent('addpane', this)
25736     },
25737     
25738      /**
25739      * Updates the tab title 
25740      * @param {String} html to set the title to.
25741      */
25742     setTitle: function(str)
25743     {
25744         if (!this.tab) {
25745             return;
25746         }
25747         this.title = str;
25748         this.tab.select('a', true).first().dom.innerHTML = str;
25749         
25750     }
25751     
25752     
25753     
25754 });
25755
25756  
25757
25758
25759  /*
25760  * - LGPL
25761  *
25762  * menu
25763  * 
25764  */
25765 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25766
25767 /**
25768  * @class Roo.bootstrap.menu.Menu
25769  * @extends Roo.bootstrap.Component
25770  * Bootstrap Menu class - container for Menu
25771  * @cfg {String} html Text of the menu
25772  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25773  * @cfg {String} icon Font awesome icon
25774  * @cfg {String} pos Menu align to (top | bottom) default bottom
25775  * 
25776  * 
25777  * @constructor
25778  * Create a new Menu
25779  * @param {Object} config The config object
25780  */
25781
25782
25783 Roo.bootstrap.menu.Menu = function(config){
25784     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25785     
25786     this.addEvents({
25787         /**
25788          * @event beforeshow
25789          * Fires before this menu is displayed
25790          * @param {Roo.bootstrap.menu.Menu} this
25791          */
25792         beforeshow : true,
25793         /**
25794          * @event beforehide
25795          * Fires before this menu is hidden
25796          * @param {Roo.bootstrap.menu.Menu} this
25797          */
25798         beforehide : true,
25799         /**
25800          * @event show
25801          * Fires after this menu is displayed
25802          * @param {Roo.bootstrap.menu.Menu} this
25803          */
25804         show : true,
25805         /**
25806          * @event hide
25807          * Fires after this menu is hidden
25808          * @param {Roo.bootstrap.menu.Menu} this
25809          */
25810         hide : true,
25811         /**
25812          * @event click
25813          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25814          * @param {Roo.bootstrap.menu.Menu} this
25815          * @param {Roo.EventObject} e
25816          */
25817         click : true
25818     });
25819     
25820 };
25821
25822 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25823     
25824     submenu : false,
25825     html : '',
25826     weight : 'default',
25827     icon : false,
25828     pos : 'bottom',
25829     
25830     
25831     getChildContainer : function() {
25832         if(this.isSubMenu){
25833             return this.el;
25834         }
25835         
25836         return this.el.select('ul.dropdown-menu', true).first();  
25837     },
25838     
25839     getAutoCreate : function()
25840     {
25841         var text = [
25842             {
25843                 tag : 'span',
25844                 cls : 'roo-menu-text',
25845                 html : this.html
25846             }
25847         ];
25848         
25849         if(this.icon){
25850             text.unshift({
25851                 tag : 'i',
25852                 cls : 'fa ' + this.icon
25853             })
25854         }
25855         
25856         
25857         var cfg = {
25858             tag : 'div',
25859             cls : 'btn-group',
25860             cn : [
25861                 {
25862                     tag : 'button',
25863                     cls : 'dropdown-button btn btn-' + this.weight,
25864                     cn : text
25865                 },
25866                 {
25867                     tag : 'button',
25868                     cls : 'dropdown-toggle btn btn-' + this.weight,
25869                     cn : [
25870                         {
25871                             tag : 'span',
25872                             cls : 'caret'
25873                         }
25874                     ]
25875                 },
25876                 {
25877                     tag : 'ul',
25878                     cls : 'dropdown-menu'
25879                 }
25880             ]
25881             
25882         };
25883         
25884         if(this.pos == 'top'){
25885             cfg.cls += ' dropup';
25886         }
25887         
25888         if(this.isSubMenu){
25889             cfg = {
25890                 tag : 'ul',
25891                 cls : 'dropdown-menu'
25892             }
25893         }
25894         
25895         return cfg;
25896     },
25897     
25898     onRender : function(ct, position)
25899     {
25900         this.isSubMenu = ct.hasClass('dropdown-submenu');
25901         
25902         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25903     },
25904     
25905     initEvents : function() 
25906     {
25907         if(this.isSubMenu){
25908             return;
25909         }
25910         
25911         this.hidden = true;
25912         
25913         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25914         this.triggerEl.on('click', this.onTriggerPress, this);
25915         
25916         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25917         this.buttonEl.on('click', this.onClick, this);
25918         
25919     },
25920     
25921     list : function()
25922     {
25923         if(this.isSubMenu){
25924             return this.el;
25925         }
25926         
25927         return this.el.select('ul.dropdown-menu', true).first();
25928     },
25929     
25930     onClick : function(e)
25931     {
25932         this.fireEvent("click", this, e);
25933     },
25934     
25935     onTriggerPress  : function(e)
25936     {   
25937         if (this.isVisible()) {
25938             this.hide();
25939         } else {
25940             this.show();
25941         }
25942     },
25943     
25944     isVisible : function(){
25945         return !this.hidden;
25946     },
25947     
25948     show : function()
25949     {
25950         this.fireEvent("beforeshow", this);
25951         
25952         this.hidden = false;
25953         this.el.addClass('open');
25954         
25955         Roo.get(document).on("mouseup", this.onMouseUp, this);
25956         
25957         this.fireEvent("show", this);
25958         
25959         
25960     },
25961     
25962     hide : function()
25963     {
25964         this.fireEvent("beforehide", this);
25965         
25966         this.hidden = true;
25967         this.el.removeClass('open');
25968         
25969         Roo.get(document).un("mouseup", this.onMouseUp);
25970         
25971         this.fireEvent("hide", this);
25972     },
25973     
25974     onMouseUp : function()
25975     {
25976         this.hide();
25977     }
25978     
25979 });
25980
25981  
25982  /*
25983  * - LGPL
25984  *
25985  * menu item
25986  * 
25987  */
25988 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25989
25990 /**
25991  * @class Roo.bootstrap.menu.Item
25992  * @extends Roo.bootstrap.Component
25993  * Bootstrap MenuItem class
25994  * @cfg {Boolean} submenu (true | false) default false
25995  * @cfg {String} html text of the item
25996  * @cfg {String} href the link
25997  * @cfg {Boolean} disable (true | false) default false
25998  * @cfg {Boolean} preventDefault (true | false) default true
25999  * @cfg {String} icon Font awesome icon
26000  * @cfg {String} pos Submenu align to (left | right) default right 
26001  * 
26002  * 
26003  * @constructor
26004  * Create a new Item
26005  * @param {Object} config The config object
26006  */
26007
26008
26009 Roo.bootstrap.menu.Item = function(config){
26010     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26011     this.addEvents({
26012         /**
26013          * @event mouseover
26014          * Fires when the mouse is hovering over this menu
26015          * @param {Roo.bootstrap.menu.Item} this
26016          * @param {Roo.EventObject} e
26017          */
26018         mouseover : true,
26019         /**
26020          * @event mouseout
26021          * Fires when the mouse exits this menu
26022          * @param {Roo.bootstrap.menu.Item} this
26023          * @param {Roo.EventObject} e
26024          */
26025         mouseout : true,
26026         // raw events
26027         /**
26028          * @event click
26029          * The raw click event for the entire grid.
26030          * @param {Roo.EventObject} e
26031          */
26032         click : true
26033     });
26034 };
26035
26036 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26037     
26038     submenu : false,
26039     href : '',
26040     html : '',
26041     preventDefault: true,
26042     disable : false,
26043     icon : false,
26044     pos : 'right',
26045     
26046     getAutoCreate : function()
26047     {
26048         var text = [
26049             {
26050                 tag : 'span',
26051                 cls : 'roo-menu-item-text',
26052                 html : this.html
26053             }
26054         ];
26055         
26056         if(this.icon){
26057             text.unshift({
26058                 tag : 'i',
26059                 cls : 'fa ' + this.icon
26060             })
26061         }
26062         
26063         var cfg = {
26064             tag : 'li',
26065             cn : [
26066                 {
26067                     tag : 'a',
26068                     href : this.href || '#',
26069                     cn : text
26070                 }
26071             ]
26072         };
26073         
26074         if(this.disable){
26075             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26076         }
26077         
26078         if(this.submenu){
26079             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26080             
26081             if(this.pos == 'left'){
26082                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26083             }
26084         }
26085         
26086         return cfg;
26087     },
26088     
26089     initEvents : function() 
26090     {
26091         this.el.on('mouseover', this.onMouseOver, this);
26092         this.el.on('mouseout', this.onMouseOut, this);
26093         
26094         this.el.select('a', true).first().on('click', this.onClick, this);
26095         
26096     },
26097     
26098     onClick : function(e)
26099     {
26100         if(this.preventDefault){
26101             e.preventDefault();
26102         }
26103         
26104         this.fireEvent("click", this, e);
26105     },
26106     
26107     onMouseOver : function(e)
26108     {
26109         if(this.submenu && this.pos == 'left'){
26110             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26111         }
26112         
26113         this.fireEvent("mouseover", this, e);
26114     },
26115     
26116     onMouseOut : function(e)
26117     {
26118         this.fireEvent("mouseout", this, e);
26119     }
26120 });
26121
26122  
26123
26124  /*
26125  * - LGPL
26126  *
26127  * menu separator
26128  * 
26129  */
26130 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26131
26132 /**
26133  * @class Roo.bootstrap.menu.Separator
26134  * @extends Roo.bootstrap.Component
26135  * Bootstrap Separator class
26136  * 
26137  * @constructor
26138  * Create a new Separator
26139  * @param {Object} config The config object
26140  */
26141
26142
26143 Roo.bootstrap.menu.Separator = function(config){
26144     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26145 };
26146
26147 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26148     
26149     getAutoCreate : function(){
26150         var cfg = {
26151             tag : 'li',
26152             cls: 'divider'
26153         };
26154         
26155         return cfg;
26156     }
26157    
26158 });
26159
26160  
26161
26162  /*
26163  * - LGPL
26164  *
26165  * Tooltip
26166  * 
26167  */
26168
26169 /**
26170  * @class Roo.bootstrap.Tooltip
26171  * Bootstrap Tooltip class
26172  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26173  * to determine which dom element triggers the tooltip.
26174  * 
26175  * It needs to add support for additional attributes like tooltip-position
26176  * 
26177  * @constructor
26178  * Create a new Toolti
26179  * @param {Object} config The config object
26180  */
26181
26182 Roo.bootstrap.Tooltip = function(config){
26183     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26184     
26185     this.alignment = Roo.bootstrap.Tooltip.alignment;
26186     
26187     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26188         this.alignment = config.alignment;
26189     }
26190     
26191 };
26192
26193 Roo.apply(Roo.bootstrap.Tooltip, {
26194     /**
26195      * @function init initialize tooltip monitoring.
26196      * @static
26197      */
26198     currentEl : false,
26199     currentTip : false,
26200     currentRegion : false,
26201     
26202     //  init : delay?
26203     
26204     init : function()
26205     {
26206         Roo.get(document).on('mouseover', this.enter ,this);
26207         Roo.get(document).on('mouseout', this.leave, this);
26208          
26209         
26210         this.currentTip = new Roo.bootstrap.Tooltip();
26211     },
26212     
26213     enter : function(ev)
26214     {
26215         var dom = ev.getTarget();
26216         
26217         //Roo.log(['enter',dom]);
26218         var el = Roo.fly(dom);
26219         if (this.currentEl) {
26220             //Roo.log(dom);
26221             //Roo.log(this.currentEl);
26222             //Roo.log(this.currentEl.contains(dom));
26223             if (this.currentEl == el) {
26224                 return;
26225             }
26226             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26227                 return;
26228             }
26229
26230         }
26231         
26232         if (this.currentTip.el) {
26233             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26234         }    
26235         //Roo.log(ev);
26236         
26237         if(!el || el.dom == document){
26238             return;
26239         }
26240         
26241         var bindEl = el;
26242         
26243         // you can not look for children, as if el is the body.. then everythign is the child..
26244         if (!el.attr('tooltip')) { //
26245             if (!el.select("[tooltip]").elements.length) {
26246                 return;
26247             }
26248             // is the mouse over this child...?
26249             bindEl = el.select("[tooltip]").first();
26250             var xy = ev.getXY();
26251             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26252                 //Roo.log("not in region.");
26253                 return;
26254             }
26255             //Roo.log("child element over..");
26256             
26257         }
26258         this.currentEl = bindEl;
26259         this.currentTip.bind(bindEl);
26260         this.currentRegion = Roo.lib.Region.getRegion(dom);
26261         this.currentTip.enter();
26262         
26263     },
26264     leave : function(ev)
26265     {
26266         var dom = ev.getTarget();
26267         //Roo.log(['leave',dom]);
26268         if (!this.currentEl) {
26269             return;
26270         }
26271         
26272         
26273         if (dom != this.currentEl.dom) {
26274             return;
26275         }
26276         var xy = ev.getXY();
26277         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26278             return;
26279         }
26280         // only activate leave if mouse cursor is outside... bounding box..
26281         
26282         
26283         
26284         
26285         if (this.currentTip) {
26286             this.currentTip.leave();
26287         }
26288         //Roo.log('clear currentEl');
26289         this.currentEl = false;
26290         
26291         
26292     },
26293     alignment : {
26294         'left' : ['r-l', [-2,0], 'right'],
26295         'right' : ['l-r', [2,0], 'left'],
26296         'bottom' : ['t-b', [0,2], 'top'],
26297         'top' : [ 'b-t', [0,-2], 'bottom']
26298     }
26299     
26300 });
26301
26302
26303 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26304     
26305     
26306     bindEl : false,
26307     
26308     delay : null, // can be { show : 300 , hide: 500}
26309     
26310     timeout : null,
26311     
26312     hoverState : null, //???
26313     
26314     placement : 'bottom', 
26315     
26316     alignment : false,
26317     
26318     getAutoCreate : function(){
26319     
26320         var cfg = {
26321            cls : 'tooltip',
26322            role : 'tooltip',
26323            cn : [
26324                 {
26325                     cls : 'tooltip-arrow'
26326                 },
26327                 {
26328                     cls : 'tooltip-inner'
26329                 }
26330            ]
26331         };
26332         
26333         return cfg;
26334     },
26335     bind : function(el)
26336     {
26337         this.bindEl = el;
26338     },
26339       
26340     
26341     enter : function () {
26342        
26343         if (this.timeout != null) {
26344             clearTimeout(this.timeout);
26345         }
26346         
26347         this.hoverState = 'in';
26348          //Roo.log("enter - show");
26349         if (!this.delay || !this.delay.show) {
26350             this.show();
26351             return;
26352         }
26353         var _t = this;
26354         this.timeout = setTimeout(function () {
26355             if (_t.hoverState == 'in') {
26356                 _t.show();
26357             }
26358         }, this.delay.show);
26359     },
26360     leave : function()
26361     {
26362         clearTimeout(this.timeout);
26363     
26364         this.hoverState = 'out';
26365          if (!this.delay || !this.delay.hide) {
26366             this.hide();
26367             return;
26368         }
26369        
26370         var _t = this;
26371         this.timeout = setTimeout(function () {
26372             //Roo.log("leave - timeout");
26373             
26374             if (_t.hoverState == 'out') {
26375                 _t.hide();
26376                 Roo.bootstrap.Tooltip.currentEl = false;
26377             }
26378         }, delay);
26379     },
26380     
26381     show : function (msg)
26382     {
26383         if (!this.el) {
26384             this.render(document.body);
26385         }
26386         // set content.
26387         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26388         
26389         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26390         
26391         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26392         
26393         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26394         
26395         var placement = typeof this.placement == 'function' ?
26396             this.placement.call(this, this.el, on_el) :
26397             this.placement;
26398             
26399         var autoToken = /\s?auto?\s?/i;
26400         var autoPlace = autoToken.test(placement);
26401         if (autoPlace) {
26402             placement = placement.replace(autoToken, '') || 'top';
26403         }
26404         
26405         //this.el.detach()
26406         //this.el.setXY([0,0]);
26407         this.el.show();
26408         //this.el.dom.style.display='block';
26409         
26410         //this.el.appendTo(on_el);
26411         
26412         var p = this.getPosition();
26413         var box = this.el.getBox();
26414         
26415         if (autoPlace) {
26416             // fixme..
26417         }
26418         
26419         var align = this.alignment[placement];
26420         
26421         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26422         
26423         if(placement == 'top' || placement == 'bottom'){
26424             if(xy[0] < 0){
26425                 placement = 'right';
26426             }
26427             
26428             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26429                 placement = 'left';
26430             }
26431             
26432             var scroll = Roo.select('body', true).first().getScroll();
26433             
26434             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26435                 placement = 'top';
26436             }
26437             
26438             align = this.alignment[placement];
26439         }
26440         
26441         this.el.alignTo(this.bindEl, align[0],align[1]);
26442         //var arrow = this.el.select('.arrow',true).first();
26443         //arrow.set(align[2], 
26444         
26445         this.el.addClass(placement);
26446         
26447         this.el.addClass('in fade');
26448         
26449         this.hoverState = null;
26450         
26451         if (this.el.hasClass('fade')) {
26452             // fade it?
26453         }
26454         
26455     },
26456     hide : function()
26457     {
26458          
26459         if (!this.el) {
26460             return;
26461         }
26462         //this.el.setXY([0,0]);
26463         this.el.removeClass('in');
26464         //this.el.hide();
26465         
26466     }
26467     
26468 });
26469  
26470
26471  /*
26472  * - LGPL
26473  *
26474  * Location Picker
26475  * 
26476  */
26477
26478 /**
26479  * @class Roo.bootstrap.LocationPicker
26480  * @extends Roo.bootstrap.Component
26481  * Bootstrap LocationPicker class
26482  * @cfg {Number} latitude Position when init default 0
26483  * @cfg {Number} longitude Position when init default 0
26484  * @cfg {Number} zoom default 15
26485  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26486  * @cfg {Boolean} mapTypeControl default false
26487  * @cfg {Boolean} disableDoubleClickZoom default false
26488  * @cfg {Boolean} scrollwheel default true
26489  * @cfg {Boolean} streetViewControl default false
26490  * @cfg {Number} radius default 0
26491  * @cfg {String} locationName
26492  * @cfg {Boolean} draggable default true
26493  * @cfg {Boolean} enableAutocomplete default false
26494  * @cfg {Boolean} enableReverseGeocode default true
26495  * @cfg {String} markerTitle
26496  * 
26497  * @constructor
26498  * Create a new LocationPicker
26499  * @param {Object} config The config object
26500  */
26501
26502
26503 Roo.bootstrap.LocationPicker = function(config){
26504     
26505     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26506     
26507     this.addEvents({
26508         /**
26509          * @event initial
26510          * Fires when the picker initialized.
26511          * @param {Roo.bootstrap.LocationPicker} this
26512          * @param {Google Location} location
26513          */
26514         initial : true,
26515         /**
26516          * @event positionchanged
26517          * Fires when the picker position changed.
26518          * @param {Roo.bootstrap.LocationPicker} this
26519          * @param {Google Location} location
26520          */
26521         positionchanged : true,
26522         /**
26523          * @event resize
26524          * Fires when the map resize.
26525          * @param {Roo.bootstrap.LocationPicker} this
26526          */
26527         resize : true,
26528         /**
26529          * @event show
26530          * Fires when the map show.
26531          * @param {Roo.bootstrap.LocationPicker} this
26532          */
26533         show : true,
26534         /**
26535          * @event hide
26536          * Fires when the map hide.
26537          * @param {Roo.bootstrap.LocationPicker} this
26538          */
26539         hide : true,
26540         /**
26541          * @event mapClick
26542          * Fires when click the map.
26543          * @param {Roo.bootstrap.LocationPicker} this
26544          * @param {Map event} e
26545          */
26546         mapClick : true,
26547         /**
26548          * @event mapRightClick
26549          * Fires when right click the map.
26550          * @param {Roo.bootstrap.LocationPicker} this
26551          * @param {Map event} e
26552          */
26553         mapRightClick : true,
26554         /**
26555          * @event markerClick
26556          * Fires when click the marker.
26557          * @param {Roo.bootstrap.LocationPicker} this
26558          * @param {Map event} e
26559          */
26560         markerClick : true,
26561         /**
26562          * @event markerRightClick
26563          * Fires when right click the marker.
26564          * @param {Roo.bootstrap.LocationPicker} this
26565          * @param {Map event} e
26566          */
26567         markerRightClick : true,
26568         /**
26569          * @event OverlayViewDraw
26570          * Fires when OverlayView Draw
26571          * @param {Roo.bootstrap.LocationPicker} this
26572          */
26573         OverlayViewDraw : true,
26574         /**
26575          * @event OverlayViewOnAdd
26576          * Fires when OverlayView Draw
26577          * @param {Roo.bootstrap.LocationPicker} this
26578          */
26579         OverlayViewOnAdd : true,
26580         /**
26581          * @event OverlayViewOnRemove
26582          * Fires when OverlayView Draw
26583          * @param {Roo.bootstrap.LocationPicker} this
26584          */
26585         OverlayViewOnRemove : true,
26586         /**
26587          * @event OverlayViewShow
26588          * Fires when OverlayView Draw
26589          * @param {Roo.bootstrap.LocationPicker} this
26590          * @param {Pixel} cpx
26591          */
26592         OverlayViewShow : true,
26593         /**
26594          * @event OverlayViewHide
26595          * Fires when OverlayView Draw
26596          * @param {Roo.bootstrap.LocationPicker} this
26597          */
26598         OverlayViewHide : true,
26599         /**
26600          * @event loadexception
26601          * Fires when load google lib failed.
26602          * @param {Roo.bootstrap.LocationPicker} this
26603          */
26604         loadexception : true
26605     });
26606         
26607 };
26608
26609 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26610     
26611     gMapContext: false,
26612     
26613     latitude: 0,
26614     longitude: 0,
26615     zoom: 15,
26616     mapTypeId: false,
26617     mapTypeControl: false,
26618     disableDoubleClickZoom: false,
26619     scrollwheel: true,
26620     streetViewControl: false,
26621     radius: 0,
26622     locationName: '',
26623     draggable: true,
26624     enableAutocomplete: false,
26625     enableReverseGeocode: true,
26626     markerTitle: '',
26627     
26628     getAutoCreate: function()
26629     {
26630
26631         var cfg = {
26632             tag: 'div',
26633             cls: 'roo-location-picker'
26634         };
26635         
26636         return cfg
26637     },
26638     
26639     initEvents: function(ct, position)
26640     {       
26641         if(!this.el.getWidth() || this.isApplied()){
26642             return;
26643         }
26644         
26645         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26646         
26647         this.initial();
26648     },
26649     
26650     initial: function()
26651     {
26652         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26653             this.fireEvent('loadexception', this);
26654             return;
26655         }
26656         
26657         if(!this.mapTypeId){
26658             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26659         }
26660         
26661         this.gMapContext = this.GMapContext();
26662         
26663         this.initOverlayView();
26664         
26665         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26666         
26667         var _this = this;
26668                 
26669         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26670             _this.setPosition(_this.gMapContext.marker.position);
26671         });
26672         
26673         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26674             _this.fireEvent('mapClick', this, event);
26675             
26676         });
26677
26678         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26679             _this.fireEvent('mapRightClick', this, event);
26680             
26681         });
26682         
26683         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26684             _this.fireEvent('markerClick', this, event);
26685             
26686         });
26687
26688         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26689             _this.fireEvent('markerRightClick', this, event);
26690             
26691         });
26692         
26693         this.setPosition(this.gMapContext.location);
26694         
26695         this.fireEvent('initial', this, this.gMapContext.location);
26696     },
26697     
26698     initOverlayView: function()
26699     {
26700         var _this = this;
26701         
26702         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26703             
26704             draw: function()
26705             {
26706                 _this.fireEvent('OverlayViewDraw', _this);
26707             },
26708             
26709             onAdd: function()
26710             {
26711                 _this.fireEvent('OverlayViewOnAdd', _this);
26712             },
26713             
26714             onRemove: function()
26715             {
26716                 _this.fireEvent('OverlayViewOnRemove', _this);
26717             },
26718             
26719             show: function(cpx)
26720             {
26721                 _this.fireEvent('OverlayViewShow', _this, cpx);
26722             },
26723             
26724             hide: function()
26725             {
26726                 _this.fireEvent('OverlayViewHide', _this);
26727             }
26728             
26729         });
26730     },
26731     
26732     fromLatLngToContainerPixel: function(event)
26733     {
26734         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26735     },
26736     
26737     isApplied: function() 
26738     {
26739         return this.getGmapContext() == false ? false : true;
26740     },
26741     
26742     getGmapContext: function() 
26743     {
26744         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26745     },
26746     
26747     GMapContext: function() 
26748     {
26749         var position = new google.maps.LatLng(this.latitude, this.longitude);
26750         
26751         var _map = new google.maps.Map(this.el.dom, {
26752             center: position,
26753             zoom: this.zoom,
26754             mapTypeId: this.mapTypeId,
26755             mapTypeControl: this.mapTypeControl,
26756             disableDoubleClickZoom: this.disableDoubleClickZoom,
26757             scrollwheel: this.scrollwheel,
26758             streetViewControl: this.streetViewControl,
26759             locationName: this.locationName,
26760             draggable: this.draggable,
26761             enableAutocomplete: this.enableAutocomplete,
26762             enableReverseGeocode: this.enableReverseGeocode
26763         });
26764         
26765         var _marker = new google.maps.Marker({
26766             position: position,
26767             map: _map,
26768             title: this.markerTitle,
26769             draggable: this.draggable
26770         });
26771         
26772         return {
26773             map: _map,
26774             marker: _marker,
26775             circle: null,
26776             location: position,
26777             radius: this.radius,
26778             locationName: this.locationName,
26779             addressComponents: {
26780                 formatted_address: null,
26781                 addressLine1: null,
26782                 addressLine2: null,
26783                 streetName: null,
26784                 streetNumber: null,
26785                 city: null,
26786                 district: null,
26787                 state: null,
26788                 stateOrProvince: null
26789             },
26790             settings: this,
26791             domContainer: this.el.dom,
26792             geodecoder: new google.maps.Geocoder()
26793         };
26794     },
26795     
26796     drawCircle: function(center, radius, options) 
26797     {
26798         if (this.gMapContext.circle != null) {
26799             this.gMapContext.circle.setMap(null);
26800         }
26801         if (radius > 0) {
26802             radius *= 1;
26803             options = Roo.apply({}, options, {
26804                 strokeColor: "#0000FF",
26805                 strokeOpacity: .35,
26806                 strokeWeight: 2,
26807                 fillColor: "#0000FF",
26808                 fillOpacity: .2
26809             });
26810             
26811             options.map = this.gMapContext.map;
26812             options.radius = radius;
26813             options.center = center;
26814             this.gMapContext.circle = new google.maps.Circle(options);
26815             return this.gMapContext.circle;
26816         }
26817         
26818         return null;
26819     },
26820     
26821     setPosition: function(location) 
26822     {
26823         this.gMapContext.location = location;
26824         this.gMapContext.marker.setPosition(location);
26825         this.gMapContext.map.panTo(location);
26826         this.drawCircle(location, this.gMapContext.radius, {});
26827         
26828         var _this = this;
26829         
26830         if (this.gMapContext.settings.enableReverseGeocode) {
26831             this.gMapContext.geodecoder.geocode({
26832                 latLng: this.gMapContext.location
26833             }, function(results, status) {
26834                 
26835                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26836                     _this.gMapContext.locationName = results[0].formatted_address;
26837                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26838                     
26839                     _this.fireEvent('positionchanged', this, location);
26840                 }
26841             });
26842             
26843             return;
26844         }
26845         
26846         this.fireEvent('positionchanged', this, location);
26847     },
26848     
26849     resize: function()
26850     {
26851         google.maps.event.trigger(this.gMapContext.map, "resize");
26852         
26853         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26854         
26855         this.fireEvent('resize', this);
26856     },
26857     
26858     setPositionByLatLng: function(latitude, longitude)
26859     {
26860         this.setPosition(new google.maps.LatLng(latitude, longitude));
26861     },
26862     
26863     getCurrentPosition: function() 
26864     {
26865         return {
26866             latitude: this.gMapContext.location.lat(),
26867             longitude: this.gMapContext.location.lng()
26868         };
26869     },
26870     
26871     getAddressName: function() 
26872     {
26873         return this.gMapContext.locationName;
26874     },
26875     
26876     getAddressComponents: function() 
26877     {
26878         return this.gMapContext.addressComponents;
26879     },
26880     
26881     address_component_from_google_geocode: function(address_components) 
26882     {
26883         var result = {};
26884         
26885         for (var i = 0; i < address_components.length; i++) {
26886             var component = address_components[i];
26887             if (component.types.indexOf("postal_code") >= 0) {
26888                 result.postalCode = component.short_name;
26889             } else if (component.types.indexOf("street_number") >= 0) {
26890                 result.streetNumber = component.short_name;
26891             } else if (component.types.indexOf("route") >= 0) {
26892                 result.streetName = component.short_name;
26893             } else if (component.types.indexOf("neighborhood") >= 0) {
26894                 result.city = component.short_name;
26895             } else if (component.types.indexOf("locality") >= 0) {
26896                 result.city = component.short_name;
26897             } else if (component.types.indexOf("sublocality") >= 0) {
26898                 result.district = component.short_name;
26899             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26900                 result.stateOrProvince = component.short_name;
26901             } else if (component.types.indexOf("country") >= 0) {
26902                 result.country = component.short_name;
26903             }
26904         }
26905         
26906         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26907         result.addressLine2 = "";
26908         return result;
26909     },
26910     
26911     setZoomLevel: function(zoom)
26912     {
26913         this.gMapContext.map.setZoom(zoom);
26914     },
26915     
26916     show: function()
26917     {
26918         if(!this.el){
26919             return;
26920         }
26921         
26922         this.el.show();
26923         
26924         this.resize();
26925         
26926         this.fireEvent('show', this);
26927     },
26928     
26929     hide: function()
26930     {
26931         if(!this.el){
26932             return;
26933         }
26934         
26935         this.el.hide();
26936         
26937         this.fireEvent('hide', this);
26938     }
26939     
26940 });
26941
26942 Roo.apply(Roo.bootstrap.LocationPicker, {
26943     
26944     OverlayView : function(map, options)
26945     {
26946         options = options || {};
26947         
26948         this.setMap(map);
26949     }
26950     
26951     
26952 });/*
26953  * - LGPL
26954  *
26955  * Alert
26956  * 
26957  */
26958
26959 /**
26960  * @class Roo.bootstrap.Alert
26961  * @extends Roo.bootstrap.Component
26962  * Bootstrap Alert class
26963  * @cfg {String} title The title of alert
26964  * @cfg {String} html The content of alert
26965  * @cfg {String} weight (  success | info | warning | danger )
26966  * @cfg {String} faicon font-awesomeicon
26967  * 
26968  * @constructor
26969  * Create a new alert
26970  * @param {Object} config The config object
26971  */
26972
26973
26974 Roo.bootstrap.Alert = function(config){
26975     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26976     
26977 };
26978
26979 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26980     
26981     title: '',
26982     html: '',
26983     weight: false,
26984     faicon: false,
26985     
26986     getAutoCreate : function()
26987     {
26988         
26989         var cfg = {
26990             tag : 'div',
26991             cls : 'alert',
26992             cn : [
26993                 {
26994                     tag : 'i',
26995                     cls : 'roo-alert-icon'
26996                     
26997                 },
26998                 {
26999                     tag : 'b',
27000                     cls : 'roo-alert-title',
27001                     html : this.title
27002                 },
27003                 {
27004                     tag : 'span',
27005                     cls : 'roo-alert-text',
27006                     html : this.html
27007                 }
27008             ]
27009         };
27010         
27011         if(this.faicon){
27012             cfg.cn[0].cls += ' fa ' + this.faicon;
27013         }
27014         
27015         if(this.weight){
27016             cfg.cls += ' alert-' + this.weight;
27017         }
27018         
27019         return cfg;
27020     },
27021     
27022     initEvents: function() 
27023     {
27024         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27025     },
27026     
27027     setTitle : function(str)
27028     {
27029         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27030     },
27031     
27032     setText : function(str)
27033     {
27034         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27035     },
27036     
27037     setWeight : function(weight)
27038     {
27039         if(this.weight){
27040             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27041         }
27042         
27043         this.weight = weight;
27044         
27045         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27046     },
27047     
27048     setIcon : function(icon)
27049     {
27050         if(this.faicon){
27051             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27052         }
27053         
27054         this.faicon = icon;
27055         
27056         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27057     },
27058     
27059     hide: function() 
27060     {
27061         this.el.hide();   
27062     },
27063     
27064     show: function() 
27065     {  
27066         this.el.show();   
27067     }
27068     
27069 });
27070
27071  
27072 /*
27073 * Licence: LGPL
27074 */
27075
27076 /**
27077  * @class Roo.bootstrap.UploadCropbox
27078  * @extends Roo.bootstrap.Component
27079  * Bootstrap UploadCropbox class
27080  * @cfg {String} emptyText show when image has been loaded
27081  * @cfg {String} rotateNotify show when image too small to rotate
27082  * @cfg {Number} errorTimeout default 3000
27083  * @cfg {Number} minWidth default 300
27084  * @cfg {Number} minHeight default 300
27085  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27086  * @cfg {Boolean} isDocument (true|false) default false
27087  * @cfg {String} url action url
27088  * @cfg {String} paramName default 'imageUpload'
27089  * @cfg {String} method default POST
27090  * @cfg {Boolean} loadMask (true|false) default true
27091  * @cfg {Boolean} loadingText default 'Loading...'
27092  * 
27093  * @constructor
27094  * Create a new UploadCropbox
27095  * @param {Object} config The config object
27096  */
27097
27098 Roo.bootstrap.UploadCropbox = function(config){
27099     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27100     
27101     this.addEvents({
27102         /**
27103          * @event beforeselectfile
27104          * Fire before select file
27105          * @param {Roo.bootstrap.UploadCropbox} this
27106          */
27107         "beforeselectfile" : true,
27108         /**
27109          * @event initial
27110          * Fire after initEvent
27111          * @param {Roo.bootstrap.UploadCropbox} this
27112          */
27113         "initial" : true,
27114         /**
27115          * @event crop
27116          * Fire after initEvent
27117          * @param {Roo.bootstrap.UploadCropbox} this
27118          * @param {String} data
27119          */
27120         "crop" : true,
27121         /**
27122          * @event prepare
27123          * Fire when preparing the file data
27124          * @param {Roo.bootstrap.UploadCropbox} this
27125          * @param {Object} file
27126          */
27127         "prepare" : true,
27128         /**
27129          * @event exception
27130          * Fire when get exception
27131          * @param {Roo.bootstrap.UploadCropbox} this
27132          * @param {XMLHttpRequest} xhr
27133          */
27134         "exception" : true,
27135         /**
27136          * @event beforeloadcanvas
27137          * Fire before load the canvas
27138          * @param {Roo.bootstrap.UploadCropbox} this
27139          * @param {String} src
27140          */
27141         "beforeloadcanvas" : true,
27142         /**
27143          * @event trash
27144          * Fire when trash image
27145          * @param {Roo.bootstrap.UploadCropbox} this
27146          */
27147         "trash" : true,
27148         /**
27149          * @event download
27150          * Fire when download the image
27151          * @param {Roo.bootstrap.UploadCropbox} this
27152          */
27153         "download" : true,
27154         /**
27155          * @event footerbuttonclick
27156          * Fire when footerbuttonclick
27157          * @param {Roo.bootstrap.UploadCropbox} this
27158          * @param {String} type
27159          */
27160         "footerbuttonclick" : true,
27161         /**
27162          * @event resize
27163          * Fire when resize
27164          * @param {Roo.bootstrap.UploadCropbox} this
27165          */
27166         "resize" : true,
27167         /**
27168          * @event rotate
27169          * Fire when rotate the image
27170          * @param {Roo.bootstrap.UploadCropbox} this
27171          * @param {String} pos
27172          */
27173         "rotate" : true,
27174         /**
27175          * @event inspect
27176          * Fire when inspect the file
27177          * @param {Roo.bootstrap.UploadCropbox} this
27178          * @param {Object} file
27179          */
27180         "inspect" : true,
27181         /**
27182          * @event upload
27183          * Fire when xhr upload the file
27184          * @param {Roo.bootstrap.UploadCropbox} this
27185          * @param {Object} data
27186          */
27187         "upload" : true,
27188         /**
27189          * @event arrange
27190          * Fire when arrange the file data
27191          * @param {Roo.bootstrap.UploadCropbox} this
27192          * @param {Object} formData
27193          */
27194         "arrange" : true
27195     });
27196     
27197     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27198 };
27199
27200 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27201     
27202     emptyText : 'Click to upload image',
27203     rotateNotify : 'Image is too small to rotate',
27204     errorTimeout : 3000,
27205     scale : 0,
27206     baseScale : 1,
27207     rotate : 0,
27208     dragable : false,
27209     pinching : false,
27210     mouseX : 0,
27211     mouseY : 0,
27212     cropData : false,
27213     minWidth : 300,
27214     minHeight : 300,
27215     file : false,
27216     exif : {},
27217     baseRotate : 1,
27218     cropType : 'image/jpeg',
27219     buttons : false,
27220     canvasLoaded : false,
27221     isDocument : false,
27222     method : 'POST',
27223     paramName : 'imageUpload',
27224     loadMask : true,
27225     loadingText : 'Loading...',
27226     maskEl : false,
27227     
27228     getAutoCreate : function()
27229     {
27230         var cfg = {
27231             tag : 'div',
27232             cls : 'roo-upload-cropbox',
27233             cn : [
27234                 {
27235                     tag : 'input',
27236                     cls : 'roo-upload-cropbox-selector',
27237                     type : 'file'
27238                 },
27239                 {
27240                     tag : 'div',
27241                     cls : 'roo-upload-cropbox-body',
27242                     style : 'cursor:pointer',
27243                     cn : [
27244                         {
27245                             tag : 'div',
27246                             cls : 'roo-upload-cropbox-preview'
27247                         },
27248                         {
27249                             tag : 'div',
27250                             cls : 'roo-upload-cropbox-thumb'
27251                         },
27252                         {
27253                             tag : 'div',
27254                             cls : 'roo-upload-cropbox-empty-notify',
27255                             html : this.emptyText
27256                         },
27257                         {
27258                             tag : 'div',
27259                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27260                             html : this.rotateNotify
27261                         }
27262                     ]
27263                 },
27264                 {
27265                     tag : 'div',
27266                     cls : 'roo-upload-cropbox-footer',
27267                     cn : {
27268                         tag : 'div',
27269                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27270                         cn : []
27271                     }
27272                 }
27273             ]
27274         };
27275         
27276         return cfg;
27277     },
27278     
27279     onRender : function(ct, position)
27280     {
27281         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27282         
27283         if (this.buttons.length) {
27284             
27285             Roo.each(this.buttons, function(bb) {
27286                 
27287                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27288                 
27289                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27290                 
27291             }, this);
27292         }
27293         
27294         if(this.loadMask){
27295             this.maskEl = this.el;
27296         }
27297     },
27298     
27299     initEvents : function()
27300     {
27301         this.urlAPI = (window.createObjectURL && window) || 
27302                                 (window.URL && URL.revokeObjectURL && URL) || 
27303                                 (window.webkitURL && webkitURL);
27304                         
27305         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27306         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27307         
27308         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27309         this.selectorEl.hide();
27310         
27311         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27312         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27313         
27314         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27315         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27316         this.thumbEl.hide();
27317         
27318         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27319         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27320         
27321         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27322         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27323         this.errorEl.hide();
27324         
27325         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27326         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27327         this.footerEl.hide();
27328         
27329         this.setThumbBoxSize();
27330         
27331         this.bind();
27332         
27333         this.resize();
27334         
27335         this.fireEvent('initial', this);
27336     },
27337
27338     bind : function()
27339     {
27340         var _this = this;
27341         
27342         window.addEventListener("resize", function() { _this.resize(); } );
27343         
27344         this.bodyEl.on('click', this.beforeSelectFile, this);
27345         
27346         if(Roo.isTouch){
27347             this.bodyEl.on('touchstart', this.onTouchStart, this);
27348             this.bodyEl.on('touchmove', this.onTouchMove, this);
27349             this.bodyEl.on('touchend', this.onTouchEnd, this);
27350         }
27351         
27352         if(!Roo.isTouch){
27353             this.bodyEl.on('mousedown', this.onMouseDown, this);
27354             this.bodyEl.on('mousemove', this.onMouseMove, this);
27355             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27356             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27357             Roo.get(document).on('mouseup', this.onMouseUp, this);
27358         }
27359         
27360         this.selectorEl.on('change', this.onFileSelected, this);
27361     },
27362     
27363     reset : function()
27364     {    
27365         this.scale = 0;
27366         this.baseScale = 1;
27367         this.rotate = 0;
27368         this.baseRotate = 1;
27369         this.dragable = false;
27370         this.pinching = false;
27371         this.mouseX = 0;
27372         this.mouseY = 0;
27373         this.cropData = false;
27374         this.notifyEl.dom.innerHTML = this.emptyText;
27375         
27376         this.selectorEl.dom.value = '';
27377         
27378     },
27379     
27380     resize : function()
27381     {
27382         if(this.fireEvent('resize', this) != false){
27383             this.setThumbBoxPosition();
27384             this.setCanvasPosition();
27385         }
27386     },
27387     
27388     onFooterButtonClick : function(e, el, o, type)
27389     {
27390         switch (type) {
27391             case 'rotate-left' :
27392                 this.onRotateLeft(e);
27393                 break;
27394             case 'rotate-right' :
27395                 this.onRotateRight(e);
27396                 break;
27397             case 'picture' :
27398                 this.beforeSelectFile(e);
27399                 break;
27400             case 'trash' :
27401                 this.trash(e);
27402                 break;
27403             case 'crop' :
27404                 this.crop(e);
27405                 break;
27406             case 'download' :
27407                 this.download(e);
27408                 break;
27409             default :
27410                 break;
27411         }
27412         
27413         this.fireEvent('footerbuttonclick', this, type);
27414     },
27415     
27416     beforeSelectFile : function(e)
27417     {
27418         e.preventDefault();
27419         
27420         if(this.fireEvent('beforeselectfile', this) != false){
27421             this.selectorEl.dom.click();
27422         }
27423     },
27424     
27425     onFileSelected : function(e)
27426     {
27427         e.preventDefault();
27428         
27429         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27430             return;
27431         }
27432         
27433         var file = this.selectorEl.dom.files[0];
27434         
27435         if(this.fireEvent('inspect', this, file) != false){
27436             this.prepare(file);
27437         }
27438         
27439     },
27440     
27441     trash : function(e)
27442     {
27443         this.fireEvent('trash', this);
27444     },
27445     
27446     download : function(e)
27447     {
27448         this.fireEvent('download', this);
27449     },
27450     
27451     loadCanvas : function(src)
27452     {   
27453         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27454             
27455             this.reset();
27456             
27457             this.imageEl = document.createElement('img');
27458             
27459             var _this = this;
27460             
27461             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27462             
27463             this.imageEl.src = src;
27464         }
27465     },
27466     
27467     onLoadCanvas : function()
27468     {   
27469         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27470         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27471         
27472         this.bodyEl.un('click', this.beforeSelectFile, this);
27473         
27474         this.notifyEl.hide();
27475         this.thumbEl.show();
27476         this.footerEl.show();
27477         
27478         this.baseRotateLevel();
27479         
27480         if(this.isDocument){
27481             this.setThumbBoxSize();
27482         }
27483         
27484         this.setThumbBoxPosition();
27485         
27486         this.baseScaleLevel();
27487         
27488         this.draw();
27489         
27490         this.resize();
27491         
27492         this.canvasLoaded = true;
27493         
27494         if(this.loadMask){
27495             this.maskEl.unmask();
27496         }
27497         
27498     },
27499     
27500     setCanvasPosition : function()
27501     {   
27502         if(!this.canvasEl){
27503             return;
27504         }
27505         
27506         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27507         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27508         
27509         this.previewEl.setLeft(pw);
27510         this.previewEl.setTop(ph);
27511         
27512     },
27513     
27514     onMouseDown : function(e)
27515     {   
27516         e.stopEvent();
27517         
27518         this.dragable = true;
27519         this.pinching = false;
27520         
27521         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27522             this.dragable = false;
27523             return;
27524         }
27525         
27526         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27527         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27528         
27529     },
27530     
27531     onMouseMove : function(e)
27532     {   
27533         e.stopEvent();
27534         
27535         if(!this.canvasLoaded){
27536             return;
27537         }
27538         
27539         if (!this.dragable){
27540             return;
27541         }
27542         
27543         var minX = Math.ceil(this.thumbEl.getLeft(true));
27544         var minY = Math.ceil(this.thumbEl.getTop(true));
27545         
27546         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27547         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27548         
27549         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27550         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27551         
27552         x = x - this.mouseX;
27553         y = y - this.mouseY;
27554         
27555         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27556         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27557         
27558         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27559         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27560         
27561         this.previewEl.setLeft(bgX);
27562         this.previewEl.setTop(bgY);
27563         
27564         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27565         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27566     },
27567     
27568     onMouseUp : function(e)
27569     {   
27570         e.stopEvent();
27571         
27572         this.dragable = false;
27573     },
27574     
27575     onMouseWheel : function(e)
27576     {   
27577         e.stopEvent();
27578         
27579         this.startScale = this.scale;
27580         
27581         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27582         
27583         if(!this.zoomable()){
27584             this.scale = this.startScale;
27585             return;
27586         }
27587         
27588         this.draw();
27589         
27590         return;
27591     },
27592     
27593     zoomable : function()
27594     {
27595         var minScale = this.thumbEl.getWidth() / this.minWidth;
27596         
27597         if(this.minWidth < this.minHeight){
27598             minScale = this.thumbEl.getHeight() / this.minHeight;
27599         }
27600         
27601         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27602         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27603         
27604         if(
27605                 this.isDocument &&
27606                 (this.rotate == 0 || this.rotate == 180) && 
27607                 (
27608                     width > this.imageEl.OriginWidth || 
27609                     height > this.imageEl.OriginHeight ||
27610                     (width < this.minWidth && height < this.minHeight)
27611                 )
27612         ){
27613             return false;
27614         }
27615         
27616         if(
27617                 this.isDocument &&
27618                 (this.rotate == 90 || this.rotate == 270) && 
27619                 (
27620                     width > this.imageEl.OriginWidth || 
27621                     height > this.imageEl.OriginHeight ||
27622                     (width < this.minHeight && height < this.minWidth)
27623                 )
27624         ){
27625             return false;
27626         }
27627         
27628         if(
27629                 !this.isDocument &&
27630                 (this.rotate == 0 || this.rotate == 180) && 
27631                 (
27632                     width < this.minWidth || 
27633                     width > this.imageEl.OriginWidth || 
27634                     height < this.minHeight || 
27635                     height > this.imageEl.OriginHeight
27636                 )
27637         ){
27638             return false;
27639         }
27640         
27641         if(
27642                 !this.isDocument &&
27643                 (this.rotate == 90 || this.rotate == 270) && 
27644                 (
27645                     width < this.minHeight || 
27646                     width > this.imageEl.OriginWidth || 
27647                     height < this.minWidth || 
27648                     height > this.imageEl.OriginHeight
27649                 )
27650         ){
27651             return false;
27652         }
27653         
27654         return true;
27655         
27656     },
27657     
27658     onRotateLeft : function(e)
27659     {   
27660         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27661             
27662             var minScale = this.thumbEl.getWidth() / this.minWidth;
27663             
27664             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27665             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27666             
27667             this.startScale = this.scale;
27668             
27669             while (this.getScaleLevel() < minScale){
27670             
27671                 this.scale = this.scale + 1;
27672                 
27673                 if(!this.zoomable()){
27674                     break;
27675                 }
27676                 
27677                 if(
27678                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27679                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27680                 ){
27681                     continue;
27682                 }
27683                 
27684                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27685
27686                 this.draw();
27687                 
27688                 return;
27689             }
27690             
27691             this.scale = this.startScale;
27692             
27693             this.onRotateFail();
27694             
27695             return false;
27696         }
27697         
27698         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27699
27700         if(this.isDocument){
27701             this.setThumbBoxSize();
27702             this.setThumbBoxPosition();
27703             this.setCanvasPosition();
27704         }
27705         
27706         this.draw();
27707         
27708         this.fireEvent('rotate', this, 'left');
27709         
27710     },
27711     
27712     onRotateRight : function(e)
27713     {
27714         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27715             
27716             var minScale = this.thumbEl.getWidth() / this.minWidth;
27717         
27718             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27719             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27720             
27721             this.startScale = this.scale;
27722             
27723             while (this.getScaleLevel() < minScale){
27724             
27725                 this.scale = this.scale + 1;
27726                 
27727                 if(!this.zoomable()){
27728                     break;
27729                 }
27730                 
27731                 if(
27732                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27733                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27734                 ){
27735                     continue;
27736                 }
27737                 
27738                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27739
27740                 this.draw();
27741                 
27742                 return;
27743             }
27744             
27745             this.scale = this.startScale;
27746             
27747             this.onRotateFail();
27748             
27749             return false;
27750         }
27751         
27752         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27753
27754         if(this.isDocument){
27755             this.setThumbBoxSize();
27756             this.setThumbBoxPosition();
27757             this.setCanvasPosition();
27758         }
27759         
27760         this.draw();
27761         
27762         this.fireEvent('rotate', this, 'right');
27763     },
27764     
27765     onRotateFail : function()
27766     {
27767         this.errorEl.show(true);
27768         
27769         var _this = this;
27770         
27771         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27772     },
27773     
27774     draw : function()
27775     {
27776         this.previewEl.dom.innerHTML = '';
27777         
27778         var canvasEl = document.createElement("canvas");
27779         
27780         var contextEl = canvasEl.getContext("2d");
27781         
27782         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27783         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27784         var center = this.imageEl.OriginWidth / 2;
27785         
27786         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27787             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27788             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27789             center = this.imageEl.OriginHeight / 2;
27790         }
27791         
27792         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27793         
27794         contextEl.translate(center, center);
27795         contextEl.rotate(this.rotate * Math.PI / 180);
27796
27797         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27798         
27799         this.canvasEl = document.createElement("canvas");
27800         
27801         this.contextEl = this.canvasEl.getContext("2d");
27802         
27803         switch (this.rotate) {
27804             case 0 :
27805                 
27806                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27807                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27808                 
27809                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27810                 
27811                 break;
27812             case 90 : 
27813                 
27814                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27815                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27816                 
27817                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27818                     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);
27819                     break;
27820                 }
27821                 
27822                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27823                 
27824                 break;
27825             case 180 :
27826                 
27827                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27828                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27829                 
27830                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27831                     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);
27832                     break;
27833                 }
27834                 
27835                 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);
27836                 
27837                 break;
27838             case 270 :
27839                 
27840                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27841                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27842         
27843                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27844                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27845                     break;
27846                 }
27847                 
27848                 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);
27849                 
27850                 break;
27851             default : 
27852                 break;
27853         }
27854         
27855         this.previewEl.appendChild(this.canvasEl);
27856         
27857         this.setCanvasPosition();
27858     },
27859     
27860     crop : function()
27861     {
27862         if(!this.canvasLoaded){
27863             return;
27864         }
27865         
27866         var imageCanvas = document.createElement("canvas");
27867         
27868         var imageContext = imageCanvas.getContext("2d");
27869         
27870         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27871         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27872         
27873         var center = imageCanvas.width / 2;
27874         
27875         imageContext.translate(center, center);
27876         
27877         imageContext.rotate(this.rotate * Math.PI / 180);
27878         
27879         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27880         
27881         var canvas = document.createElement("canvas");
27882         
27883         var context = canvas.getContext("2d");
27884                 
27885         canvas.width = this.minWidth;
27886         canvas.height = this.minHeight;
27887
27888         switch (this.rotate) {
27889             case 0 :
27890                 
27891                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27892                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27893                 
27894                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27895                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27896                 
27897                 var targetWidth = this.minWidth - 2 * x;
27898                 var targetHeight = this.minHeight - 2 * y;
27899                 
27900                 var scale = 1;
27901                 
27902                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27903                     scale = targetWidth / width;
27904                 }
27905                 
27906                 if(x > 0 && y == 0){
27907                     scale = targetHeight / height;
27908                 }
27909                 
27910                 if(x > 0 && y > 0){
27911                     scale = targetWidth / width;
27912                     
27913                     if(width < height){
27914                         scale = targetHeight / height;
27915                     }
27916                 }
27917                 
27918                 context.scale(scale, scale);
27919                 
27920                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27921                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27922
27923                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27924                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27925
27926                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27927                 
27928                 break;
27929             case 90 : 
27930                 
27931                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27932                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27933                 
27934                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27935                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27936                 
27937                 var targetWidth = this.minWidth - 2 * x;
27938                 var targetHeight = this.minHeight - 2 * y;
27939                 
27940                 var scale = 1;
27941                 
27942                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27943                     scale = targetWidth / width;
27944                 }
27945                 
27946                 if(x > 0 && y == 0){
27947                     scale = targetHeight / height;
27948                 }
27949                 
27950                 if(x > 0 && y > 0){
27951                     scale = targetWidth / width;
27952                     
27953                     if(width < height){
27954                         scale = targetHeight / height;
27955                     }
27956                 }
27957                 
27958                 context.scale(scale, scale);
27959                 
27960                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27961                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27962
27963                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27964                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27965                 
27966                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27967                 
27968                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27969                 
27970                 break;
27971             case 180 :
27972                 
27973                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27974                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27975                 
27976                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27977                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27978                 
27979                 var targetWidth = this.minWidth - 2 * x;
27980                 var targetHeight = this.minHeight - 2 * y;
27981                 
27982                 var scale = 1;
27983                 
27984                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27985                     scale = targetWidth / width;
27986                 }
27987                 
27988                 if(x > 0 && y == 0){
27989                     scale = targetHeight / height;
27990                 }
27991                 
27992                 if(x > 0 && y > 0){
27993                     scale = targetWidth / width;
27994                     
27995                     if(width < height){
27996                         scale = targetHeight / height;
27997                     }
27998                 }
27999                 
28000                 context.scale(scale, scale);
28001                 
28002                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28003                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28004
28005                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28006                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28007
28008                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28009                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28010                 
28011                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28012                 
28013                 break;
28014             case 270 :
28015                 
28016                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28017                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28018                 
28019                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28020                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28021                 
28022                 var targetWidth = this.minWidth - 2 * x;
28023                 var targetHeight = this.minHeight - 2 * y;
28024                 
28025                 var scale = 1;
28026                 
28027                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28028                     scale = targetWidth / width;
28029                 }
28030                 
28031                 if(x > 0 && y == 0){
28032                     scale = targetHeight / height;
28033                 }
28034                 
28035                 if(x > 0 && y > 0){
28036                     scale = targetWidth / width;
28037                     
28038                     if(width < height){
28039                         scale = targetHeight / height;
28040                     }
28041                 }
28042                 
28043                 context.scale(scale, scale);
28044                 
28045                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28046                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28047
28048                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28049                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28050                 
28051                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28052                 
28053                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28054                 
28055                 break;
28056             default : 
28057                 break;
28058         }
28059         
28060         this.cropData = canvas.toDataURL(this.cropType);
28061         
28062         if(this.fireEvent('crop', this, this.cropData) !== false){
28063             this.process(this.file, this.cropData);
28064         }
28065         
28066         return;
28067         
28068     },
28069     
28070     setThumbBoxSize : function()
28071     {
28072         var width, height;
28073         
28074         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28075             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28076             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28077             
28078             this.minWidth = width;
28079             this.minHeight = height;
28080             
28081             if(this.rotate == 90 || this.rotate == 270){
28082                 this.minWidth = height;
28083                 this.minHeight = width;
28084             }
28085         }
28086         
28087         height = 300;
28088         width = Math.ceil(this.minWidth * height / this.minHeight);
28089         
28090         if(this.minWidth > this.minHeight){
28091             width = 300;
28092             height = Math.ceil(this.minHeight * width / this.minWidth);
28093         }
28094         
28095         this.thumbEl.setStyle({
28096             width : width + 'px',
28097             height : height + 'px'
28098         });
28099
28100         return;
28101             
28102     },
28103     
28104     setThumbBoxPosition : function()
28105     {
28106         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28107         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28108         
28109         this.thumbEl.setLeft(x);
28110         this.thumbEl.setTop(y);
28111         
28112     },
28113     
28114     baseRotateLevel : function()
28115     {
28116         this.baseRotate = 1;
28117         
28118         if(
28119                 typeof(this.exif) != 'undefined' &&
28120                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28121                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28122         ){
28123             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28124         }
28125         
28126         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28127         
28128     },
28129     
28130     baseScaleLevel : function()
28131     {
28132         var width, height;
28133         
28134         if(this.isDocument){
28135             
28136             if(this.baseRotate == 6 || this.baseRotate == 8){
28137             
28138                 height = this.thumbEl.getHeight();
28139                 this.baseScale = height / this.imageEl.OriginWidth;
28140
28141                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28142                     width = this.thumbEl.getWidth();
28143                     this.baseScale = width / this.imageEl.OriginHeight;
28144                 }
28145
28146                 return;
28147             }
28148
28149             height = this.thumbEl.getHeight();
28150             this.baseScale = height / this.imageEl.OriginHeight;
28151
28152             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28153                 width = this.thumbEl.getWidth();
28154                 this.baseScale = width / this.imageEl.OriginWidth;
28155             }
28156
28157             return;
28158         }
28159         
28160         if(this.baseRotate == 6 || this.baseRotate == 8){
28161             
28162             width = this.thumbEl.getHeight();
28163             this.baseScale = width / this.imageEl.OriginHeight;
28164             
28165             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28166                 height = this.thumbEl.getWidth();
28167                 this.baseScale = height / this.imageEl.OriginHeight;
28168             }
28169             
28170             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28171                 height = this.thumbEl.getWidth();
28172                 this.baseScale = height / this.imageEl.OriginHeight;
28173                 
28174                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28175                     width = this.thumbEl.getHeight();
28176                     this.baseScale = width / this.imageEl.OriginWidth;
28177                 }
28178             }
28179             
28180             return;
28181         }
28182         
28183         width = this.thumbEl.getWidth();
28184         this.baseScale = width / this.imageEl.OriginWidth;
28185         
28186         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28187             height = this.thumbEl.getHeight();
28188             this.baseScale = height / this.imageEl.OriginHeight;
28189         }
28190         
28191         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28192             
28193             height = this.thumbEl.getHeight();
28194             this.baseScale = height / this.imageEl.OriginHeight;
28195             
28196             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28197                 width = this.thumbEl.getWidth();
28198                 this.baseScale = width / this.imageEl.OriginWidth;
28199             }
28200             
28201         }
28202         
28203         return;
28204     },
28205     
28206     getScaleLevel : function()
28207     {
28208         return this.baseScale * Math.pow(1.1, this.scale);
28209     },
28210     
28211     onTouchStart : function(e)
28212     {
28213         if(!this.canvasLoaded){
28214             this.beforeSelectFile(e);
28215             return;
28216         }
28217         
28218         var touches = e.browserEvent.touches;
28219         
28220         if(!touches){
28221             return;
28222         }
28223         
28224         if(touches.length == 1){
28225             this.onMouseDown(e);
28226             return;
28227         }
28228         
28229         if(touches.length != 2){
28230             return;
28231         }
28232         
28233         var coords = [];
28234         
28235         for(var i = 0, finger; finger = touches[i]; i++){
28236             coords.push(finger.pageX, finger.pageY);
28237         }
28238         
28239         var x = Math.pow(coords[0] - coords[2], 2);
28240         var y = Math.pow(coords[1] - coords[3], 2);
28241         
28242         this.startDistance = Math.sqrt(x + y);
28243         
28244         this.startScale = this.scale;
28245         
28246         this.pinching = true;
28247         this.dragable = false;
28248         
28249     },
28250     
28251     onTouchMove : function(e)
28252     {
28253         if(!this.pinching && !this.dragable){
28254             return;
28255         }
28256         
28257         var touches = e.browserEvent.touches;
28258         
28259         if(!touches){
28260             return;
28261         }
28262         
28263         if(this.dragable){
28264             this.onMouseMove(e);
28265             return;
28266         }
28267         
28268         var coords = [];
28269         
28270         for(var i = 0, finger; finger = touches[i]; i++){
28271             coords.push(finger.pageX, finger.pageY);
28272         }
28273         
28274         var x = Math.pow(coords[0] - coords[2], 2);
28275         var y = Math.pow(coords[1] - coords[3], 2);
28276         
28277         this.endDistance = Math.sqrt(x + y);
28278         
28279         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28280         
28281         if(!this.zoomable()){
28282             this.scale = this.startScale;
28283             return;
28284         }
28285         
28286         this.draw();
28287         
28288     },
28289     
28290     onTouchEnd : function(e)
28291     {
28292         this.pinching = false;
28293         this.dragable = false;
28294         
28295     },
28296     
28297     process : function(file, crop)
28298     {
28299         if(this.loadMask){
28300             this.maskEl.mask(this.loadingText);
28301         }
28302         
28303         this.xhr = new XMLHttpRequest();
28304         
28305         file.xhr = this.xhr;
28306
28307         this.xhr.open(this.method, this.url, true);
28308         
28309         var headers = {
28310             "Accept": "application/json",
28311             "Cache-Control": "no-cache",
28312             "X-Requested-With": "XMLHttpRequest"
28313         };
28314         
28315         for (var headerName in headers) {
28316             var headerValue = headers[headerName];
28317             if (headerValue) {
28318                 this.xhr.setRequestHeader(headerName, headerValue);
28319             }
28320         }
28321         
28322         var _this = this;
28323         
28324         this.xhr.onload = function()
28325         {
28326             _this.xhrOnLoad(_this.xhr);
28327         }
28328         
28329         this.xhr.onerror = function()
28330         {
28331             _this.xhrOnError(_this.xhr);
28332         }
28333         
28334         var formData = new FormData();
28335
28336         formData.append('returnHTML', 'NO');
28337         
28338         if(crop){
28339             formData.append('crop', crop);
28340         }
28341         
28342         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28343             formData.append(this.paramName, file, file.name);
28344         }
28345         
28346         if(typeof(file.filename) != 'undefined'){
28347             formData.append('filename', file.filename);
28348         }
28349         
28350         if(typeof(file.mimetype) != 'undefined'){
28351             formData.append('mimetype', file.mimetype);
28352         }
28353         
28354         if(this.fireEvent('arrange', this, formData) != false){
28355             this.xhr.send(formData);
28356         };
28357     },
28358     
28359     xhrOnLoad : function(xhr)
28360     {
28361         if(this.loadMask){
28362             this.maskEl.unmask();
28363         }
28364         
28365         if (xhr.readyState !== 4) {
28366             this.fireEvent('exception', this, xhr);
28367             return;
28368         }
28369
28370         var response = Roo.decode(xhr.responseText);
28371         
28372         if(!response.success){
28373             this.fireEvent('exception', this, xhr);
28374             return;
28375         }
28376         
28377         var response = Roo.decode(xhr.responseText);
28378         
28379         this.fireEvent('upload', this, response);
28380         
28381     },
28382     
28383     xhrOnError : function()
28384     {
28385         if(this.loadMask){
28386             this.maskEl.unmask();
28387         }
28388         
28389         Roo.log('xhr on error');
28390         
28391         var response = Roo.decode(xhr.responseText);
28392           
28393         Roo.log(response);
28394         
28395     },
28396     
28397     prepare : function(file)
28398     {   
28399         if(this.loadMask){
28400             this.maskEl.mask(this.loadingText);
28401         }
28402         
28403         this.file = false;
28404         this.exif = {};
28405         
28406         if(typeof(file) === 'string'){
28407             this.loadCanvas(file);
28408             return;
28409         }
28410         
28411         if(!file || !this.urlAPI){
28412             return;
28413         }
28414         
28415         this.file = file;
28416         this.cropType = file.type;
28417         
28418         var _this = this;
28419         
28420         if(this.fireEvent('prepare', this, this.file) != false){
28421             
28422             var reader = new FileReader();
28423             
28424             reader.onload = function (e) {
28425                 if (e.target.error) {
28426                     Roo.log(e.target.error);
28427                     return;
28428                 }
28429                 
28430                 var buffer = e.target.result,
28431                     dataView = new DataView(buffer),
28432                     offset = 2,
28433                     maxOffset = dataView.byteLength - 4,
28434                     markerBytes,
28435                     markerLength;
28436                 
28437                 if (dataView.getUint16(0) === 0xffd8) {
28438                     while (offset < maxOffset) {
28439                         markerBytes = dataView.getUint16(offset);
28440                         
28441                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28442                             markerLength = dataView.getUint16(offset + 2) + 2;
28443                             if (offset + markerLength > dataView.byteLength) {
28444                                 Roo.log('Invalid meta data: Invalid segment size.');
28445                                 break;
28446                             }
28447                             
28448                             if(markerBytes == 0xffe1){
28449                                 _this.parseExifData(
28450                                     dataView,
28451                                     offset,
28452                                     markerLength
28453                                 );
28454                             }
28455                             
28456                             offset += markerLength;
28457                             
28458                             continue;
28459                         }
28460                         
28461                         break;
28462                     }
28463                     
28464                 }
28465                 
28466                 var url = _this.urlAPI.createObjectURL(_this.file);
28467                 
28468                 _this.loadCanvas(url);
28469                 
28470                 return;
28471             }
28472             
28473             reader.readAsArrayBuffer(this.file);
28474             
28475         }
28476         
28477     },
28478     
28479     parseExifData : function(dataView, offset, length)
28480     {
28481         var tiffOffset = offset + 10,
28482             littleEndian,
28483             dirOffset;
28484     
28485         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28486             // No Exif data, might be XMP data instead
28487             return;
28488         }
28489         
28490         // Check for the ASCII code for "Exif" (0x45786966):
28491         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28492             // No Exif data, might be XMP data instead
28493             return;
28494         }
28495         if (tiffOffset + 8 > dataView.byteLength) {
28496             Roo.log('Invalid Exif data: Invalid segment size.');
28497             return;
28498         }
28499         // Check for the two null bytes:
28500         if (dataView.getUint16(offset + 8) !== 0x0000) {
28501             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28502             return;
28503         }
28504         // Check the byte alignment:
28505         switch (dataView.getUint16(tiffOffset)) {
28506         case 0x4949:
28507             littleEndian = true;
28508             break;
28509         case 0x4D4D:
28510             littleEndian = false;
28511             break;
28512         default:
28513             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28514             return;
28515         }
28516         // Check for the TIFF tag marker (0x002A):
28517         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28518             Roo.log('Invalid Exif data: Missing TIFF marker.');
28519             return;
28520         }
28521         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28522         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28523         
28524         this.parseExifTags(
28525             dataView,
28526             tiffOffset,
28527             tiffOffset + dirOffset,
28528             littleEndian
28529         );
28530     },
28531     
28532     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28533     {
28534         var tagsNumber,
28535             dirEndOffset,
28536             i;
28537         if (dirOffset + 6 > dataView.byteLength) {
28538             Roo.log('Invalid Exif data: Invalid directory offset.');
28539             return;
28540         }
28541         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28542         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28543         if (dirEndOffset + 4 > dataView.byteLength) {
28544             Roo.log('Invalid Exif data: Invalid directory size.');
28545             return;
28546         }
28547         for (i = 0; i < tagsNumber; i += 1) {
28548             this.parseExifTag(
28549                 dataView,
28550                 tiffOffset,
28551                 dirOffset + 2 + 12 * i, // tag offset
28552                 littleEndian
28553             );
28554         }
28555         // Return the offset to the next directory:
28556         return dataView.getUint32(dirEndOffset, littleEndian);
28557     },
28558     
28559     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28560     {
28561         var tag = dataView.getUint16(offset, littleEndian);
28562         
28563         this.exif[tag] = this.getExifValue(
28564             dataView,
28565             tiffOffset,
28566             offset,
28567             dataView.getUint16(offset + 2, littleEndian), // tag type
28568             dataView.getUint32(offset + 4, littleEndian), // tag length
28569             littleEndian
28570         );
28571     },
28572     
28573     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28574     {
28575         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28576             tagSize,
28577             dataOffset,
28578             values,
28579             i,
28580             str,
28581             c;
28582     
28583         if (!tagType) {
28584             Roo.log('Invalid Exif data: Invalid tag type.');
28585             return;
28586         }
28587         
28588         tagSize = tagType.size * length;
28589         // Determine if the value is contained in the dataOffset bytes,
28590         // or if the value at the dataOffset is a pointer to the actual data:
28591         dataOffset = tagSize > 4 ?
28592                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28593         if (dataOffset + tagSize > dataView.byteLength) {
28594             Roo.log('Invalid Exif data: Invalid data offset.');
28595             return;
28596         }
28597         if (length === 1) {
28598             return tagType.getValue(dataView, dataOffset, littleEndian);
28599         }
28600         values = [];
28601         for (i = 0; i < length; i += 1) {
28602             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28603         }
28604         
28605         if (tagType.ascii) {
28606             str = '';
28607             // Concatenate the chars:
28608             for (i = 0; i < values.length; i += 1) {
28609                 c = values[i];
28610                 // Ignore the terminating NULL byte(s):
28611                 if (c === '\u0000') {
28612                     break;
28613                 }
28614                 str += c;
28615             }
28616             return str;
28617         }
28618         return values;
28619     }
28620     
28621 });
28622
28623 Roo.apply(Roo.bootstrap.UploadCropbox, {
28624     tags : {
28625         'Orientation': 0x0112
28626     },
28627     
28628     Orientation: {
28629             1: 0, //'top-left',
28630 //            2: 'top-right',
28631             3: 180, //'bottom-right',
28632 //            4: 'bottom-left',
28633 //            5: 'left-top',
28634             6: 90, //'right-top',
28635 //            7: 'right-bottom',
28636             8: 270 //'left-bottom'
28637     },
28638     
28639     exifTagTypes : {
28640         // byte, 8-bit unsigned int:
28641         1: {
28642             getValue: function (dataView, dataOffset) {
28643                 return dataView.getUint8(dataOffset);
28644             },
28645             size: 1
28646         },
28647         // ascii, 8-bit byte:
28648         2: {
28649             getValue: function (dataView, dataOffset) {
28650                 return String.fromCharCode(dataView.getUint8(dataOffset));
28651             },
28652             size: 1,
28653             ascii: true
28654         },
28655         // short, 16 bit int:
28656         3: {
28657             getValue: function (dataView, dataOffset, littleEndian) {
28658                 return dataView.getUint16(dataOffset, littleEndian);
28659             },
28660             size: 2
28661         },
28662         // long, 32 bit int:
28663         4: {
28664             getValue: function (dataView, dataOffset, littleEndian) {
28665                 return dataView.getUint32(dataOffset, littleEndian);
28666             },
28667             size: 4
28668         },
28669         // rational = two long values, first is numerator, second is denominator:
28670         5: {
28671             getValue: function (dataView, dataOffset, littleEndian) {
28672                 return dataView.getUint32(dataOffset, littleEndian) /
28673                     dataView.getUint32(dataOffset + 4, littleEndian);
28674             },
28675             size: 8
28676         },
28677         // slong, 32 bit signed int:
28678         9: {
28679             getValue: function (dataView, dataOffset, littleEndian) {
28680                 return dataView.getInt32(dataOffset, littleEndian);
28681             },
28682             size: 4
28683         },
28684         // srational, two slongs, first is numerator, second is denominator:
28685         10: {
28686             getValue: function (dataView, dataOffset, littleEndian) {
28687                 return dataView.getInt32(dataOffset, littleEndian) /
28688                     dataView.getInt32(dataOffset + 4, littleEndian);
28689             },
28690             size: 8
28691         }
28692     },
28693     
28694     footer : {
28695         STANDARD : [
28696             {
28697                 tag : 'div',
28698                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28699                 action : 'rotate-left',
28700                 cn : [
28701                     {
28702                         tag : 'button',
28703                         cls : 'btn btn-default',
28704                         html : '<i class="fa fa-undo"></i>'
28705                     }
28706                 ]
28707             },
28708             {
28709                 tag : 'div',
28710                 cls : 'btn-group roo-upload-cropbox-picture',
28711                 action : 'picture',
28712                 cn : [
28713                     {
28714                         tag : 'button',
28715                         cls : 'btn btn-default',
28716                         html : '<i class="fa fa-picture-o"></i>'
28717                     }
28718                 ]
28719             },
28720             {
28721                 tag : 'div',
28722                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28723                 action : 'rotate-right',
28724                 cn : [
28725                     {
28726                         tag : 'button',
28727                         cls : 'btn btn-default',
28728                         html : '<i class="fa fa-repeat"></i>'
28729                     }
28730                 ]
28731             }
28732         ],
28733         DOCUMENT : [
28734             {
28735                 tag : 'div',
28736                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28737                 action : 'rotate-left',
28738                 cn : [
28739                     {
28740                         tag : 'button',
28741                         cls : 'btn btn-default',
28742                         html : '<i class="fa fa-undo"></i>'
28743                     }
28744                 ]
28745             },
28746             {
28747                 tag : 'div',
28748                 cls : 'btn-group roo-upload-cropbox-download',
28749                 action : 'download',
28750                 cn : [
28751                     {
28752                         tag : 'button',
28753                         cls : 'btn btn-default',
28754                         html : '<i class="fa fa-download"></i>'
28755                     }
28756                 ]
28757             },
28758             {
28759                 tag : 'div',
28760                 cls : 'btn-group roo-upload-cropbox-crop',
28761                 action : 'crop',
28762                 cn : [
28763                     {
28764                         tag : 'button',
28765                         cls : 'btn btn-default',
28766                         html : '<i class="fa fa-crop"></i>'
28767                     }
28768                 ]
28769             },
28770             {
28771                 tag : 'div',
28772                 cls : 'btn-group roo-upload-cropbox-trash',
28773                 action : 'trash',
28774                 cn : [
28775                     {
28776                         tag : 'button',
28777                         cls : 'btn btn-default',
28778                         html : '<i class="fa fa-trash"></i>'
28779                     }
28780                 ]
28781             },
28782             {
28783                 tag : 'div',
28784                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28785                 action : 'rotate-right',
28786                 cn : [
28787                     {
28788                         tag : 'button',
28789                         cls : 'btn btn-default',
28790                         html : '<i class="fa fa-repeat"></i>'
28791                     }
28792                 ]
28793             }
28794         ],
28795         ROTATOR : [
28796             {
28797                 tag : 'div',
28798                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28799                 action : 'rotate-left',
28800                 cn : [
28801                     {
28802                         tag : 'button',
28803                         cls : 'btn btn-default',
28804                         html : '<i class="fa fa-undo"></i>'
28805                     }
28806                 ]
28807             },
28808             {
28809                 tag : 'div',
28810                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28811                 action : 'rotate-right',
28812                 cn : [
28813                     {
28814                         tag : 'button',
28815                         cls : 'btn btn-default',
28816                         html : '<i class="fa fa-repeat"></i>'
28817                     }
28818                 ]
28819             }
28820         ]
28821     }
28822 });
28823
28824 /*
28825 * Licence: LGPL
28826 */
28827
28828 /**
28829  * @class Roo.bootstrap.DocumentManager
28830  * @extends Roo.bootstrap.Component
28831  * Bootstrap DocumentManager class
28832  * @cfg {String} paramName default 'imageUpload'
28833  * @cfg {String} toolTipName default 'filename'
28834  * @cfg {String} method default POST
28835  * @cfg {String} url action url
28836  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28837  * @cfg {Boolean} multiple multiple upload default true
28838  * @cfg {Number} thumbSize default 300
28839  * @cfg {String} fieldLabel
28840  * @cfg {Number} labelWidth default 4
28841  * @cfg {String} labelAlign (left|top) default left
28842  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28843 * @cfg {Number} labellg set the width of label (1-12)
28844  * @cfg {Number} labelmd set the width of label (1-12)
28845  * @cfg {Number} labelsm set the width of label (1-12)
28846  * @cfg {Number} labelxs set the width of label (1-12)
28847  * 
28848  * @constructor
28849  * Create a new DocumentManager
28850  * @param {Object} config The config object
28851  */
28852
28853 Roo.bootstrap.DocumentManager = function(config){
28854     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28855     
28856     this.files = [];
28857     this.delegates = [];
28858     
28859     this.addEvents({
28860         /**
28861          * @event initial
28862          * Fire when initial the DocumentManager
28863          * @param {Roo.bootstrap.DocumentManager} this
28864          */
28865         "initial" : true,
28866         /**
28867          * @event inspect
28868          * inspect selected file
28869          * @param {Roo.bootstrap.DocumentManager} this
28870          * @param {File} file
28871          */
28872         "inspect" : true,
28873         /**
28874          * @event exception
28875          * Fire when xhr load exception
28876          * @param {Roo.bootstrap.DocumentManager} this
28877          * @param {XMLHttpRequest} xhr
28878          */
28879         "exception" : true,
28880         /**
28881          * @event afterupload
28882          * Fire when xhr load exception
28883          * @param {Roo.bootstrap.DocumentManager} this
28884          * @param {XMLHttpRequest} xhr
28885          */
28886         "afterupload" : true,
28887         /**
28888          * @event prepare
28889          * prepare the form data
28890          * @param {Roo.bootstrap.DocumentManager} this
28891          * @param {Object} formData
28892          */
28893         "prepare" : true,
28894         /**
28895          * @event remove
28896          * Fire when remove the file
28897          * @param {Roo.bootstrap.DocumentManager} this
28898          * @param {Object} file
28899          */
28900         "remove" : true,
28901         /**
28902          * @event refresh
28903          * Fire after refresh the file
28904          * @param {Roo.bootstrap.DocumentManager} this
28905          */
28906         "refresh" : true,
28907         /**
28908          * @event click
28909          * Fire after click the image
28910          * @param {Roo.bootstrap.DocumentManager} this
28911          * @param {Object} file
28912          */
28913         "click" : true,
28914         /**
28915          * @event edit
28916          * Fire when upload a image and editable set to true
28917          * @param {Roo.bootstrap.DocumentManager} this
28918          * @param {Object} file
28919          */
28920         "edit" : true,
28921         /**
28922          * @event beforeselectfile
28923          * Fire before select file
28924          * @param {Roo.bootstrap.DocumentManager} this
28925          */
28926         "beforeselectfile" : true,
28927         /**
28928          * @event process
28929          * Fire before process file
28930          * @param {Roo.bootstrap.DocumentManager} this
28931          * @param {Object} file
28932          */
28933         "process" : true,
28934         /**
28935          * @event previewrendered
28936          * Fire when preview rendered
28937          * @param {Roo.bootstrap.DocumentManager} this
28938          * @param {Object} file
28939          */
28940         "previewrendered" : true,
28941         /**
28942          */
28943         "previewResize" : true
28944         
28945     });
28946 };
28947
28948 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28949     
28950     boxes : 0,
28951     inputName : '',
28952     thumbSize : 300,
28953     multiple : true,
28954     files : false,
28955     method : 'POST',
28956     url : '',
28957     paramName : 'imageUpload',
28958     toolTipName : 'filename',
28959     fieldLabel : '',
28960     labelWidth : 4,
28961     labelAlign : 'left',
28962     editable : true,
28963     delegates : false,
28964     xhr : false, 
28965     
28966     labellg : 0,
28967     labelmd : 0,
28968     labelsm : 0,
28969     labelxs : 0,
28970     
28971     getAutoCreate : function()
28972     {   
28973         var managerWidget = {
28974             tag : 'div',
28975             cls : 'roo-document-manager',
28976             cn : [
28977                 {
28978                     tag : 'input',
28979                     cls : 'roo-document-manager-selector',
28980                     type : 'file'
28981                 },
28982                 {
28983                     tag : 'div',
28984                     cls : 'roo-document-manager-uploader',
28985                     cn : [
28986                         {
28987                             tag : 'div',
28988                             cls : 'roo-document-manager-upload-btn',
28989                             html : '<i class="fa fa-plus"></i>'
28990                         }
28991                     ]
28992                     
28993                 }
28994             ]
28995         };
28996         
28997         var content = [
28998             {
28999                 tag : 'div',
29000                 cls : 'column col-md-12',
29001                 cn : managerWidget
29002             }
29003         ];
29004         
29005         if(this.fieldLabel.length){
29006             
29007             content = [
29008                 {
29009                     tag : 'div',
29010                     cls : 'column col-md-12',
29011                     html : this.fieldLabel
29012                 },
29013                 {
29014                     tag : 'div',
29015                     cls : 'column col-md-12',
29016                     cn : managerWidget
29017                 }
29018             ];
29019
29020             if(this.labelAlign == 'left'){
29021                 content = [
29022                     {
29023                         tag : 'div',
29024                         cls : 'column',
29025                         html : this.fieldLabel
29026                     },
29027                     {
29028                         tag : 'div',
29029                         cls : 'column',
29030                         cn : managerWidget
29031                     }
29032                 ];
29033                 
29034                 if(this.labelWidth > 12){
29035                     content[0].style = "width: " + this.labelWidth + 'px';
29036                 }
29037
29038                 if(this.labelWidth < 13 && this.labelmd == 0){
29039                     this.labelmd = this.labelWidth;
29040                 }
29041
29042                 if(this.labellg > 0){
29043                     content[0].cls += ' col-lg-' + this.labellg;
29044                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29045                 }
29046
29047                 if(this.labelmd > 0){
29048                     content[0].cls += ' col-md-' + this.labelmd;
29049                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29050                 }
29051
29052                 if(this.labelsm > 0){
29053                     content[0].cls += ' col-sm-' + this.labelsm;
29054                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29055                 }
29056
29057                 if(this.labelxs > 0){
29058                     content[0].cls += ' col-xs-' + this.labelxs;
29059                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29060                 }
29061                 
29062             }
29063         }
29064         
29065         var cfg = {
29066             tag : 'div',
29067             cls : 'row clearfix',
29068             cn : content
29069         };
29070         
29071         return cfg;
29072         
29073     },
29074     
29075     initEvents : function()
29076     {
29077         this.managerEl = this.el.select('.roo-document-manager', true).first();
29078         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29079         
29080         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29081         this.selectorEl.hide();
29082         
29083         if(this.multiple){
29084             this.selectorEl.attr('multiple', 'multiple');
29085         }
29086         
29087         this.selectorEl.on('change', this.onFileSelected, this);
29088         
29089         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29090         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29091         
29092         this.uploader.on('click', this.onUploaderClick, this);
29093         
29094         this.renderProgressDialog();
29095         
29096         var _this = this;
29097         
29098         window.addEventListener("resize", function() { _this.refresh(); } );
29099         
29100         this.fireEvent('initial', this);
29101     },
29102     
29103     renderProgressDialog : function()
29104     {
29105         var _this = this;
29106         
29107         this.progressDialog = new Roo.bootstrap.Modal({
29108             cls : 'roo-document-manager-progress-dialog',
29109             allow_close : false,
29110             title : '',
29111             buttons : [
29112                 {
29113                     name  :'cancel',
29114                     weight : 'danger',
29115                     html : 'Cancel'
29116                 }
29117             ], 
29118             listeners : { 
29119                 btnclick : function() {
29120                     _this.uploadCancel();
29121                     this.hide();
29122                 }
29123             }
29124         });
29125          
29126         this.progressDialog.render(Roo.get(document.body));
29127          
29128         this.progress = new Roo.bootstrap.Progress({
29129             cls : 'roo-document-manager-progress',
29130             active : true,
29131             striped : true
29132         });
29133         
29134         this.progress.render(this.progressDialog.getChildContainer());
29135         
29136         this.progressBar = new Roo.bootstrap.ProgressBar({
29137             cls : 'roo-document-manager-progress-bar',
29138             aria_valuenow : 0,
29139             aria_valuemin : 0,
29140             aria_valuemax : 12,
29141             panel : 'success'
29142         });
29143         
29144         this.progressBar.render(this.progress.getChildContainer());
29145     },
29146     
29147     onUploaderClick : function(e)
29148     {
29149         e.preventDefault();
29150      
29151         if(this.fireEvent('beforeselectfile', this) != false){
29152             this.selectorEl.dom.click();
29153         }
29154         
29155     },
29156     
29157     onFileSelected : function(e)
29158     {
29159         e.preventDefault();
29160         
29161         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29162             return;
29163         }
29164         
29165         Roo.each(this.selectorEl.dom.files, function(file){
29166             if(this.fireEvent('inspect', this, file) != false){
29167                 this.files.push(file);
29168             }
29169         }, this);
29170         
29171         this.queue();
29172         
29173     },
29174     
29175     queue : function()
29176     {
29177         this.selectorEl.dom.value = '';
29178         
29179         if(!this.files || !this.files.length){
29180             return;
29181         }
29182         
29183         if(this.boxes > 0 && this.files.length > this.boxes){
29184             this.files = this.files.slice(0, this.boxes);
29185         }
29186         
29187         this.uploader.show();
29188         
29189         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29190             this.uploader.hide();
29191         }
29192         
29193         var _this = this;
29194         
29195         var files = [];
29196         
29197         var docs = [];
29198         
29199         Roo.each(this.files, function(file){
29200             
29201             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29202                 var f = this.renderPreview(file);
29203                 files.push(f);
29204                 return;
29205             }
29206             
29207             if(file.type.indexOf('image') != -1){
29208                 this.delegates.push(
29209                     (function(){
29210                         _this.process(file);
29211                     }).createDelegate(this)
29212                 );
29213         
29214                 return;
29215             }
29216             
29217             docs.push(
29218                 (function(){
29219                     _this.process(file);
29220                 }).createDelegate(this)
29221             );
29222             
29223         }, this);
29224         
29225         this.files = files;
29226         
29227         this.delegates = this.delegates.concat(docs);
29228         
29229         if(!this.delegates.length){
29230             this.refresh();
29231             return;
29232         }
29233         
29234         this.progressBar.aria_valuemax = this.delegates.length;
29235         
29236         this.arrange();
29237         
29238         return;
29239     },
29240     
29241     arrange : function()
29242     {
29243         if(!this.delegates.length){
29244             this.progressDialog.hide();
29245             this.refresh();
29246             return;
29247         }
29248         
29249         var delegate = this.delegates.shift();
29250         
29251         this.progressDialog.show();
29252         
29253         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29254         
29255         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29256         
29257         delegate();
29258     },
29259     
29260     refresh : function()
29261     {
29262         this.uploader.show();
29263         
29264         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29265             this.uploader.hide();
29266         }
29267         
29268         Roo.isTouch ? this.closable(false) : this.closable(true);
29269         
29270         this.fireEvent('refresh', this);
29271     },
29272     
29273     onRemove : function(e, el, o)
29274     {
29275         e.preventDefault();
29276         
29277         this.fireEvent('remove', this, o);
29278         
29279     },
29280     
29281     remove : function(o)
29282     {
29283         var files = [];
29284         
29285         Roo.each(this.files, function(file){
29286             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29287                 files.push(file);
29288                 return;
29289             }
29290
29291             o.target.remove();
29292
29293         }, this);
29294         
29295         this.files = files;
29296         
29297         this.refresh();
29298     },
29299     
29300     clear : function()
29301     {
29302         Roo.each(this.files, function(file){
29303             if(!file.target){
29304                 return;
29305             }
29306             
29307             file.target.remove();
29308
29309         }, this);
29310         
29311         this.files = [];
29312         
29313         this.refresh();
29314     },
29315     
29316     onClick : function(e, el, o)
29317     {
29318         e.preventDefault();
29319         
29320         this.fireEvent('click', this, o);
29321         
29322     },
29323     
29324     closable : function(closable)
29325     {
29326         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29327             
29328             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29329             
29330             if(closable){
29331                 el.show();
29332                 return;
29333             }
29334             
29335             el.hide();
29336             
29337         }, this);
29338     },
29339     
29340     xhrOnLoad : function(xhr)
29341     {
29342         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29343             el.remove();
29344         }, this);
29345         
29346         if (xhr.readyState !== 4) {
29347             this.arrange();
29348             this.fireEvent('exception', this, xhr);
29349             return;
29350         }
29351
29352         var response = Roo.decode(xhr.responseText);
29353         
29354         if(!response.success){
29355             this.arrange();
29356             this.fireEvent('exception', this, xhr);
29357             return;
29358         }
29359         
29360         var file = this.renderPreview(response.data);
29361         
29362         this.files.push(file);
29363         
29364         this.arrange();
29365         
29366         this.fireEvent('afterupload', this, xhr);
29367         
29368     },
29369     
29370     xhrOnError : function(xhr)
29371     {
29372         Roo.log('xhr on error');
29373         
29374         var response = Roo.decode(xhr.responseText);
29375           
29376         Roo.log(response);
29377         
29378         this.arrange();
29379     },
29380     
29381     process : function(file)
29382     {
29383         if(this.fireEvent('process', this, file) !== false){
29384             if(this.editable && file.type.indexOf('image') != -1){
29385                 this.fireEvent('edit', this, file);
29386                 return;
29387             }
29388
29389             this.uploadStart(file, false);
29390
29391             return;
29392         }
29393         
29394     },
29395     
29396     uploadStart : function(file, crop)
29397     {
29398         this.xhr = new XMLHttpRequest();
29399         
29400         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29401             this.arrange();
29402             return;
29403         }
29404         
29405         file.xhr = this.xhr;
29406             
29407         this.managerEl.createChild({
29408             tag : 'div',
29409             cls : 'roo-document-manager-loading',
29410             cn : [
29411                 {
29412                     tag : 'div',
29413                     tooltip : file.name,
29414                     cls : 'roo-document-manager-thumb',
29415                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29416                 }
29417             ]
29418
29419         });
29420
29421         this.xhr.open(this.method, this.url, true);
29422         
29423         var headers = {
29424             "Accept": "application/json",
29425             "Cache-Control": "no-cache",
29426             "X-Requested-With": "XMLHttpRequest"
29427         };
29428         
29429         for (var headerName in headers) {
29430             var headerValue = headers[headerName];
29431             if (headerValue) {
29432                 this.xhr.setRequestHeader(headerName, headerValue);
29433             }
29434         }
29435         
29436         var _this = this;
29437         
29438         this.xhr.onload = function()
29439         {
29440             _this.xhrOnLoad(_this.xhr);
29441         }
29442         
29443         this.xhr.onerror = function()
29444         {
29445             _this.xhrOnError(_this.xhr);
29446         }
29447         
29448         var formData = new FormData();
29449
29450         formData.append('returnHTML', 'NO');
29451         
29452         if(crop){
29453             formData.append('crop', crop);
29454         }
29455         
29456         formData.append(this.paramName, file, file.name);
29457         
29458         var options = {
29459             file : file, 
29460             manually : false
29461         };
29462         
29463         if(this.fireEvent('prepare', this, formData, options) != false){
29464             
29465             if(options.manually){
29466                 return;
29467             }
29468             
29469             this.xhr.send(formData);
29470             return;
29471         };
29472         
29473         this.uploadCancel();
29474     },
29475     
29476     uploadCancel : function()
29477     {
29478         if (this.xhr) {
29479             this.xhr.abort();
29480         }
29481         
29482         this.delegates = [];
29483         
29484         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29485             el.remove();
29486         }, this);
29487         
29488         this.arrange();
29489     },
29490     
29491     renderPreview : function(file)
29492     {
29493         if(typeof(file.target) != 'undefined' && file.target){
29494             return file;
29495         }
29496         
29497         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29498         
29499         var previewEl = this.managerEl.createChild({
29500             tag : 'div',
29501             cls : 'roo-document-manager-preview',
29502             cn : [
29503                 {
29504                     tag : 'div',
29505                     tooltip : file[this.toolTipName],
29506                     cls : 'roo-document-manager-thumb',
29507                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29508                 },
29509                 {
29510                     tag : 'button',
29511                     cls : 'close',
29512                     html : '<i class="fa fa-times-circle"></i>'
29513                 }
29514             ]
29515         });
29516
29517         var close = previewEl.select('button.close', true).first();
29518
29519         close.on('click', this.onRemove, this, file);
29520
29521         file.target = previewEl;
29522
29523         var image = previewEl.select('img', true).first();
29524         
29525         var _this = this;
29526         
29527         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29528         
29529         image.on('click', this.onClick, this, file);
29530         
29531         this.fireEvent('previewrendered', this, file);
29532         
29533         return file;
29534         
29535     },
29536     
29537     onPreviewLoad : function(file, image)
29538     {
29539         if(typeof(file.target) == 'undefined' || !file.target){
29540             return;
29541         }
29542         
29543         var width = image.dom.naturalWidth || image.dom.width;
29544         var height = image.dom.naturalHeight || image.dom.height;
29545         
29546         if(!this.previewResize) {
29547             return;
29548         }
29549         
29550         if(width > height){
29551             file.target.addClass('wide');
29552             return;
29553         }
29554         
29555         file.target.addClass('tall');
29556         return;
29557         
29558     },
29559     
29560     uploadFromSource : function(file, crop)
29561     {
29562         this.xhr = new XMLHttpRequest();
29563         
29564         this.managerEl.createChild({
29565             tag : 'div',
29566             cls : 'roo-document-manager-loading',
29567             cn : [
29568                 {
29569                     tag : 'div',
29570                     tooltip : file.name,
29571                     cls : 'roo-document-manager-thumb',
29572                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29573                 }
29574             ]
29575
29576         });
29577
29578         this.xhr.open(this.method, this.url, true);
29579         
29580         var headers = {
29581             "Accept": "application/json",
29582             "Cache-Control": "no-cache",
29583             "X-Requested-With": "XMLHttpRequest"
29584         };
29585         
29586         for (var headerName in headers) {
29587             var headerValue = headers[headerName];
29588             if (headerValue) {
29589                 this.xhr.setRequestHeader(headerName, headerValue);
29590             }
29591         }
29592         
29593         var _this = this;
29594         
29595         this.xhr.onload = function()
29596         {
29597             _this.xhrOnLoad(_this.xhr);
29598         }
29599         
29600         this.xhr.onerror = function()
29601         {
29602             _this.xhrOnError(_this.xhr);
29603         }
29604         
29605         var formData = new FormData();
29606
29607         formData.append('returnHTML', 'NO');
29608         
29609         formData.append('crop', crop);
29610         
29611         if(typeof(file.filename) != 'undefined'){
29612             formData.append('filename', file.filename);
29613         }
29614         
29615         if(typeof(file.mimetype) != 'undefined'){
29616             formData.append('mimetype', file.mimetype);
29617         }
29618         
29619         Roo.log(formData);
29620         
29621         if(this.fireEvent('prepare', this, formData) != false){
29622             this.xhr.send(formData);
29623         };
29624     }
29625 });
29626
29627 /*
29628 * Licence: LGPL
29629 */
29630
29631 /**
29632  * @class Roo.bootstrap.DocumentViewer
29633  * @extends Roo.bootstrap.Component
29634  * Bootstrap DocumentViewer class
29635  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29636  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29637  * 
29638  * @constructor
29639  * Create a new DocumentViewer
29640  * @param {Object} config The config object
29641  */
29642
29643 Roo.bootstrap.DocumentViewer = function(config){
29644     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29645     
29646     this.addEvents({
29647         /**
29648          * @event initial
29649          * Fire after initEvent
29650          * @param {Roo.bootstrap.DocumentViewer} this
29651          */
29652         "initial" : true,
29653         /**
29654          * @event click
29655          * Fire after click
29656          * @param {Roo.bootstrap.DocumentViewer} this
29657          */
29658         "click" : true,
29659         /**
29660          * @event download
29661          * Fire after download button
29662          * @param {Roo.bootstrap.DocumentViewer} this
29663          */
29664         "download" : true,
29665         /**
29666          * @event trash
29667          * Fire after trash button
29668          * @param {Roo.bootstrap.DocumentViewer} this
29669          */
29670         "trash" : true
29671         
29672     });
29673 };
29674
29675 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29676     
29677     showDownload : true,
29678     
29679     showTrash : true,
29680     
29681     getAutoCreate : function()
29682     {
29683         var cfg = {
29684             tag : 'div',
29685             cls : 'roo-document-viewer',
29686             cn : [
29687                 {
29688                     tag : 'div',
29689                     cls : 'roo-document-viewer-body',
29690                     cn : [
29691                         {
29692                             tag : 'div',
29693                             cls : 'roo-document-viewer-thumb',
29694                             cn : [
29695                                 {
29696                                     tag : 'img',
29697                                     cls : 'roo-document-viewer-image'
29698                                 }
29699                             ]
29700                         }
29701                     ]
29702                 },
29703                 {
29704                     tag : 'div',
29705                     cls : 'roo-document-viewer-footer',
29706                     cn : {
29707                         tag : 'div',
29708                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29709                         cn : [
29710                             {
29711                                 tag : 'div',
29712                                 cls : 'btn-group roo-document-viewer-download',
29713                                 cn : [
29714                                     {
29715                                         tag : 'button',
29716                                         cls : 'btn btn-default',
29717                                         html : '<i class="fa fa-download"></i>'
29718                                     }
29719                                 ]
29720                             },
29721                             {
29722                                 tag : 'div',
29723                                 cls : 'btn-group roo-document-viewer-trash',
29724                                 cn : [
29725                                     {
29726                                         tag : 'button',
29727                                         cls : 'btn btn-default',
29728                                         html : '<i class="fa fa-trash"></i>'
29729                                     }
29730                                 ]
29731                             }
29732                         ]
29733                     }
29734                 }
29735             ]
29736         };
29737         
29738         return cfg;
29739     },
29740     
29741     initEvents : function()
29742     {
29743         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29744         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29745         
29746         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29747         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29748         
29749         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29750         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29751         
29752         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29753         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29754         
29755         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29756         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29757         
29758         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29759         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29760         
29761         this.bodyEl.on('click', this.onClick, this);
29762         this.downloadBtn.on('click', this.onDownload, this);
29763         this.trashBtn.on('click', this.onTrash, this);
29764         
29765         this.downloadBtn.hide();
29766         this.trashBtn.hide();
29767         
29768         if(this.showDownload){
29769             this.downloadBtn.show();
29770         }
29771         
29772         if(this.showTrash){
29773             this.trashBtn.show();
29774         }
29775         
29776         if(!this.showDownload && !this.showTrash) {
29777             this.footerEl.hide();
29778         }
29779         
29780     },
29781     
29782     initial : function()
29783     {
29784         this.fireEvent('initial', this);
29785         
29786     },
29787     
29788     onClick : function(e)
29789     {
29790         e.preventDefault();
29791         
29792         this.fireEvent('click', this);
29793     },
29794     
29795     onDownload : function(e)
29796     {
29797         e.preventDefault();
29798         
29799         this.fireEvent('download', this);
29800     },
29801     
29802     onTrash : function(e)
29803     {
29804         e.preventDefault();
29805         
29806         this.fireEvent('trash', this);
29807     }
29808     
29809 });
29810 /*
29811  * - LGPL
29812  *
29813  * nav progress bar
29814  * 
29815  */
29816
29817 /**
29818  * @class Roo.bootstrap.NavProgressBar
29819  * @extends Roo.bootstrap.Component
29820  * Bootstrap NavProgressBar class
29821  * 
29822  * @constructor
29823  * Create a new nav progress bar
29824  * @param {Object} config The config object
29825  */
29826
29827 Roo.bootstrap.NavProgressBar = function(config){
29828     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29829
29830     this.bullets = this.bullets || [];
29831    
29832 //    Roo.bootstrap.NavProgressBar.register(this);
29833      this.addEvents({
29834         /**
29835              * @event changed
29836              * Fires when the active item changes
29837              * @param {Roo.bootstrap.NavProgressBar} this
29838              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29839              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29840          */
29841         'changed': true
29842      });
29843     
29844 };
29845
29846 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29847     
29848     bullets : [],
29849     barItems : [],
29850     
29851     getAutoCreate : function()
29852     {
29853         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29854         
29855         cfg = {
29856             tag : 'div',
29857             cls : 'roo-navigation-bar-group',
29858             cn : [
29859                 {
29860                     tag : 'div',
29861                     cls : 'roo-navigation-top-bar'
29862                 },
29863                 {
29864                     tag : 'div',
29865                     cls : 'roo-navigation-bullets-bar',
29866                     cn : [
29867                         {
29868                             tag : 'ul',
29869                             cls : 'roo-navigation-bar'
29870                         }
29871                     ]
29872                 },
29873                 
29874                 {
29875                     tag : 'div',
29876                     cls : 'roo-navigation-bottom-bar'
29877                 }
29878             ]
29879             
29880         };
29881         
29882         return cfg;
29883         
29884     },
29885     
29886     initEvents: function() 
29887     {
29888         
29889     },
29890     
29891     onRender : function(ct, position) 
29892     {
29893         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29894         
29895         if(this.bullets.length){
29896             Roo.each(this.bullets, function(b){
29897                this.addItem(b);
29898             }, this);
29899         }
29900         
29901         this.format();
29902         
29903     },
29904     
29905     addItem : function(cfg)
29906     {
29907         var item = new Roo.bootstrap.NavProgressItem(cfg);
29908         
29909         item.parentId = this.id;
29910         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29911         
29912         if(cfg.html){
29913             var top = new Roo.bootstrap.Element({
29914                 tag : 'div',
29915                 cls : 'roo-navigation-bar-text'
29916             });
29917             
29918             var bottom = new Roo.bootstrap.Element({
29919                 tag : 'div',
29920                 cls : 'roo-navigation-bar-text'
29921             });
29922             
29923             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29924             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29925             
29926             var topText = new Roo.bootstrap.Element({
29927                 tag : 'span',
29928                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29929             });
29930             
29931             var bottomText = new Roo.bootstrap.Element({
29932                 tag : 'span',
29933                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29934             });
29935             
29936             topText.onRender(top.el, null);
29937             bottomText.onRender(bottom.el, null);
29938             
29939             item.topEl = top;
29940             item.bottomEl = bottom;
29941         }
29942         
29943         this.barItems.push(item);
29944         
29945         return item;
29946     },
29947     
29948     getActive : function()
29949     {
29950         var active = false;
29951         
29952         Roo.each(this.barItems, function(v){
29953             
29954             if (!v.isActive()) {
29955                 return;
29956             }
29957             
29958             active = v;
29959             return false;
29960             
29961         });
29962         
29963         return active;
29964     },
29965     
29966     setActiveItem : function(item)
29967     {
29968         var prev = false;
29969         
29970         Roo.each(this.barItems, function(v){
29971             if (v.rid == item.rid) {
29972                 return ;
29973             }
29974             
29975             if (v.isActive()) {
29976                 v.setActive(false);
29977                 prev = v;
29978             }
29979         });
29980
29981         item.setActive(true);
29982         
29983         this.fireEvent('changed', this, item, prev);
29984     },
29985     
29986     getBarItem: function(rid)
29987     {
29988         var ret = false;
29989         
29990         Roo.each(this.barItems, function(e) {
29991             if (e.rid != rid) {
29992                 return;
29993             }
29994             
29995             ret =  e;
29996             return false;
29997         });
29998         
29999         return ret;
30000     },
30001     
30002     indexOfItem : function(item)
30003     {
30004         var index = false;
30005         
30006         Roo.each(this.barItems, function(v, i){
30007             
30008             if (v.rid != item.rid) {
30009                 return;
30010             }
30011             
30012             index = i;
30013             return false
30014         });
30015         
30016         return index;
30017     },
30018     
30019     setActiveNext : function()
30020     {
30021         var i = this.indexOfItem(this.getActive());
30022         
30023         if (i > this.barItems.length) {
30024             return;
30025         }
30026         
30027         this.setActiveItem(this.barItems[i+1]);
30028     },
30029     
30030     setActivePrev : function()
30031     {
30032         var i = this.indexOfItem(this.getActive());
30033         
30034         if (i  < 1) {
30035             return;
30036         }
30037         
30038         this.setActiveItem(this.barItems[i-1]);
30039     },
30040     
30041     format : function()
30042     {
30043         if(!this.barItems.length){
30044             return;
30045         }
30046      
30047         var width = 100 / this.barItems.length;
30048         
30049         Roo.each(this.barItems, function(i){
30050             i.el.setStyle('width', width + '%');
30051             i.topEl.el.setStyle('width', width + '%');
30052             i.bottomEl.el.setStyle('width', width + '%');
30053         }, this);
30054         
30055     }
30056     
30057 });
30058 /*
30059  * - LGPL
30060  *
30061  * Nav Progress Item
30062  * 
30063  */
30064
30065 /**
30066  * @class Roo.bootstrap.NavProgressItem
30067  * @extends Roo.bootstrap.Component
30068  * Bootstrap NavProgressItem class
30069  * @cfg {String} rid the reference id
30070  * @cfg {Boolean} active (true|false) Is item active default false
30071  * @cfg {Boolean} disabled (true|false) Is item active default false
30072  * @cfg {String} html
30073  * @cfg {String} position (top|bottom) text position default bottom
30074  * @cfg {String} icon show icon instead of number
30075  * 
30076  * @constructor
30077  * Create a new NavProgressItem
30078  * @param {Object} config The config object
30079  */
30080 Roo.bootstrap.NavProgressItem = function(config){
30081     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30082     this.addEvents({
30083         // raw events
30084         /**
30085          * @event click
30086          * The raw click event for the entire grid.
30087          * @param {Roo.bootstrap.NavProgressItem} this
30088          * @param {Roo.EventObject} e
30089          */
30090         "click" : true
30091     });
30092    
30093 };
30094
30095 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30096     
30097     rid : '',
30098     active : false,
30099     disabled : false,
30100     html : '',
30101     position : 'bottom',
30102     icon : false,
30103     
30104     getAutoCreate : function()
30105     {
30106         var iconCls = 'roo-navigation-bar-item-icon';
30107         
30108         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30109         
30110         var cfg = {
30111             tag: 'li',
30112             cls: 'roo-navigation-bar-item',
30113             cn : [
30114                 {
30115                     tag : 'i',
30116                     cls : iconCls
30117                 }
30118             ]
30119         };
30120         
30121         if(this.active){
30122             cfg.cls += ' active';
30123         }
30124         if(this.disabled){
30125             cfg.cls += ' disabled';
30126         }
30127         
30128         return cfg;
30129     },
30130     
30131     disable : function()
30132     {
30133         this.setDisabled(true);
30134     },
30135     
30136     enable : function()
30137     {
30138         this.setDisabled(false);
30139     },
30140     
30141     initEvents: function() 
30142     {
30143         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30144         
30145         this.iconEl.on('click', this.onClick, this);
30146     },
30147     
30148     onClick : function(e)
30149     {
30150         e.preventDefault();
30151         
30152         if(this.disabled){
30153             return;
30154         }
30155         
30156         if(this.fireEvent('click', this, e) === false){
30157             return;
30158         };
30159         
30160         this.parent().setActiveItem(this);
30161     },
30162     
30163     isActive: function () 
30164     {
30165         return this.active;
30166     },
30167     
30168     setActive : function(state)
30169     {
30170         if(this.active == state){
30171             return;
30172         }
30173         
30174         this.active = state;
30175         
30176         if (state) {
30177             this.el.addClass('active');
30178             return;
30179         }
30180         
30181         this.el.removeClass('active');
30182         
30183         return;
30184     },
30185     
30186     setDisabled : function(state)
30187     {
30188         if(this.disabled == state){
30189             return;
30190         }
30191         
30192         this.disabled = state;
30193         
30194         if (state) {
30195             this.el.addClass('disabled');
30196             return;
30197         }
30198         
30199         this.el.removeClass('disabled');
30200     },
30201     
30202     tooltipEl : function()
30203     {
30204         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30205     }
30206 });
30207  
30208
30209  /*
30210  * - LGPL
30211  *
30212  * FieldLabel
30213  * 
30214  */
30215
30216 /**
30217  * @class Roo.bootstrap.FieldLabel
30218  * @extends Roo.bootstrap.Component
30219  * Bootstrap FieldLabel class
30220  * @cfg {String} html contents of the element
30221  * @cfg {String} tag tag of the element default label
30222  * @cfg {String} cls class of the element
30223  * @cfg {String} target label target 
30224  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30225  * @cfg {String} invalidClass default "text-warning"
30226  * @cfg {String} validClass default "text-success"
30227  * @cfg {String} iconTooltip default "This field is required"
30228  * @cfg {String} indicatorpos (left|right) default left
30229  * 
30230  * @constructor
30231  * Create a new FieldLabel
30232  * @param {Object} config The config object
30233  */
30234
30235 Roo.bootstrap.FieldLabel = function(config){
30236     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30237     
30238     this.addEvents({
30239             /**
30240              * @event invalid
30241              * Fires after the field has been marked as invalid.
30242              * @param {Roo.form.FieldLabel} this
30243              * @param {String} msg The validation message
30244              */
30245             invalid : true,
30246             /**
30247              * @event valid
30248              * Fires after the field has been validated with no errors.
30249              * @param {Roo.form.FieldLabel} this
30250              */
30251             valid : true
30252         });
30253 };
30254
30255 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30256     
30257     tag: 'label',
30258     cls: '',
30259     html: '',
30260     target: '',
30261     allowBlank : true,
30262     invalidClass : 'has-warning',
30263     validClass : 'has-success',
30264     iconTooltip : 'This field is required',
30265     indicatorpos : 'left',
30266     
30267     getAutoCreate : function(){
30268         
30269         var cls = "";
30270         if (!this.allowBlank) {
30271             cls  = "visible";
30272         }
30273         
30274         var cfg = {
30275             tag : this.tag,
30276             cls : 'roo-bootstrap-field-label ' + this.cls,
30277             for : this.target,
30278             cn : [
30279                 {
30280                     tag : 'i',
30281                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30282                     tooltip : this.iconTooltip
30283                 },
30284                 {
30285                     tag : 'span',
30286                     html : this.html
30287                 }
30288             ] 
30289         };
30290         
30291         if(this.indicatorpos == 'right'){
30292             var cfg = {
30293                 tag : this.tag,
30294                 cls : 'roo-bootstrap-field-label ' + this.cls,
30295                 for : this.target,
30296                 cn : [
30297                     {
30298                         tag : 'span',
30299                         html : this.html
30300                     },
30301                     {
30302                         tag : 'i',
30303                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30304                         tooltip : this.iconTooltip
30305                     }
30306                 ] 
30307             };
30308         }
30309         
30310         return cfg;
30311     },
30312     
30313     initEvents: function() 
30314     {
30315         Roo.bootstrap.Element.superclass.initEvents.call(this);
30316         
30317         this.indicator = this.indicatorEl();
30318         
30319         if(this.indicator){
30320             this.indicator.removeClass('visible');
30321             this.indicator.addClass('invisible');
30322         }
30323         
30324         Roo.bootstrap.FieldLabel.register(this);
30325     },
30326     
30327     indicatorEl : function()
30328     {
30329         var indicator = this.el.select('i.roo-required-indicator',true).first();
30330         
30331         if(!indicator){
30332             return false;
30333         }
30334         
30335         return indicator;
30336         
30337     },
30338     
30339     /**
30340      * Mark this field as valid
30341      */
30342     markValid : function()
30343     {
30344         if(this.indicator){
30345             this.indicator.removeClass('visible');
30346             this.indicator.addClass('invisible');
30347         }
30348         
30349         this.el.removeClass(this.invalidClass);
30350         
30351         this.el.addClass(this.validClass);
30352         
30353         this.fireEvent('valid', this);
30354     },
30355     
30356     /**
30357      * Mark this field as invalid
30358      * @param {String} msg The validation message
30359      */
30360     markInvalid : function(msg)
30361     {
30362         if(this.indicator){
30363             this.indicator.removeClass('invisible');
30364             this.indicator.addClass('visible');
30365         }
30366         
30367         this.el.removeClass(this.validClass);
30368         
30369         this.el.addClass(this.invalidClass);
30370         
30371         this.fireEvent('invalid', this, msg);
30372     }
30373     
30374    
30375 });
30376
30377 Roo.apply(Roo.bootstrap.FieldLabel, {
30378     
30379     groups: {},
30380     
30381      /**
30382     * register a FieldLabel Group
30383     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30384     */
30385     register : function(label)
30386     {
30387         if(this.groups.hasOwnProperty(label.target)){
30388             return;
30389         }
30390      
30391         this.groups[label.target] = label;
30392         
30393     },
30394     /**
30395     * fetch a FieldLabel Group based on the target
30396     * @param {string} target
30397     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30398     */
30399     get: function(target) {
30400         if (typeof(this.groups[target]) == 'undefined') {
30401             return false;
30402         }
30403         
30404         return this.groups[target] ;
30405     }
30406 });
30407
30408  
30409
30410  /*
30411  * - LGPL
30412  *
30413  * page DateSplitField.
30414  * 
30415  */
30416
30417
30418 /**
30419  * @class Roo.bootstrap.DateSplitField
30420  * @extends Roo.bootstrap.Component
30421  * Bootstrap DateSplitField class
30422  * @cfg {string} fieldLabel - the label associated
30423  * @cfg {Number} labelWidth set the width of label (0-12)
30424  * @cfg {String} labelAlign (top|left)
30425  * @cfg {Boolean} dayAllowBlank (true|false) default false
30426  * @cfg {Boolean} monthAllowBlank (true|false) default false
30427  * @cfg {Boolean} yearAllowBlank (true|false) default false
30428  * @cfg {string} dayPlaceholder 
30429  * @cfg {string} monthPlaceholder
30430  * @cfg {string} yearPlaceholder
30431  * @cfg {string} dayFormat default 'd'
30432  * @cfg {string} monthFormat default 'm'
30433  * @cfg {string} yearFormat default 'Y'
30434  * @cfg {Number} labellg set the width of label (1-12)
30435  * @cfg {Number} labelmd set the width of label (1-12)
30436  * @cfg {Number} labelsm set the width of label (1-12)
30437  * @cfg {Number} labelxs set the width of label (1-12)
30438
30439  *     
30440  * @constructor
30441  * Create a new DateSplitField
30442  * @param {Object} config The config object
30443  */
30444
30445 Roo.bootstrap.DateSplitField = function(config){
30446     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30447     
30448     this.addEvents({
30449         // raw events
30450          /**
30451          * @event years
30452          * getting the data of years
30453          * @param {Roo.bootstrap.DateSplitField} this
30454          * @param {Object} years
30455          */
30456         "years" : true,
30457         /**
30458          * @event days
30459          * getting the data of days
30460          * @param {Roo.bootstrap.DateSplitField} this
30461          * @param {Object} days
30462          */
30463         "days" : true,
30464         /**
30465          * @event invalid
30466          * Fires after the field has been marked as invalid.
30467          * @param {Roo.form.Field} this
30468          * @param {String} msg The validation message
30469          */
30470         invalid : true,
30471        /**
30472          * @event valid
30473          * Fires after the field has been validated with no errors.
30474          * @param {Roo.form.Field} this
30475          */
30476         valid : true
30477     });
30478 };
30479
30480 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30481     
30482     fieldLabel : '',
30483     labelAlign : 'top',
30484     labelWidth : 3,
30485     dayAllowBlank : false,
30486     monthAllowBlank : false,
30487     yearAllowBlank : false,
30488     dayPlaceholder : '',
30489     monthPlaceholder : '',
30490     yearPlaceholder : '',
30491     dayFormat : 'd',
30492     monthFormat : 'm',
30493     yearFormat : 'Y',
30494     isFormField : true,
30495     labellg : 0,
30496     labelmd : 0,
30497     labelsm : 0,
30498     labelxs : 0,
30499     
30500     getAutoCreate : function()
30501     {
30502         var cfg = {
30503             tag : 'div',
30504             cls : 'row roo-date-split-field-group',
30505             cn : [
30506                 {
30507                     tag : 'input',
30508                     type : 'hidden',
30509                     cls : 'form-hidden-field roo-date-split-field-group-value',
30510                     name : this.name
30511                 }
30512             ]
30513         };
30514         
30515         var labelCls = 'col-md-12';
30516         var contentCls = 'col-md-4';
30517         
30518         if(this.fieldLabel){
30519             
30520             var label = {
30521                 tag : 'div',
30522                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30523                 cn : [
30524                     {
30525                         tag : 'label',
30526                         html : this.fieldLabel
30527                     }
30528                 ]
30529             };
30530             
30531             if(this.labelAlign == 'left'){
30532             
30533                 if(this.labelWidth > 12){
30534                     label.style = "width: " + this.labelWidth + 'px';
30535                 }
30536
30537                 if(this.labelWidth < 13 && this.labelmd == 0){
30538                     this.labelmd = this.labelWidth;
30539                 }
30540
30541                 if(this.labellg > 0){
30542                     labelCls = ' col-lg-' + this.labellg;
30543                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30544                 }
30545
30546                 if(this.labelmd > 0){
30547                     labelCls = ' col-md-' + this.labelmd;
30548                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30549                 }
30550
30551                 if(this.labelsm > 0){
30552                     labelCls = ' col-sm-' + this.labelsm;
30553                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30554                 }
30555
30556                 if(this.labelxs > 0){
30557                     labelCls = ' col-xs-' + this.labelxs;
30558                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30559                 }
30560             }
30561             
30562             label.cls += ' ' + labelCls;
30563             
30564             cfg.cn.push(label);
30565         }
30566         
30567         Roo.each(['day', 'month', 'year'], function(t){
30568             cfg.cn.push({
30569                 tag : 'div',
30570                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30571             });
30572         }, this);
30573         
30574         return cfg;
30575     },
30576     
30577     inputEl: function ()
30578     {
30579         return this.el.select('.roo-date-split-field-group-value', true).first();
30580     },
30581     
30582     onRender : function(ct, position) 
30583     {
30584         var _this = this;
30585         
30586         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30587         
30588         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30589         
30590         this.dayField = new Roo.bootstrap.ComboBox({
30591             allowBlank : this.dayAllowBlank,
30592             alwaysQuery : true,
30593             displayField : 'value',
30594             editable : false,
30595             fieldLabel : '',
30596             forceSelection : true,
30597             mode : 'local',
30598             placeholder : this.dayPlaceholder,
30599             selectOnFocus : true,
30600             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30601             triggerAction : 'all',
30602             typeAhead : true,
30603             valueField : 'value',
30604             store : new Roo.data.SimpleStore({
30605                 data : (function() {    
30606                     var days = [];
30607                     _this.fireEvent('days', _this, days);
30608                     return days;
30609                 })(),
30610                 fields : [ 'value' ]
30611             }),
30612             listeners : {
30613                 select : function (_self, record, index)
30614                 {
30615                     _this.setValue(_this.getValue());
30616                 }
30617             }
30618         });
30619
30620         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30621         
30622         this.monthField = new Roo.bootstrap.MonthField({
30623             after : '<i class=\"fa fa-calendar\"></i>',
30624             allowBlank : this.monthAllowBlank,
30625             placeholder : this.monthPlaceholder,
30626             readOnly : true,
30627             listeners : {
30628                 render : function (_self)
30629                 {
30630                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30631                         e.preventDefault();
30632                         _self.focus();
30633                     });
30634                 },
30635                 select : function (_self, oldvalue, newvalue)
30636                 {
30637                     _this.setValue(_this.getValue());
30638                 }
30639             }
30640         });
30641         
30642         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30643         
30644         this.yearField = new Roo.bootstrap.ComboBox({
30645             allowBlank : this.yearAllowBlank,
30646             alwaysQuery : true,
30647             displayField : 'value',
30648             editable : false,
30649             fieldLabel : '',
30650             forceSelection : true,
30651             mode : 'local',
30652             placeholder : this.yearPlaceholder,
30653             selectOnFocus : true,
30654             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30655             triggerAction : 'all',
30656             typeAhead : true,
30657             valueField : 'value',
30658             store : new Roo.data.SimpleStore({
30659                 data : (function() {
30660                     var years = [];
30661                     _this.fireEvent('years', _this, years);
30662                     return years;
30663                 })(),
30664                 fields : [ 'value' ]
30665             }),
30666             listeners : {
30667                 select : function (_self, record, index)
30668                 {
30669                     _this.setValue(_this.getValue());
30670                 }
30671             }
30672         });
30673
30674         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30675     },
30676     
30677     setValue : function(v, format)
30678     {
30679         this.inputEl.dom.value = v;
30680         
30681         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30682         
30683         var d = Date.parseDate(v, f);
30684         
30685         if(!d){
30686             this.validate();
30687             return;
30688         }
30689         
30690         this.setDay(d.format(this.dayFormat));
30691         this.setMonth(d.format(this.monthFormat));
30692         this.setYear(d.format(this.yearFormat));
30693         
30694         this.validate();
30695         
30696         return;
30697     },
30698     
30699     setDay : function(v)
30700     {
30701         this.dayField.setValue(v);
30702         this.inputEl.dom.value = this.getValue();
30703         this.validate();
30704         return;
30705     },
30706     
30707     setMonth : function(v)
30708     {
30709         this.monthField.setValue(v, true);
30710         this.inputEl.dom.value = this.getValue();
30711         this.validate();
30712         return;
30713     },
30714     
30715     setYear : function(v)
30716     {
30717         this.yearField.setValue(v);
30718         this.inputEl.dom.value = this.getValue();
30719         this.validate();
30720         return;
30721     },
30722     
30723     getDay : function()
30724     {
30725         return this.dayField.getValue();
30726     },
30727     
30728     getMonth : function()
30729     {
30730         return this.monthField.getValue();
30731     },
30732     
30733     getYear : function()
30734     {
30735         return this.yearField.getValue();
30736     },
30737     
30738     getValue : function()
30739     {
30740         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30741         
30742         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30743         
30744         return date;
30745     },
30746     
30747     reset : function()
30748     {
30749         this.setDay('');
30750         this.setMonth('');
30751         this.setYear('');
30752         this.inputEl.dom.value = '';
30753         this.validate();
30754         return;
30755     },
30756     
30757     validate : function()
30758     {
30759         var d = this.dayField.validate();
30760         var m = this.monthField.validate();
30761         var y = this.yearField.validate();
30762         
30763         var valid = true;
30764         
30765         if(
30766                 (!this.dayAllowBlank && !d) ||
30767                 (!this.monthAllowBlank && !m) ||
30768                 (!this.yearAllowBlank && !y)
30769         ){
30770             valid = false;
30771         }
30772         
30773         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30774             return valid;
30775         }
30776         
30777         if(valid){
30778             this.markValid();
30779             return valid;
30780         }
30781         
30782         this.markInvalid();
30783         
30784         return valid;
30785     },
30786     
30787     markValid : function()
30788     {
30789         
30790         var label = this.el.select('label', true).first();
30791         var icon = this.el.select('i.fa-star', true).first();
30792
30793         if(label && icon){
30794             icon.remove();
30795         }
30796         
30797         this.fireEvent('valid', this);
30798     },
30799     
30800      /**
30801      * Mark this field as invalid
30802      * @param {String} msg The validation message
30803      */
30804     markInvalid : function(msg)
30805     {
30806         
30807         var label = this.el.select('label', true).first();
30808         var icon = this.el.select('i.fa-star', true).first();
30809
30810         if(label && !icon){
30811             this.el.select('.roo-date-split-field-label', true).createChild({
30812                 tag : 'i',
30813                 cls : 'text-danger fa fa-lg fa-star',
30814                 tooltip : 'This field is required',
30815                 style : 'margin-right:5px;'
30816             }, label, true);
30817         }
30818         
30819         this.fireEvent('invalid', this, msg);
30820     },
30821     
30822     clearInvalid : function()
30823     {
30824         var label = this.el.select('label', true).first();
30825         var icon = this.el.select('i.fa-star', true).first();
30826
30827         if(label && icon){
30828             icon.remove();
30829         }
30830         
30831         this.fireEvent('valid', this);
30832     },
30833     
30834     getName: function()
30835     {
30836         return this.name;
30837     }
30838     
30839 });
30840
30841  /**
30842  *
30843  * This is based on 
30844  * http://masonry.desandro.com
30845  *
30846  * The idea is to render all the bricks based on vertical width...
30847  *
30848  * The original code extends 'outlayer' - we might need to use that....
30849  * 
30850  */
30851
30852
30853 /**
30854  * @class Roo.bootstrap.LayoutMasonry
30855  * @extends Roo.bootstrap.Component
30856  * Bootstrap Layout Masonry class
30857  * 
30858  * @constructor
30859  * Create a new Element
30860  * @param {Object} config The config object
30861  */
30862
30863 Roo.bootstrap.LayoutMasonry = function(config){
30864     
30865     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30866     
30867     this.bricks = [];
30868     
30869     Roo.bootstrap.LayoutMasonry.register(this);
30870     
30871     this.addEvents({
30872         // raw events
30873         /**
30874          * @event layout
30875          * Fire after layout the items
30876          * @param {Roo.bootstrap.LayoutMasonry} this
30877          * @param {Roo.EventObject} e
30878          */
30879         "layout" : true
30880     });
30881     
30882 };
30883
30884 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30885     
30886     /**
30887      * @cfg {Boolean} isLayoutInstant = no animation?
30888      */   
30889     isLayoutInstant : false, // needed?
30890    
30891     /**
30892      * @cfg {Number} boxWidth  width of the columns
30893      */   
30894     boxWidth : 450,
30895     
30896       /**
30897      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30898      */   
30899     boxHeight : 0,
30900     
30901     /**
30902      * @cfg {Number} padWidth padding below box..
30903      */   
30904     padWidth : 10, 
30905     
30906     /**
30907      * @cfg {Number} gutter gutter width..
30908      */   
30909     gutter : 10,
30910     
30911      /**
30912      * @cfg {Number} maxCols maximum number of columns
30913      */   
30914     
30915     maxCols: 0,
30916     
30917     /**
30918      * @cfg {Boolean} isAutoInitial defalut true
30919      */   
30920     isAutoInitial : true, 
30921     
30922     containerWidth: 0,
30923     
30924     /**
30925      * @cfg {Boolean} isHorizontal defalut false
30926      */   
30927     isHorizontal : false, 
30928
30929     currentSize : null,
30930     
30931     tag: 'div',
30932     
30933     cls: '',
30934     
30935     bricks: null, //CompositeElement
30936     
30937     cols : 1,
30938     
30939     _isLayoutInited : false,
30940     
30941 //    isAlternative : false, // only use for vertical layout...
30942     
30943     /**
30944      * @cfg {Number} alternativePadWidth padding below box..
30945      */   
30946     alternativePadWidth : 50,
30947     
30948     selectedBrick : [],
30949     
30950     getAutoCreate : function(){
30951         
30952         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30953         
30954         var cfg = {
30955             tag: this.tag,
30956             cls: 'blog-masonary-wrapper ' + this.cls,
30957             cn : {
30958                 cls : 'mas-boxes masonary'
30959             }
30960         };
30961         
30962         return cfg;
30963     },
30964     
30965     getChildContainer: function( )
30966     {
30967         if (this.boxesEl) {
30968             return this.boxesEl;
30969         }
30970         
30971         this.boxesEl = this.el.select('.mas-boxes').first();
30972         
30973         return this.boxesEl;
30974     },
30975     
30976     
30977     initEvents : function()
30978     {
30979         var _this = this;
30980         
30981         if(this.isAutoInitial){
30982             Roo.log('hook children rendered');
30983             this.on('childrenrendered', function() {
30984                 Roo.log('children rendered');
30985                 _this.initial();
30986             } ,this);
30987         }
30988     },
30989     
30990     initial : function()
30991     {
30992         this.selectedBrick = [];
30993         
30994         this.currentSize = this.el.getBox(true);
30995         
30996         Roo.EventManager.onWindowResize(this.resize, this); 
30997
30998         if(!this.isAutoInitial){
30999             this.layout();
31000             return;
31001         }
31002         
31003         this.layout();
31004         
31005         return;
31006         //this.layout.defer(500,this);
31007         
31008     },
31009     
31010     resize : function()
31011     {
31012         var cs = this.el.getBox(true);
31013         
31014         if (
31015                 this.currentSize.width == cs.width && 
31016                 this.currentSize.x == cs.x && 
31017                 this.currentSize.height == cs.height && 
31018                 this.currentSize.y == cs.y 
31019         ) {
31020             Roo.log("no change in with or X or Y");
31021             return;
31022         }
31023         
31024         this.currentSize = cs;
31025         
31026         this.layout();
31027         
31028     },
31029     
31030     layout : function()
31031     {   
31032         this._resetLayout();
31033         
31034         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31035         
31036         this.layoutItems( isInstant );
31037       
31038         this._isLayoutInited = true;
31039         
31040         this.fireEvent('layout', this);
31041         
31042     },
31043     
31044     _resetLayout : function()
31045     {
31046         if(this.isHorizontal){
31047             this.horizontalMeasureColumns();
31048             return;
31049         }
31050         
31051         this.verticalMeasureColumns();
31052         
31053     },
31054     
31055     verticalMeasureColumns : function()
31056     {
31057         this.getContainerWidth();
31058         
31059 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31060 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31061 //            return;
31062 //        }
31063         
31064         var boxWidth = this.boxWidth + this.padWidth;
31065         
31066         if(this.containerWidth < this.boxWidth){
31067             boxWidth = this.containerWidth
31068         }
31069         
31070         var containerWidth = this.containerWidth;
31071         
31072         var cols = Math.floor(containerWidth / boxWidth);
31073         
31074         this.cols = Math.max( cols, 1 );
31075         
31076         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31077         
31078         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31079         
31080         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31081         
31082         this.colWidth = boxWidth + avail - this.padWidth;
31083         
31084         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31085         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31086     },
31087     
31088     horizontalMeasureColumns : function()
31089     {
31090         this.getContainerWidth();
31091         
31092         var boxWidth = this.boxWidth;
31093         
31094         if(this.containerWidth < boxWidth){
31095             boxWidth = this.containerWidth;
31096         }
31097         
31098         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31099         
31100         this.el.setHeight(boxWidth);
31101         
31102     },
31103     
31104     getContainerWidth : function()
31105     {
31106         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31107     },
31108     
31109     layoutItems : function( isInstant )
31110     {
31111         Roo.log(this.bricks);
31112         
31113         var items = Roo.apply([], this.bricks);
31114         
31115         if(this.isHorizontal){
31116             this._horizontalLayoutItems( items , isInstant );
31117             return;
31118         }
31119         
31120 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31121 //            this._verticalAlternativeLayoutItems( items , isInstant );
31122 //            return;
31123 //        }
31124         
31125         this._verticalLayoutItems( items , isInstant );
31126         
31127     },
31128     
31129     _verticalLayoutItems : function ( items , isInstant)
31130     {
31131         if ( !items || !items.length ) {
31132             return;
31133         }
31134         
31135         var standard = [
31136             ['xs', 'xs', 'xs', 'tall'],
31137             ['xs', 'xs', 'tall'],
31138             ['xs', 'xs', 'sm'],
31139             ['xs', 'xs', 'xs'],
31140             ['xs', 'tall'],
31141             ['xs', 'sm'],
31142             ['xs', 'xs'],
31143             ['xs'],
31144             
31145             ['sm', 'xs', 'xs'],
31146             ['sm', 'xs'],
31147             ['sm'],
31148             
31149             ['tall', 'xs', 'xs', 'xs'],
31150             ['tall', 'xs', 'xs'],
31151             ['tall', 'xs'],
31152             ['tall']
31153             
31154         ];
31155         
31156         var queue = [];
31157         
31158         var boxes = [];
31159         
31160         var box = [];
31161         
31162         Roo.each(items, function(item, k){
31163             
31164             switch (item.size) {
31165                 // these layouts take up a full box,
31166                 case 'md' :
31167                 case 'md-left' :
31168                 case 'md-right' :
31169                 case 'wide' :
31170                     
31171                     if(box.length){
31172                         boxes.push(box);
31173                         box = [];
31174                     }
31175                     
31176                     boxes.push([item]);
31177                     
31178                     break;
31179                     
31180                 case 'xs' :
31181                 case 'sm' :
31182                 case 'tall' :
31183                     
31184                     box.push(item);
31185                     
31186                     break;
31187                 default :
31188                     break;
31189                     
31190             }
31191             
31192         }, this);
31193         
31194         if(box.length){
31195             boxes.push(box);
31196             box = [];
31197         }
31198         
31199         var filterPattern = function(box, length)
31200         {
31201             if(!box.length){
31202                 return;
31203             }
31204             
31205             var match = false;
31206             
31207             var pattern = box.slice(0, length);
31208             
31209             var format = [];
31210             
31211             Roo.each(pattern, function(i){
31212                 format.push(i.size);
31213             }, this);
31214             
31215             Roo.each(standard, function(s){
31216                 
31217                 if(String(s) != String(format)){
31218                     return;
31219                 }
31220                 
31221                 match = true;
31222                 return false;
31223                 
31224             }, this);
31225             
31226             if(!match && length == 1){
31227                 return;
31228             }
31229             
31230             if(!match){
31231                 filterPattern(box, length - 1);
31232                 return;
31233             }
31234                 
31235             queue.push(pattern);
31236
31237             box = box.slice(length, box.length);
31238
31239             filterPattern(box, 4);
31240
31241             return;
31242             
31243         }
31244         
31245         Roo.each(boxes, function(box, k){
31246             
31247             if(!box.length){
31248                 return;
31249             }
31250             
31251             if(box.length == 1){
31252                 queue.push(box);
31253                 return;
31254             }
31255             
31256             filterPattern(box, 4);
31257             
31258         }, this);
31259         
31260         this._processVerticalLayoutQueue( queue, isInstant );
31261         
31262     },
31263     
31264 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31265 //    {
31266 //        if ( !items || !items.length ) {
31267 //            return;
31268 //        }
31269 //
31270 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31271 //        
31272 //    },
31273     
31274     _horizontalLayoutItems : function ( items , isInstant)
31275     {
31276         if ( !items || !items.length || items.length < 3) {
31277             return;
31278         }
31279         
31280         items.reverse();
31281         
31282         var eItems = items.slice(0, 3);
31283         
31284         items = items.slice(3, items.length);
31285         
31286         var standard = [
31287             ['xs', 'xs', 'xs', 'wide'],
31288             ['xs', 'xs', 'wide'],
31289             ['xs', 'xs', 'sm'],
31290             ['xs', 'xs', 'xs'],
31291             ['xs', 'wide'],
31292             ['xs', 'sm'],
31293             ['xs', 'xs'],
31294             ['xs'],
31295             
31296             ['sm', 'xs', 'xs'],
31297             ['sm', 'xs'],
31298             ['sm'],
31299             
31300             ['wide', 'xs', 'xs', 'xs'],
31301             ['wide', 'xs', 'xs'],
31302             ['wide', 'xs'],
31303             ['wide'],
31304             
31305             ['wide-thin']
31306         ];
31307         
31308         var queue = [];
31309         
31310         var boxes = [];
31311         
31312         var box = [];
31313         
31314         Roo.each(items, function(item, k){
31315             
31316             switch (item.size) {
31317                 case 'md' :
31318                 case 'md-left' :
31319                 case 'md-right' :
31320                 case 'tall' :
31321                     
31322                     if(box.length){
31323                         boxes.push(box);
31324                         box = [];
31325                     }
31326                     
31327                     boxes.push([item]);
31328                     
31329                     break;
31330                     
31331                 case 'xs' :
31332                 case 'sm' :
31333                 case 'wide' :
31334                 case 'wide-thin' :
31335                     
31336                     box.push(item);
31337                     
31338                     break;
31339                 default :
31340                     break;
31341                     
31342             }
31343             
31344         }, this);
31345         
31346         if(box.length){
31347             boxes.push(box);
31348             box = [];
31349         }
31350         
31351         var filterPattern = function(box, length)
31352         {
31353             if(!box.length){
31354                 return;
31355             }
31356             
31357             var match = false;
31358             
31359             var pattern = box.slice(0, length);
31360             
31361             var format = [];
31362             
31363             Roo.each(pattern, function(i){
31364                 format.push(i.size);
31365             }, this);
31366             
31367             Roo.each(standard, function(s){
31368                 
31369                 if(String(s) != String(format)){
31370                     return;
31371                 }
31372                 
31373                 match = true;
31374                 return false;
31375                 
31376             }, this);
31377             
31378             if(!match && length == 1){
31379                 return;
31380             }
31381             
31382             if(!match){
31383                 filterPattern(box, length - 1);
31384                 return;
31385             }
31386                 
31387             queue.push(pattern);
31388
31389             box = box.slice(length, box.length);
31390
31391             filterPattern(box, 4);
31392
31393             return;
31394             
31395         }
31396         
31397         Roo.each(boxes, function(box, k){
31398             
31399             if(!box.length){
31400                 return;
31401             }
31402             
31403             if(box.length == 1){
31404                 queue.push(box);
31405                 return;
31406             }
31407             
31408             filterPattern(box, 4);
31409             
31410         }, this);
31411         
31412         
31413         var prune = [];
31414         
31415         var pos = this.el.getBox(true);
31416         
31417         var minX = pos.x;
31418         
31419         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31420         
31421         var hit_end = false;
31422         
31423         Roo.each(queue, function(box){
31424             
31425             if(hit_end){
31426                 
31427                 Roo.each(box, function(b){
31428                 
31429                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31430                     b.el.hide();
31431
31432                 }, this);
31433
31434                 return;
31435             }
31436             
31437             var mx = 0;
31438             
31439             Roo.each(box, function(b){
31440                 
31441                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31442                 b.el.show();
31443
31444                 mx = Math.max(mx, b.x);
31445                 
31446             }, this);
31447             
31448             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31449             
31450             if(maxX < minX){
31451                 
31452                 Roo.each(box, function(b){
31453                 
31454                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31455                     b.el.hide();
31456                     
31457                 }, this);
31458                 
31459                 hit_end = true;
31460                 
31461                 return;
31462             }
31463             
31464             prune.push(box);
31465             
31466         }, this);
31467         
31468         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31469     },
31470     
31471     /** Sets position of item in DOM
31472     * @param {Element} item
31473     * @param {Number} x - horizontal position
31474     * @param {Number} y - vertical position
31475     * @param {Boolean} isInstant - disables transitions
31476     */
31477     _processVerticalLayoutQueue : function( queue, isInstant )
31478     {
31479         var pos = this.el.getBox(true);
31480         var x = pos.x;
31481         var y = pos.y;
31482         var maxY = [];
31483         
31484         for (var i = 0; i < this.cols; i++){
31485             maxY[i] = pos.y;
31486         }
31487         
31488         Roo.each(queue, function(box, k){
31489             
31490             var col = k % this.cols;
31491             
31492             Roo.each(box, function(b,kk){
31493                 
31494                 b.el.position('absolute');
31495                 
31496                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31497                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31498                 
31499                 if(b.size == 'md-left' || b.size == 'md-right'){
31500                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31501                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31502                 }
31503                 
31504                 b.el.setWidth(width);
31505                 b.el.setHeight(height);
31506                 // iframe?
31507                 b.el.select('iframe',true).setSize(width,height);
31508                 
31509             }, this);
31510             
31511             for (var i = 0; i < this.cols; i++){
31512                 
31513                 if(maxY[i] < maxY[col]){
31514                     col = i;
31515                     continue;
31516                 }
31517                 
31518                 col = Math.min(col, i);
31519                 
31520             }
31521             
31522             x = pos.x + col * (this.colWidth + this.padWidth);
31523             
31524             y = maxY[col];
31525             
31526             var positions = [];
31527             
31528             switch (box.length){
31529                 case 1 :
31530                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31531                     break;
31532                 case 2 :
31533                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31534                     break;
31535                 case 3 :
31536                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31537                     break;
31538                 case 4 :
31539                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31540                     break;
31541                 default :
31542                     break;
31543             }
31544             
31545             Roo.each(box, function(b,kk){
31546                 
31547                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31548                 
31549                 var sz = b.el.getSize();
31550                 
31551                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31552                 
31553             }, this);
31554             
31555         }, this);
31556         
31557         var mY = 0;
31558         
31559         for (var i = 0; i < this.cols; i++){
31560             mY = Math.max(mY, maxY[i]);
31561         }
31562         
31563         this.el.setHeight(mY - pos.y);
31564         
31565     },
31566     
31567 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31568 //    {
31569 //        var pos = this.el.getBox(true);
31570 //        var x = pos.x;
31571 //        var y = pos.y;
31572 //        var maxX = pos.right;
31573 //        
31574 //        var maxHeight = 0;
31575 //        
31576 //        Roo.each(items, function(item, k){
31577 //            
31578 //            var c = k % 2;
31579 //            
31580 //            item.el.position('absolute');
31581 //                
31582 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31583 //
31584 //            item.el.setWidth(width);
31585 //
31586 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31587 //
31588 //            item.el.setHeight(height);
31589 //            
31590 //            if(c == 0){
31591 //                item.el.setXY([x, y], isInstant ? false : true);
31592 //            } else {
31593 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31594 //            }
31595 //            
31596 //            y = y + height + this.alternativePadWidth;
31597 //            
31598 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31599 //            
31600 //        }, this);
31601 //        
31602 //        this.el.setHeight(maxHeight);
31603 //        
31604 //    },
31605     
31606     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31607     {
31608         var pos = this.el.getBox(true);
31609         
31610         var minX = pos.x;
31611         var minY = pos.y;
31612         
31613         var maxX = pos.right;
31614         
31615         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31616         
31617         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31618         
31619         Roo.each(queue, function(box, k){
31620             
31621             Roo.each(box, function(b, kk){
31622                 
31623                 b.el.position('absolute');
31624                 
31625                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31626                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31627                 
31628                 if(b.size == 'md-left' || b.size == 'md-right'){
31629                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31630                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31631                 }
31632                 
31633                 b.el.setWidth(width);
31634                 b.el.setHeight(height);
31635                 
31636             }, this);
31637             
31638             if(!box.length){
31639                 return;
31640             }
31641             
31642             var positions = [];
31643             
31644             switch (box.length){
31645                 case 1 :
31646                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31647                     break;
31648                 case 2 :
31649                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31650                     break;
31651                 case 3 :
31652                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31653                     break;
31654                 case 4 :
31655                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31656                     break;
31657                 default :
31658                     break;
31659             }
31660             
31661             Roo.each(box, function(b,kk){
31662                 
31663                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31664                 
31665                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31666                 
31667             }, this);
31668             
31669         }, this);
31670         
31671     },
31672     
31673     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31674     {
31675         Roo.each(eItems, function(b,k){
31676             
31677             b.size = (k == 0) ? 'sm' : 'xs';
31678             b.x = (k == 0) ? 2 : 1;
31679             b.y = (k == 0) ? 2 : 1;
31680             
31681             b.el.position('absolute');
31682             
31683             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31684                 
31685             b.el.setWidth(width);
31686             
31687             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31688             
31689             b.el.setHeight(height);
31690             
31691         }, this);
31692
31693         var positions = [];
31694         
31695         positions.push({
31696             x : maxX - this.unitWidth * 2 - this.gutter,
31697             y : minY
31698         });
31699         
31700         positions.push({
31701             x : maxX - this.unitWidth,
31702             y : minY + (this.unitWidth + this.gutter) * 2
31703         });
31704         
31705         positions.push({
31706             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31707             y : minY
31708         });
31709         
31710         Roo.each(eItems, function(b,k){
31711             
31712             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31713
31714         }, this);
31715         
31716     },
31717     
31718     getVerticalOneBoxColPositions : function(x, y, box)
31719     {
31720         var pos = [];
31721         
31722         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31723         
31724         if(box[0].size == 'md-left'){
31725             rand = 0;
31726         }
31727         
31728         if(box[0].size == 'md-right'){
31729             rand = 1;
31730         }
31731         
31732         pos.push({
31733             x : x + (this.unitWidth + this.gutter) * rand,
31734             y : y
31735         });
31736         
31737         return pos;
31738     },
31739     
31740     getVerticalTwoBoxColPositions : function(x, y, box)
31741     {
31742         var pos = [];
31743         
31744         if(box[0].size == 'xs'){
31745             
31746             pos.push({
31747                 x : x,
31748                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31749             });
31750
31751             pos.push({
31752                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31753                 y : y
31754             });
31755             
31756             return pos;
31757             
31758         }
31759         
31760         pos.push({
31761             x : x,
31762             y : y
31763         });
31764
31765         pos.push({
31766             x : x + (this.unitWidth + this.gutter) * 2,
31767             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31768         });
31769         
31770         return pos;
31771         
31772     },
31773     
31774     getVerticalThreeBoxColPositions : function(x, y, box)
31775     {
31776         var pos = [];
31777         
31778         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31779             
31780             pos.push({
31781                 x : x,
31782                 y : y
31783             });
31784
31785             pos.push({
31786                 x : x + (this.unitWidth + this.gutter) * 1,
31787                 y : y
31788             });
31789             
31790             pos.push({
31791                 x : x + (this.unitWidth + this.gutter) * 2,
31792                 y : y
31793             });
31794             
31795             return pos;
31796             
31797         }
31798         
31799         if(box[0].size == 'xs' && box[1].size == 'xs'){
31800             
31801             pos.push({
31802                 x : x,
31803                 y : y
31804             });
31805
31806             pos.push({
31807                 x : x,
31808                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31809             });
31810             
31811             pos.push({
31812                 x : x + (this.unitWidth + this.gutter) * 1,
31813                 y : y
31814             });
31815             
31816             return pos;
31817             
31818         }
31819         
31820         pos.push({
31821             x : x,
31822             y : y
31823         });
31824
31825         pos.push({
31826             x : x + (this.unitWidth + this.gutter) * 2,
31827             y : y
31828         });
31829
31830         pos.push({
31831             x : x + (this.unitWidth + this.gutter) * 2,
31832             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31833         });
31834             
31835         return pos;
31836         
31837     },
31838     
31839     getVerticalFourBoxColPositions : function(x, y, box)
31840     {
31841         var pos = [];
31842         
31843         if(box[0].size == 'xs'){
31844             
31845             pos.push({
31846                 x : x,
31847                 y : y
31848             });
31849
31850             pos.push({
31851                 x : x,
31852                 y : y + (this.unitHeight + this.gutter) * 1
31853             });
31854             
31855             pos.push({
31856                 x : x,
31857                 y : y + (this.unitHeight + this.gutter) * 2
31858             });
31859             
31860             pos.push({
31861                 x : x + (this.unitWidth + this.gutter) * 1,
31862                 y : y
31863             });
31864             
31865             return pos;
31866             
31867         }
31868         
31869         pos.push({
31870             x : x,
31871             y : y
31872         });
31873
31874         pos.push({
31875             x : x + (this.unitWidth + this.gutter) * 2,
31876             y : y
31877         });
31878
31879         pos.push({
31880             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31881             y : y + (this.unitHeight + this.gutter) * 1
31882         });
31883
31884         pos.push({
31885             x : x + (this.unitWidth + this.gutter) * 2,
31886             y : y + (this.unitWidth + this.gutter) * 2
31887         });
31888
31889         return pos;
31890         
31891     },
31892     
31893     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31894     {
31895         var pos = [];
31896         
31897         if(box[0].size == 'md-left'){
31898             pos.push({
31899                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31900                 y : minY
31901             });
31902             
31903             return pos;
31904         }
31905         
31906         if(box[0].size == 'md-right'){
31907             pos.push({
31908                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31909                 y : minY + (this.unitWidth + this.gutter) * 1
31910             });
31911             
31912             return pos;
31913         }
31914         
31915         var rand = Math.floor(Math.random() * (4 - box[0].y));
31916         
31917         pos.push({
31918             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31919             y : minY + (this.unitWidth + this.gutter) * rand
31920         });
31921         
31922         return pos;
31923         
31924     },
31925     
31926     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31927     {
31928         var pos = [];
31929         
31930         if(box[0].size == 'xs'){
31931             
31932             pos.push({
31933                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31934                 y : minY
31935             });
31936
31937             pos.push({
31938                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31939                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31940             });
31941             
31942             return pos;
31943             
31944         }
31945         
31946         pos.push({
31947             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31948             y : minY
31949         });
31950
31951         pos.push({
31952             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31953             y : minY + (this.unitWidth + this.gutter) * 2
31954         });
31955         
31956         return pos;
31957         
31958     },
31959     
31960     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31961     {
31962         var pos = [];
31963         
31964         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31965             
31966             pos.push({
31967                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31968                 y : minY
31969             });
31970
31971             pos.push({
31972                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31973                 y : minY + (this.unitWidth + this.gutter) * 1
31974             });
31975             
31976             pos.push({
31977                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31978                 y : minY + (this.unitWidth + this.gutter) * 2
31979             });
31980             
31981             return pos;
31982             
31983         }
31984         
31985         if(box[0].size == 'xs' && box[1].size == 'xs'){
31986             
31987             pos.push({
31988                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31989                 y : minY
31990             });
31991
31992             pos.push({
31993                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31994                 y : minY
31995             });
31996             
31997             pos.push({
31998                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31999                 y : minY + (this.unitWidth + this.gutter) * 1
32000             });
32001             
32002             return pos;
32003             
32004         }
32005         
32006         pos.push({
32007             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32008             y : minY
32009         });
32010
32011         pos.push({
32012             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32013             y : minY + (this.unitWidth + this.gutter) * 2
32014         });
32015
32016         pos.push({
32017             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32018             y : minY + (this.unitWidth + this.gutter) * 2
32019         });
32020             
32021         return pos;
32022         
32023     },
32024     
32025     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32026     {
32027         var pos = [];
32028         
32029         if(box[0].size == 'xs'){
32030             
32031             pos.push({
32032                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32033                 y : minY
32034             });
32035
32036             pos.push({
32037                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32038                 y : minY
32039             });
32040             
32041             pos.push({
32042                 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),
32043                 y : minY
32044             });
32045             
32046             pos.push({
32047                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32048                 y : minY + (this.unitWidth + this.gutter) * 1
32049             });
32050             
32051             return pos;
32052             
32053         }
32054         
32055         pos.push({
32056             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32057             y : minY
32058         });
32059         
32060         pos.push({
32061             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32062             y : minY + (this.unitWidth + this.gutter) * 2
32063         });
32064         
32065         pos.push({
32066             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32067             y : minY + (this.unitWidth + this.gutter) * 2
32068         });
32069         
32070         pos.push({
32071             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),
32072             y : minY + (this.unitWidth + this.gutter) * 2
32073         });
32074
32075         return pos;
32076         
32077     },
32078     
32079     /**
32080     * remove a Masonry Brick
32081     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32082     */
32083     removeBrick : function(brick_id)
32084     {
32085         if (!brick_id) {
32086             return;
32087         }
32088         
32089         for (var i = 0; i<this.bricks.length; i++) {
32090             if (this.bricks[i].id == brick_id) {
32091                 this.bricks.splice(i,1);
32092                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32093                 this.initial();
32094             }
32095         }
32096     },
32097     
32098     /**
32099     * adds a Masonry Brick
32100     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32101     */
32102     addBrick : function(cfg)
32103     {
32104         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32105         //this.register(cn);
32106         cn.parentId = this.id;
32107         cn.render(this.el);
32108         return cn;
32109     },
32110     
32111     /**
32112     * register a Masonry Brick
32113     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32114     */
32115     
32116     register : function(brick)
32117     {
32118         this.bricks.push(brick);
32119         brick.masonryId = this.id;
32120     },
32121     
32122     /**
32123     * clear all the Masonry Brick
32124     */
32125     clearAll : function()
32126     {
32127         this.bricks = [];
32128         //this.getChildContainer().dom.innerHTML = "";
32129         this.el.dom.innerHTML = '';
32130     },
32131     
32132     getSelected : function()
32133     {
32134         if (!this.selectedBrick) {
32135             return false;
32136         }
32137         
32138         return this.selectedBrick;
32139     }
32140 });
32141
32142 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32143     
32144     groups: {},
32145      /**
32146     * register a Masonry Layout
32147     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32148     */
32149     
32150     register : function(layout)
32151     {
32152         this.groups[layout.id] = layout;
32153     },
32154     /**
32155     * fetch a  Masonry Layout based on the masonry layout ID
32156     * @param {string} the masonry layout to add
32157     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32158     */
32159     
32160     get: function(layout_id) {
32161         if (typeof(this.groups[layout_id]) == 'undefined') {
32162             return false;
32163         }
32164         return this.groups[layout_id] ;
32165     }
32166     
32167     
32168     
32169 });
32170
32171  
32172
32173  /**
32174  *
32175  * This is based on 
32176  * http://masonry.desandro.com
32177  *
32178  * The idea is to render all the bricks based on vertical width...
32179  *
32180  * The original code extends 'outlayer' - we might need to use that....
32181  * 
32182  */
32183
32184
32185 /**
32186  * @class Roo.bootstrap.LayoutMasonryAuto
32187  * @extends Roo.bootstrap.Component
32188  * Bootstrap Layout Masonry class
32189  * 
32190  * @constructor
32191  * Create a new Element
32192  * @param {Object} config The config object
32193  */
32194
32195 Roo.bootstrap.LayoutMasonryAuto = function(config){
32196     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32197 };
32198
32199 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32200     
32201       /**
32202      * @cfg {Boolean} isFitWidth  - resize the width..
32203      */   
32204     isFitWidth : false,  // options..
32205     /**
32206      * @cfg {Boolean} isOriginLeft = left align?
32207      */   
32208     isOriginLeft : true,
32209     /**
32210      * @cfg {Boolean} isOriginTop = top align?
32211      */   
32212     isOriginTop : false,
32213     /**
32214      * @cfg {Boolean} isLayoutInstant = no animation?
32215      */   
32216     isLayoutInstant : false, // needed?
32217     /**
32218      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32219      */   
32220     isResizingContainer : true,
32221     /**
32222      * @cfg {Number} columnWidth  width of the columns 
32223      */   
32224     
32225     columnWidth : 0,
32226     
32227     /**
32228      * @cfg {Number} maxCols maximum number of columns
32229      */   
32230     
32231     maxCols: 0,
32232     /**
32233      * @cfg {Number} padHeight padding below box..
32234      */   
32235     
32236     padHeight : 10, 
32237     
32238     /**
32239      * @cfg {Boolean} isAutoInitial defalut true
32240      */   
32241     
32242     isAutoInitial : true, 
32243     
32244     // private?
32245     gutter : 0,
32246     
32247     containerWidth: 0,
32248     initialColumnWidth : 0,
32249     currentSize : null,
32250     
32251     colYs : null, // array.
32252     maxY : 0,
32253     padWidth: 10,
32254     
32255     
32256     tag: 'div',
32257     cls: '',
32258     bricks: null, //CompositeElement
32259     cols : 0, // array?
32260     // element : null, // wrapped now this.el
32261     _isLayoutInited : null, 
32262     
32263     
32264     getAutoCreate : function(){
32265         
32266         var cfg = {
32267             tag: this.tag,
32268             cls: 'blog-masonary-wrapper ' + this.cls,
32269             cn : {
32270                 cls : 'mas-boxes masonary'
32271             }
32272         };
32273         
32274         return cfg;
32275     },
32276     
32277     getChildContainer: function( )
32278     {
32279         if (this.boxesEl) {
32280             return this.boxesEl;
32281         }
32282         
32283         this.boxesEl = this.el.select('.mas-boxes').first();
32284         
32285         return this.boxesEl;
32286     },
32287     
32288     
32289     initEvents : function()
32290     {
32291         var _this = this;
32292         
32293         if(this.isAutoInitial){
32294             Roo.log('hook children rendered');
32295             this.on('childrenrendered', function() {
32296                 Roo.log('children rendered');
32297                 _this.initial();
32298             } ,this);
32299         }
32300         
32301     },
32302     
32303     initial : function()
32304     {
32305         this.reloadItems();
32306
32307         this.currentSize = this.el.getBox(true);
32308
32309         /// was window resize... - let's see if this works..
32310         Roo.EventManager.onWindowResize(this.resize, this); 
32311
32312         if(!this.isAutoInitial){
32313             this.layout();
32314             return;
32315         }
32316         
32317         this.layout.defer(500,this);
32318     },
32319     
32320     reloadItems: function()
32321     {
32322         this.bricks = this.el.select('.masonry-brick', true);
32323         
32324         this.bricks.each(function(b) {
32325             //Roo.log(b.getSize());
32326             if (!b.attr('originalwidth')) {
32327                 b.attr('originalwidth',  b.getSize().width);
32328             }
32329             
32330         });
32331         
32332         Roo.log(this.bricks.elements.length);
32333     },
32334     
32335     resize : function()
32336     {
32337         Roo.log('resize');
32338         var cs = this.el.getBox(true);
32339         
32340         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32341             Roo.log("no change in with or X");
32342             return;
32343         }
32344         this.currentSize = cs;
32345         this.layout();
32346     },
32347     
32348     layout : function()
32349     {
32350          Roo.log('layout');
32351         this._resetLayout();
32352         //this._manageStamps();
32353       
32354         // don't animate first layout
32355         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32356         this.layoutItems( isInstant );
32357       
32358         // flag for initalized
32359         this._isLayoutInited = true;
32360     },
32361     
32362     layoutItems : function( isInstant )
32363     {
32364         //var items = this._getItemsForLayout( this.items );
32365         // original code supports filtering layout items.. we just ignore it..
32366         
32367         this._layoutItems( this.bricks , isInstant );
32368       
32369         this._postLayout();
32370     },
32371     _layoutItems : function ( items , isInstant)
32372     {
32373        //this.fireEvent( 'layout', this, items );
32374     
32375
32376         if ( !items || !items.elements.length ) {
32377           // no items, emit event with empty array
32378             return;
32379         }
32380
32381         var queue = [];
32382         items.each(function(item) {
32383             Roo.log("layout item");
32384             Roo.log(item);
32385             // get x/y object from method
32386             var position = this._getItemLayoutPosition( item );
32387             // enqueue
32388             position.item = item;
32389             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32390             queue.push( position );
32391         }, this);
32392       
32393         this._processLayoutQueue( queue );
32394     },
32395     /** Sets position of item in DOM
32396     * @param {Element} item
32397     * @param {Number} x - horizontal position
32398     * @param {Number} y - vertical position
32399     * @param {Boolean} isInstant - disables transitions
32400     */
32401     _processLayoutQueue : function( queue )
32402     {
32403         for ( var i=0, len = queue.length; i < len; i++ ) {
32404             var obj = queue[i];
32405             obj.item.position('absolute');
32406             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32407         }
32408     },
32409       
32410     
32411     /**
32412     * Any logic you want to do after each layout,
32413     * i.e. size the container
32414     */
32415     _postLayout : function()
32416     {
32417         this.resizeContainer();
32418     },
32419     
32420     resizeContainer : function()
32421     {
32422         if ( !this.isResizingContainer ) {
32423             return;
32424         }
32425         var size = this._getContainerSize();
32426         if ( size ) {
32427             this.el.setSize(size.width,size.height);
32428             this.boxesEl.setSize(size.width,size.height);
32429         }
32430     },
32431     
32432     
32433     
32434     _resetLayout : function()
32435     {
32436         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32437         this.colWidth = this.el.getWidth();
32438         //this.gutter = this.el.getWidth(); 
32439         
32440         this.measureColumns();
32441
32442         // reset column Y
32443         var i = this.cols;
32444         this.colYs = [];
32445         while (i--) {
32446             this.colYs.push( 0 );
32447         }
32448     
32449         this.maxY = 0;
32450     },
32451
32452     measureColumns : function()
32453     {
32454         this.getContainerWidth();
32455       // if columnWidth is 0, default to outerWidth of first item
32456         if ( !this.columnWidth ) {
32457             var firstItem = this.bricks.first();
32458             Roo.log(firstItem);
32459             this.columnWidth  = this.containerWidth;
32460             if (firstItem && firstItem.attr('originalwidth') ) {
32461                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32462             }
32463             // columnWidth fall back to item of first element
32464             Roo.log("set column width?");
32465                         this.initialColumnWidth = this.columnWidth  ;
32466
32467             // if first elem has no width, default to size of container
32468             
32469         }
32470         
32471         
32472         if (this.initialColumnWidth) {
32473             this.columnWidth = this.initialColumnWidth;
32474         }
32475         
32476         
32477             
32478         // column width is fixed at the top - however if container width get's smaller we should
32479         // reduce it...
32480         
32481         // this bit calcs how man columns..
32482             
32483         var columnWidth = this.columnWidth += this.gutter;
32484       
32485         // calculate columns
32486         var containerWidth = this.containerWidth + this.gutter;
32487         
32488         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32489         // fix rounding errors, typically with gutters
32490         var excess = columnWidth - containerWidth % columnWidth;
32491         
32492         
32493         // if overshoot is less than a pixel, round up, otherwise floor it
32494         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32495         cols = Math[ mathMethod ]( cols );
32496         this.cols = Math.max( cols, 1 );
32497         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32498         
32499          // padding positioning..
32500         var totalColWidth = this.cols * this.columnWidth;
32501         var padavail = this.containerWidth - totalColWidth;
32502         // so for 2 columns - we need 3 'pads'
32503         
32504         var padNeeded = (1+this.cols) * this.padWidth;
32505         
32506         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32507         
32508         this.columnWidth += padExtra
32509         //this.padWidth = Math.floor(padavail /  ( this.cols));
32510         
32511         // adjust colum width so that padding is fixed??
32512         
32513         // we have 3 columns ... total = width * 3
32514         // we have X left over... that should be used by 
32515         
32516         //if (this.expandC) {
32517             
32518         //}
32519         
32520         
32521         
32522     },
32523     
32524     getContainerWidth : function()
32525     {
32526        /* // container is parent if fit width
32527         var container = this.isFitWidth ? this.element.parentNode : this.element;
32528         // check that this.size and size are there
32529         // IE8 triggers resize on body size change, so they might not be
32530         
32531         var size = getSize( container );  //FIXME
32532         this.containerWidth = size && size.innerWidth; //FIXME
32533         */
32534          
32535         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32536         
32537     },
32538     
32539     _getItemLayoutPosition : function( item )  // what is item?
32540     {
32541         // we resize the item to our columnWidth..
32542       
32543         item.setWidth(this.columnWidth);
32544         item.autoBoxAdjust  = false;
32545         
32546         var sz = item.getSize();
32547  
32548         // how many columns does this brick span
32549         var remainder = this.containerWidth % this.columnWidth;
32550         
32551         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32552         // round if off by 1 pixel, otherwise use ceil
32553         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32554         colSpan = Math.min( colSpan, this.cols );
32555         
32556         // normally this should be '1' as we dont' currently allow multi width columns..
32557         
32558         var colGroup = this._getColGroup( colSpan );
32559         // get the minimum Y value from the columns
32560         var minimumY = Math.min.apply( Math, colGroup );
32561         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32562         
32563         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32564          
32565         // position the brick
32566         var position = {
32567             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32568             y: this.currentSize.y + minimumY + this.padHeight
32569         };
32570         
32571         Roo.log(position);
32572         // apply setHeight to necessary columns
32573         var setHeight = minimumY + sz.height + this.padHeight;
32574         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32575         
32576         var setSpan = this.cols + 1 - colGroup.length;
32577         for ( var i = 0; i < setSpan; i++ ) {
32578           this.colYs[ shortColIndex + i ] = setHeight ;
32579         }
32580       
32581         return position;
32582     },
32583     
32584     /**
32585      * @param {Number} colSpan - number of columns the element spans
32586      * @returns {Array} colGroup
32587      */
32588     _getColGroup : function( colSpan )
32589     {
32590         if ( colSpan < 2 ) {
32591           // if brick spans only one column, use all the column Ys
32592           return this.colYs;
32593         }
32594       
32595         var colGroup = [];
32596         // how many different places could this brick fit horizontally
32597         var groupCount = this.cols + 1 - colSpan;
32598         // for each group potential horizontal position
32599         for ( var i = 0; i < groupCount; i++ ) {
32600           // make an array of colY values for that one group
32601           var groupColYs = this.colYs.slice( i, i + colSpan );
32602           // and get the max value of the array
32603           colGroup[i] = Math.max.apply( Math, groupColYs );
32604         }
32605         return colGroup;
32606     },
32607     /*
32608     _manageStamp : function( stamp )
32609     {
32610         var stampSize =  stamp.getSize();
32611         var offset = stamp.getBox();
32612         // get the columns that this stamp affects
32613         var firstX = this.isOriginLeft ? offset.x : offset.right;
32614         var lastX = firstX + stampSize.width;
32615         var firstCol = Math.floor( firstX / this.columnWidth );
32616         firstCol = Math.max( 0, firstCol );
32617         
32618         var lastCol = Math.floor( lastX / this.columnWidth );
32619         // lastCol should not go over if multiple of columnWidth #425
32620         lastCol -= lastX % this.columnWidth ? 0 : 1;
32621         lastCol = Math.min( this.cols - 1, lastCol );
32622         
32623         // set colYs to bottom of the stamp
32624         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32625             stampSize.height;
32626             
32627         for ( var i = firstCol; i <= lastCol; i++ ) {
32628           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32629         }
32630     },
32631     */
32632     
32633     _getContainerSize : function()
32634     {
32635         this.maxY = Math.max.apply( Math, this.colYs );
32636         var size = {
32637             height: this.maxY
32638         };
32639       
32640         if ( this.isFitWidth ) {
32641             size.width = this._getContainerFitWidth();
32642         }
32643       
32644         return size;
32645     },
32646     
32647     _getContainerFitWidth : function()
32648     {
32649         var unusedCols = 0;
32650         // count unused columns
32651         var i = this.cols;
32652         while ( --i ) {
32653           if ( this.colYs[i] !== 0 ) {
32654             break;
32655           }
32656           unusedCols++;
32657         }
32658         // fit container to columns that have been used
32659         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32660     },
32661     
32662     needsResizeLayout : function()
32663     {
32664         var previousWidth = this.containerWidth;
32665         this.getContainerWidth();
32666         return previousWidth !== this.containerWidth;
32667     }
32668  
32669 });
32670
32671  
32672
32673  /*
32674  * - LGPL
32675  *
32676  * element
32677  * 
32678  */
32679
32680 /**
32681  * @class Roo.bootstrap.MasonryBrick
32682  * @extends Roo.bootstrap.Component
32683  * Bootstrap MasonryBrick class
32684  * 
32685  * @constructor
32686  * Create a new MasonryBrick
32687  * @param {Object} config The config object
32688  */
32689
32690 Roo.bootstrap.MasonryBrick = function(config){
32691     
32692     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32693     
32694     Roo.bootstrap.MasonryBrick.register(this);
32695     
32696     this.addEvents({
32697         // raw events
32698         /**
32699          * @event click
32700          * When a MasonryBrick is clcik
32701          * @param {Roo.bootstrap.MasonryBrick} this
32702          * @param {Roo.EventObject} e
32703          */
32704         "click" : true
32705     });
32706 };
32707
32708 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32709     
32710     /**
32711      * @cfg {String} title
32712      */   
32713     title : '',
32714     /**
32715      * @cfg {String} html
32716      */   
32717     html : '',
32718     /**
32719      * @cfg {String} bgimage
32720      */   
32721     bgimage : '',
32722     /**
32723      * @cfg {String} videourl
32724      */   
32725     videourl : '',
32726     /**
32727      * @cfg {String} cls
32728      */   
32729     cls : '',
32730     /**
32731      * @cfg {String} href
32732      */   
32733     href : '',
32734     /**
32735      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32736      */   
32737     size : 'xs',
32738     
32739     /**
32740      * @cfg {String} placetitle (center|bottom)
32741      */   
32742     placetitle : '',
32743     
32744     /**
32745      * @cfg {Boolean} isFitContainer defalut true
32746      */   
32747     isFitContainer : true, 
32748     
32749     /**
32750      * @cfg {Boolean} preventDefault defalut false
32751      */   
32752     preventDefault : false, 
32753     
32754     /**
32755      * @cfg {Boolean} inverse defalut false
32756      */   
32757     maskInverse : false, 
32758     
32759     getAutoCreate : function()
32760     {
32761         if(!this.isFitContainer){
32762             return this.getSplitAutoCreate();
32763         }
32764         
32765         var cls = 'masonry-brick masonry-brick-full';
32766         
32767         if(this.href.length){
32768             cls += ' masonry-brick-link';
32769         }
32770         
32771         if(this.bgimage.length){
32772             cls += ' masonry-brick-image';
32773         }
32774         
32775         if(this.maskInverse){
32776             cls += ' mask-inverse';
32777         }
32778         
32779         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32780             cls += ' enable-mask';
32781         }
32782         
32783         if(this.size){
32784             cls += ' masonry-' + this.size + '-brick';
32785         }
32786         
32787         if(this.placetitle.length){
32788             
32789             switch (this.placetitle) {
32790                 case 'center' :
32791                     cls += ' masonry-center-title';
32792                     break;
32793                 case 'bottom' :
32794                     cls += ' masonry-bottom-title';
32795                     break;
32796                 default:
32797                     break;
32798             }
32799             
32800         } else {
32801             if(!this.html.length && !this.bgimage.length){
32802                 cls += ' masonry-center-title';
32803             }
32804
32805             if(!this.html.length && this.bgimage.length){
32806                 cls += ' masonry-bottom-title';
32807             }
32808         }
32809         
32810         if(this.cls){
32811             cls += ' ' + this.cls;
32812         }
32813         
32814         var cfg = {
32815             tag: (this.href.length) ? 'a' : 'div',
32816             cls: cls,
32817             cn: [
32818                 {
32819                     tag: 'div',
32820                     cls: 'masonry-brick-mask'
32821                 },
32822                 {
32823                     tag: 'div',
32824                     cls: 'masonry-brick-paragraph',
32825                     cn: []
32826                 }
32827             ]
32828         };
32829         
32830         if(this.href.length){
32831             cfg.href = this.href;
32832         }
32833         
32834         var cn = cfg.cn[1].cn;
32835         
32836         if(this.title.length){
32837             cn.push({
32838                 tag: 'h4',
32839                 cls: 'masonry-brick-title',
32840                 html: this.title
32841             });
32842         }
32843         
32844         if(this.html.length){
32845             cn.push({
32846                 tag: 'p',
32847                 cls: 'masonry-brick-text',
32848                 html: this.html
32849             });
32850         }
32851         
32852         if (!this.title.length && !this.html.length) {
32853             cfg.cn[1].cls += ' hide';
32854         }
32855         
32856         if(this.bgimage.length){
32857             cfg.cn.push({
32858                 tag: 'img',
32859                 cls: 'masonry-brick-image-view',
32860                 src: this.bgimage
32861             });
32862         }
32863         
32864         if(this.videourl.length){
32865             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32866             // youtube support only?
32867             cfg.cn.push({
32868                 tag: 'iframe',
32869                 cls: 'masonry-brick-image-view',
32870                 src: vurl,
32871                 frameborder : 0,
32872                 allowfullscreen : true
32873             });
32874         }
32875         
32876         return cfg;
32877         
32878     },
32879     
32880     getSplitAutoCreate : function()
32881     {
32882         var cls = 'masonry-brick masonry-brick-split';
32883         
32884         if(this.href.length){
32885             cls += ' masonry-brick-link';
32886         }
32887         
32888         if(this.bgimage.length){
32889             cls += ' masonry-brick-image';
32890         }
32891         
32892         if(this.size){
32893             cls += ' masonry-' + this.size + '-brick';
32894         }
32895         
32896         switch (this.placetitle) {
32897             case 'center' :
32898                 cls += ' masonry-center-title';
32899                 break;
32900             case 'bottom' :
32901                 cls += ' masonry-bottom-title';
32902                 break;
32903             default:
32904                 if(!this.bgimage.length){
32905                     cls += ' masonry-center-title';
32906                 }
32907
32908                 if(this.bgimage.length){
32909                     cls += ' masonry-bottom-title';
32910                 }
32911                 break;
32912         }
32913         
32914         if(this.cls){
32915             cls += ' ' + this.cls;
32916         }
32917         
32918         var cfg = {
32919             tag: (this.href.length) ? 'a' : 'div',
32920             cls: cls,
32921             cn: [
32922                 {
32923                     tag: 'div',
32924                     cls: 'masonry-brick-split-head',
32925                     cn: [
32926                         {
32927                             tag: 'div',
32928                             cls: 'masonry-brick-paragraph',
32929                             cn: []
32930                         }
32931                     ]
32932                 },
32933                 {
32934                     tag: 'div',
32935                     cls: 'masonry-brick-split-body',
32936                     cn: []
32937                 }
32938             ]
32939         };
32940         
32941         if(this.href.length){
32942             cfg.href = this.href;
32943         }
32944         
32945         if(this.title.length){
32946             cfg.cn[0].cn[0].cn.push({
32947                 tag: 'h4',
32948                 cls: 'masonry-brick-title',
32949                 html: this.title
32950             });
32951         }
32952         
32953         if(this.html.length){
32954             cfg.cn[1].cn.push({
32955                 tag: 'p',
32956                 cls: 'masonry-brick-text',
32957                 html: this.html
32958             });
32959         }
32960
32961         if(this.bgimage.length){
32962             cfg.cn[0].cn.push({
32963                 tag: 'img',
32964                 cls: 'masonry-brick-image-view',
32965                 src: this.bgimage
32966             });
32967         }
32968         
32969         if(this.videourl.length){
32970             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32971             // youtube support only?
32972             cfg.cn[0].cn.cn.push({
32973                 tag: 'iframe',
32974                 cls: 'masonry-brick-image-view',
32975                 src: vurl,
32976                 frameborder : 0,
32977                 allowfullscreen : true
32978             });
32979         }
32980         
32981         return cfg;
32982     },
32983     
32984     initEvents: function() 
32985     {
32986         switch (this.size) {
32987             case 'xs' :
32988                 this.x = 1;
32989                 this.y = 1;
32990                 break;
32991             case 'sm' :
32992                 this.x = 2;
32993                 this.y = 2;
32994                 break;
32995             case 'md' :
32996             case 'md-left' :
32997             case 'md-right' :
32998                 this.x = 3;
32999                 this.y = 3;
33000                 break;
33001             case 'tall' :
33002                 this.x = 2;
33003                 this.y = 3;
33004                 break;
33005             case 'wide' :
33006                 this.x = 3;
33007                 this.y = 2;
33008                 break;
33009             case 'wide-thin' :
33010                 this.x = 3;
33011                 this.y = 1;
33012                 break;
33013                         
33014             default :
33015                 break;
33016         }
33017         
33018         if(Roo.isTouch){
33019             this.el.on('touchstart', this.onTouchStart, this);
33020             this.el.on('touchmove', this.onTouchMove, this);
33021             this.el.on('touchend', this.onTouchEnd, this);
33022             this.el.on('contextmenu', this.onContextMenu, this);
33023         } else {
33024             this.el.on('mouseenter'  ,this.enter, this);
33025             this.el.on('mouseleave', this.leave, this);
33026             this.el.on('click', this.onClick, this);
33027         }
33028         
33029         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33030             this.parent().bricks.push(this);   
33031         }
33032         
33033     },
33034     
33035     onClick: function(e, el)
33036     {
33037         var time = this.endTimer - this.startTimer;
33038         // Roo.log(e.preventDefault());
33039         if(Roo.isTouch){
33040             if(time > 1000){
33041                 e.preventDefault();
33042                 return;
33043             }
33044         }
33045         
33046         if(!this.preventDefault){
33047             return;
33048         }
33049         
33050         e.preventDefault();
33051         
33052         if (this.activeClass != '') {
33053             this.selectBrick();
33054         }
33055         
33056         this.fireEvent('click', this, e);
33057     },
33058     
33059     enter: function(e, el)
33060     {
33061         e.preventDefault();
33062         
33063         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33064             return;
33065         }
33066         
33067         if(this.bgimage.length && this.html.length){
33068             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33069         }
33070     },
33071     
33072     leave: function(e, el)
33073     {
33074         e.preventDefault();
33075         
33076         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33077             return;
33078         }
33079         
33080         if(this.bgimage.length && this.html.length){
33081             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33082         }
33083     },
33084     
33085     onTouchStart: function(e, el)
33086     {
33087 //        e.preventDefault();
33088         
33089         this.touchmoved = false;
33090         
33091         if(!this.isFitContainer){
33092             return;
33093         }
33094         
33095         if(!this.bgimage.length || !this.html.length){
33096             return;
33097         }
33098         
33099         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33100         
33101         this.timer = new Date().getTime();
33102         
33103     },
33104     
33105     onTouchMove: function(e, el)
33106     {
33107         this.touchmoved = true;
33108     },
33109     
33110     onContextMenu : function(e,el)
33111     {
33112         e.preventDefault();
33113         e.stopPropagation();
33114         return false;
33115     },
33116     
33117     onTouchEnd: function(e, el)
33118     {
33119 //        e.preventDefault();
33120         
33121         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33122         
33123             this.leave(e,el);
33124             
33125             return;
33126         }
33127         
33128         if(!this.bgimage.length || !this.html.length){
33129             
33130             if(this.href.length){
33131                 window.location.href = this.href;
33132             }
33133             
33134             return;
33135         }
33136         
33137         if(!this.isFitContainer){
33138             return;
33139         }
33140         
33141         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33142         
33143         window.location.href = this.href;
33144     },
33145     
33146     //selection on single brick only
33147     selectBrick : function() {
33148         
33149         if (!this.parentId) {
33150             return;
33151         }
33152         
33153         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33154         var index = m.selectedBrick.indexOf(this.id);
33155         
33156         if ( index > -1) {
33157             m.selectedBrick.splice(index,1);
33158             this.el.removeClass(this.activeClass);
33159             return;
33160         }
33161         
33162         for(var i = 0; i < m.selectedBrick.length; i++) {
33163             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33164             b.el.removeClass(b.activeClass);
33165         }
33166         
33167         m.selectedBrick = [];
33168         
33169         m.selectedBrick.push(this.id);
33170         this.el.addClass(this.activeClass);
33171         return;
33172     },
33173     
33174     isSelected : function(){
33175         return this.el.hasClass(this.activeClass);
33176         
33177     }
33178 });
33179
33180 Roo.apply(Roo.bootstrap.MasonryBrick, {
33181     
33182     //groups: {},
33183     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33184      /**
33185     * register a Masonry Brick
33186     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33187     */
33188     
33189     register : function(brick)
33190     {
33191         //this.groups[brick.id] = brick;
33192         this.groups.add(brick.id, brick);
33193     },
33194     /**
33195     * fetch a  masonry brick based on the masonry brick ID
33196     * @param {string} the masonry brick to add
33197     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33198     */
33199     
33200     get: function(brick_id) 
33201     {
33202         // if (typeof(this.groups[brick_id]) == 'undefined') {
33203         //     return false;
33204         // }
33205         // return this.groups[brick_id] ;
33206         
33207         if(this.groups.key(brick_id)) {
33208             return this.groups.key(brick_id);
33209         }
33210         
33211         return false;
33212     }
33213     
33214     
33215     
33216 });
33217
33218  /*
33219  * - LGPL
33220  *
33221  * element
33222  * 
33223  */
33224
33225 /**
33226  * @class Roo.bootstrap.Brick
33227  * @extends Roo.bootstrap.Component
33228  * Bootstrap Brick class
33229  * 
33230  * @constructor
33231  * Create a new Brick
33232  * @param {Object} config The config object
33233  */
33234
33235 Roo.bootstrap.Brick = function(config){
33236     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33237     
33238     this.addEvents({
33239         // raw events
33240         /**
33241          * @event click
33242          * When a Brick is click
33243          * @param {Roo.bootstrap.Brick} this
33244          * @param {Roo.EventObject} e
33245          */
33246         "click" : true
33247     });
33248 };
33249
33250 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33251     
33252     /**
33253      * @cfg {String} title
33254      */   
33255     title : '',
33256     /**
33257      * @cfg {String} html
33258      */   
33259     html : '',
33260     /**
33261      * @cfg {String} bgimage
33262      */   
33263     bgimage : '',
33264     /**
33265      * @cfg {String} cls
33266      */   
33267     cls : '',
33268     /**
33269      * @cfg {String} href
33270      */   
33271     href : '',
33272     /**
33273      * @cfg {String} video
33274      */   
33275     video : '',
33276     /**
33277      * @cfg {Boolean} square
33278      */   
33279     square : true,
33280     
33281     getAutoCreate : function()
33282     {
33283         var cls = 'roo-brick';
33284         
33285         if(this.href.length){
33286             cls += ' roo-brick-link';
33287         }
33288         
33289         if(this.bgimage.length){
33290             cls += ' roo-brick-image';
33291         }
33292         
33293         if(!this.html.length && !this.bgimage.length){
33294             cls += ' roo-brick-center-title';
33295         }
33296         
33297         if(!this.html.length && this.bgimage.length){
33298             cls += ' roo-brick-bottom-title';
33299         }
33300         
33301         if(this.cls){
33302             cls += ' ' + this.cls;
33303         }
33304         
33305         var cfg = {
33306             tag: (this.href.length) ? 'a' : 'div',
33307             cls: cls,
33308             cn: [
33309                 {
33310                     tag: 'div',
33311                     cls: 'roo-brick-paragraph',
33312                     cn: []
33313                 }
33314             ]
33315         };
33316         
33317         if(this.href.length){
33318             cfg.href = this.href;
33319         }
33320         
33321         var cn = cfg.cn[0].cn;
33322         
33323         if(this.title.length){
33324             cn.push({
33325                 tag: 'h4',
33326                 cls: 'roo-brick-title',
33327                 html: this.title
33328             });
33329         }
33330         
33331         if(this.html.length){
33332             cn.push({
33333                 tag: 'p',
33334                 cls: 'roo-brick-text',
33335                 html: this.html
33336             });
33337         } else {
33338             cn.cls += ' hide';
33339         }
33340         
33341         if(this.bgimage.length){
33342             cfg.cn.push({
33343                 tag: 'img',
33344                 cls: 'roo-brick-image-view',
33345                 src: this.bgimage
33346             });
33347         }
33348         
33349         return cfg;
33350     },
33351     
33352     initEvents: function() 
33353     {
33354         if(this.title.length || this.html.length){
33355             this.el.on('mouseenter'  ,this.enter, this);
33356             this.el.on('mouseleave', this.leave, this);
33357         }
33358         
33359         Roo.EventManager.onWindowResize(this.resize, this); 
33360         
33361         if(this.bgimage.length){
33362             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33363             this.imageEl.on('load', this.onImageLoad, this);
33364             return;
33365         }
33366         
33367         this.resize();
33368     },
33369     
33370     onImageLoad : function()
33371     {
33372         this.resize();
33373     },
33374     
33375     resize : function()
33376     {
33377         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33378         
33379         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33380         
33381         if(this.bgimage.length){
33382             var image = this.el.select('.roo-brick-image-view', true).first();
33383             
33384             image.setWidth(paragraph.getWidth());
33385             
33386             if(this.square){
33387                 image.setHeight(paragraph.getWidth());
33388             }
33389             
33390             this.el.setHeight(image.getHeight());
33391             paragraph.setHeight(image.getHeight());
33392             
33393         }
33394         
33395     },
33396     
33397     enter: function(e, el)
33398     {
33399         e.preventDefault();
33400         
33401         if(this.bgimage.length){
33402             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33403             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33404         }
33405     },
33406     
33407     leave: function(e, el)
33408     {
33409         e.preventDefault();
33410         
33411         if(this.bgimage.length){
33412             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33413             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33414         }
33415     }
33416     
33417 });
33418
33419  
33420
33421  /*
33422  * - LGPL
33423  *
33424  * Number field 
33425  */
33426
33427 /**
33428  * @class Roo.bootstrap.NumberField
33429  * @extends Roo.bootstrap.Input
33430  * Bootstrap NumberField class
33431  * 
33432  * 
33433  * 
33434  * 
33435  * @constructor
33436  * Create a new NumberField
33437  * @param {Object} config The config object
33438  */
33439
33440 Roo.bootstrap.NumberField = function(config){
33441     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33442 };
33443
33444 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33445     
33446     /**
33447      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33448      */
33449     allowDecimals : true,
33450     /**
33451      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33452      */
33453     decimalSeparator : ".",
33454     /**
33455      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33456      */
33457     decimalPrecision : 2,
33458     /**
33459      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33460      */
33461     allowNegative : true,
33462     
33463     /**
33464      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33465      */
33466     allowZero: true,
33467     /**
33468      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33469      */
33470     minValue : Number.NEGATIVE_INFINITY,
33471     /**
33472      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33473      */
33474     maxValue : Number.MAX_VALUE,
33475     /**
33476      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33477      */
33478     minText : "The minimum value for this field is {0}",
33479     /**
33480      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33481      */
33482     maxText : "The maximum value for this field is {0}",
33483     /**
33484      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33485      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33486      */
33487     nanText : "{0} is not a valid number",
33488     /**
33489      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33490      */
33491     thousandsDelimiter : false,
33492     /**
33493      * @cfg {String} valueAlign alignment of value
33494      */
33495     valueAlign : "left",
33496
33497     getAutoCreate : function()
33498     {
33499         var hiddenInput = {
33500             tag: 'input',
33501             type: 'hidden',
33502             id: Roo.id(),
33503             cls: 'hidden-number-input'
33504         };
33505         
33506         if (this.name) {
33507             hiddenInput.name = this.name;
33508         }
33509         
33510         this.name = '';
33511         
33512         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33513         
33514         this.name = hiddenInput.name;
33515         
33516         if(cfg.cn.length > 0) {
33517             cfg.cn.push(hiddenInput);
33518         }
33519         
33520         return cfg;
33521     },
33522
33523     // private
33524     initEvents : function()
33525     {   
33526         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33527         
33528         var allowed = "0123456789";
33529         
33530         if(this.allowDecimals){
33531             allowed += this.decimalSeparator;
33532         }
33533         
33534         if(this.allowNegative){
33535             allowed += "-";
33536         }
33537         
33538         if(this.thousandsDelimiter) {
33539             allowed += ",";
33540         }
33541         
33542         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33543         
33544         var keyPress = function(e){
33545             
33546             var k = e.getKey();
33547             
33548             var c = e.getCharCode();
33549             
33550             if(
33551                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33552                     allowed.indexOf(String.fromCharCode(c)) === -1
33553             ){
33554                 e.stopEvent();
33555                 return;
33556             }
33557             
33558             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33559                 return;
33560             }
33561             
33562             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33563                 e.stopEvent();
33564             }
33565         };
33566         
33567         this.el.on("keypress", keyPress, this);
33568     },
33569     
33570     validateValue : function(value)
33571     {
33572         
33573         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33574             return false;
33575         }
33576         
33577         var num = this.parseValue(value);
33578         
33579         if(isNaN(num)){
33580             this.markInvalid(String.format(this.nanText, value));
33581             return false;
33582         }
33583         
33584         if(num < this.minValue){
33585             this.markInvalid(String.format(this.minText, this.minValue));
33586             return false;
33587         }
33588         
33589         if(num > this.maxValue){
33590             this.markInvalid(String.format(this.maxText, this.maxValue));
33591             return false;
33592         }
33593         
33594         return true;
33595     },
33596
33597     getValue : function()
33598     {
33599         var v = this.hiddenEl().getValue();
33600         
33601         return this.fixPrecision(this.parseValue(v));
33602     },
33603
33604     parseValue : function(value)
33605     {
33606         if(this.thousandsDelimiter) {
33607             value += "";
33608             r = new RegExp(",", "g");
33609             value = value.replace(r, "");
33610         }
33611         
33612         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33613         return isNaN(value) ? '' : value;
33614     },
33615
33616     fixPrecision : function(value)
33617     {
33618         if(this.thousandsDelimiter) {
33619             value += "";
33620             r = new RegExp(",", "g");
33621             value = value.replace(r, "");
33622         }
33623         
33624         var nan = isNaN(value);
33625         
33626         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33627             return nan ? '' : value;
33628         }
33629         return parseFloat(value).toFixed(this.decimalPrecision);
33630     },
33631
33632     setValue : function(v)
33633     {
33634         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33635         
33636         this.value = v;
33637         
33638         if(this.rendered){
33639             
33640             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33641             
33642             this.inputEl().dom.value = (v == '') ? '' :
33643                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33644             
33645             if(!this.allowZero && v === '0') {
33646                 this.hiddenEl().dom.value = '';
33647                 this.inputEl().dom.value = '';
33648             }
33649             
33650             this.validate();
33651         }
33652     },
33653
33654     decimalPrecisionFcn : function(v)
33655     {
33656         return Math.floor(v);
33657     },
33658
33659     beforeBlur : function()
33660     {
33661         var v = this.parseValue(this.getRawValue());
33662         
33663         if(v || v === 0 || v === ''){
33664             this.setValue(v);
33665         }
33666     },
33667     
33668     hiddenEl : function()
33669     {
33670         return this.el.select('input.hidden-number-input',true).first();
33671     }
33672     
33673 });
33674
33675  
33676
33677 /*
33678 * Licence: LGPL
33679 */
33680
33681 /**
33682  * @class Roo.bootstrap.DocumentSlider
33683  * @extends Roo.bootstrap.Component
33684  * Bootstrap DocumentSlider class
33685  * 
33686  * @constructor
33687  * Create a new DocumentViewer
33688  * @param {Object} config The config object
33689  */
33690
33691 Roo.bootstrap.DocumentSlider = function(config){
33692     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33693     
33694     this.files = [];
33695     
33696     this.addEvents({
33697         /**
33698          * @event initial
33699          * Fire after initEvent
33700          * @param {Roo.bootstrap.DocumentSlider} this
33701          */
33702         "initial" : true,
33703         /**
33704          * @event update
33705          * Fire after update
33706          * @param {Roo.bootstrap.DocumentSlider} this
33707          */
33708         "update" : true,
33709         /**
33710          * @event click
33711          * Fire after click
33712          * @param {Roo.bootstrap.DocumentSlider} this
33713          */
33714         "click" : true
33715     });
33716 };
33717
33718 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33719     
33720     files : false,
33721     
33722     indicator : 0,
33723     
33724     getAutoCreate : function()
33725     {
33726         var cfg = {
33727             tag : 'div',
33728             cls : 'roo-document-slider',
33729             cn : [
33730                 {
33731                     tag : 'div',
33732                     cls : 'roo-document-slider-header',
33733                     cn : [
33734                         {
33735                             tag : 'div',
33736                             cls : 'roo-document-slider-header-title'
33737                         }
33738                     ]
33739                 },
33740                 {
33741                     tag : 'div',
33742                     cls : 'roo-document-slider-body',
33743                     cn : [
33744                         {
33745                             tag : 'div',
33746                             cls : 'roo-document-slider-prev',
33747                             cn : [
33748                                 {
33749                                     tag : 'i',
33750                                     cls : 'fa fa-chevron-left'
33751                                 }
33752                             ]
33753                         },
33754                         {
33755                             tag : 'div',
33756                             cls : 'roo-document-slider-thumb',
33757                             cn : [
33758                                 {
33759                                     tag : 'img',
33760                                     cls : 'roo-document-slider-image'
33761                                 }
33762                             ]
33763                         },
33764                         {
33765                             tag : 'div',
33766                             cls : 'roo-document-slider-next',
33767                             cn : [
33768                                 {
33769                                     tag : 'i',
33770                                     cls : 'fa fa-chevron-right'
33771                                 }
33772                             ]
33773                         }
33774                     ]
33775                 }
33776             ]
33777         };
33778         
33779         return cfg;
33780     },
33781     
33782     initEvents : function()
33783     {
33784         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33785         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33786         
33787         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33788         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33789         
33790         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33791         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33792         
33793         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33794         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33795         
33796         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33797         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33798         
33799         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33800         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33801         
33802         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33803         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33804         
33805         this.thumbEl.on('click', this.onClick, this);
33806         
33807         this.prevIndicator.on('click', this.prev, this);
33808         
33809         this.nextIndicator.on('click', this.next, this);
33810         
33811     },
33812     
33813     initial : function()
33814     {
33815         if(this.files.length){
33816             this.indicator = 1;
33817             this.update()
33818         }
33819         
33820         this.fireEvent('initial', this);
33821     },
33822     
33823     update : function()
33824     {
33825         this.imageEl.attr('src', this.files[this.indicator - 1]);
33826         
33827         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33828         
33829         this.prevIndicator.show();
33830         
33831         if(this.indicator == 1){
33832             this.prevIndicator.hide();
33833         }
33834         
33835         this.nextIndicator.show();
33836         
33837         if(this.indicator == this.files.length){
33838             this.nextIndicator.hide();
33839         }
33840         
33841         this.thumbEl.scrollTo('top');
33842         
33843         this.fireEvent('update', this);
33844     },
33845     
33846     onClick : function(e)
33847     {
33848         e.preventDefault();
33849         
33850         this.fireEvent('click', this);
33851     },
33852     
33853     prev : function(e)
33854     {
33855         e.preventDefault();
33856         
33857         this.indicator = Math.max(1, this.indicator - 1);
33858         
33859         this.update();
33860     },
33861     
33862     next : function(e)
33863     {
33864         e.preventDefault();
33865         
33866         this.indicator = Math.min(this.files.length, this.indicator + 1);
33867         
33868         this.update();
33869     }
33870 });
33871 /*
33872  * - LGPL
33873  *
33874  * RadioSet
33875  *
33876  *
33877  */
33878
33879 /**
33880  * @class Roo.bootstrap.RadioSet
33881  * @extends Roo.bootstrap.Input
33882  * Bootstrap RadioSet class
33883  * @cfg {String} indicatorpos (left|right) default left
33884  * @cfg {Boolean} inline (true|false) inline the element (default true)
33885  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33886  * @constructor
33887  * Create a new RadioSet
33888  * @param {Object} config The config object
33889  */
33890
33891 Roo.bootstrap.RadioSet = function(config){
33892     
33893     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33894     
33895     this.radioes = [];
33896     
33897     Roo.bootstrap.RadioSet.register(this);
33898     
33899     this.addEvents({
33900         /**
33901         * @event check
33902         * Fires when the element is checked or unchecked.
33903         * @param {Roo.bootstrap.RadioSet} this This radio
33904         * @param {Roo.bootstrap.Radio} item The checked item
33905         */
33906        check : true,
33907        /**
33908         * @event click
33909         * Fires when the element is click.
33910         * @param {Roo.bootstrap.RadioSet} this This radio set
33911         * @param {Roo.bootstrap.Radio} item The checked item
33912         * @param {Roo.EventObject} e The event object
33913         */
33914        click : true
33915     });
33916     
33917 };
33918
33919 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33920
33921     radioes : false,
33922     
33923     inline : true,
33924     
33925     weight : '',
33926     
33927     indicatorpos : 'left',
33928     
33929     getAutoCreate : function()
33930     {
33931         var label = {
33932             tag : 'label',
33933             cls : 'roo-radio-set-label',
33934             cn : [
33935                 {
33936                     tag : 'span',
33937                     html : this.fieldLabel
33938                 }
33939             ]
33940         };
33941         
33942         if(this.indicatorpos == 'left'){
33943             label.cn.unshift({
33944                 tag : 'i',
33945                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33946                 tooltip : 'This field is required'
33947             });
33948         } else {
33949             label.cn.push({
33950                 tag : 'i',
33951                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33952                 tooltip : 'This field is required'
33953             });
33954         }
33955         
33956         var items = {
33957             tag : 'div',
33958             cls : 'roo-radio-set-items'
33959         };
33960         
33961         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33962         
33963         if (align === 'left' && this.fieldLabel.length) {
33964             
33965             items = {
33966                 cls : "roo-radio-set-right", 
33967                 cn: [
33968                     items
33969                 ]
33970             };
33971             
33972             if(this.labelWidth > 12){
33973                 label.style = "width: " + this.labelWidth + 'px';
33974             }
33975             
33976             if(this.labelWidth < 13 && this.labelmd == 0){
33977                 this.labelmd = this.labelWidth;
33978             }
33979             
33980             if(this.labellg > 0){
33981                 label.cls += ' col-lg-' + this.labellg;
33982                 items.cls += ' col-lg-' + (12 - this.labellg);
33983             }
33984             
33985             if(this.labelmd > 0){
33986                 label.cls += ' col-md-' + this.labelmd;
33987                 items.cls += ' col-md-' + (12 - this.labelmd);
33988             }
33989             
33990             if(this.labelsm > 0){
33991                 label.cls += ' col-sm-' + this.labelsm;
33992                 items.cls += ' col-sm-' + (12 - this.labelsm);
33993             }
33994             
33995             if(this.labelxs > 0){
33996                 label.cls += ' col-xs-' + this.labelxs;
33997                 items.cls += ' col-xs-' + (12 - this.labelxs);
33998             }
33999         }
34000         
34001         var cfg = {
34002             tag : 'div',
34003             cls : 'roo-radio-set',
34004             cn : [
34005                 {
34006                     tag : 'input',
34007                     cls : 'roo-radio-set-input',
34008                     type : 'hidden',
34009                     name : this.name,
34010                     value : this.value ? this.value :  ''
34011                 },
34012                 label,
34013                 items
34014             ]
34015         };
34016         
34017         if(this.weight.length){
34018             cfg.cls += ' roo-radio-' + this.weight;
34019         }
34020         
34021         if(this.inline) {
34022             cfg.cls += ' roo-radio-set-inline';
34023         }
34024         
34025         var settings=this;
34026         ['xs','sm','md','lg'].map(function(size){
34027             if (settings[size]) {
34028                 cfg.cls += ' col-' + size + '-' + settings[size];
34029             }
34030         });
34031         
34032         return cfg;
34033         
34034     },
34035
34036     initEvents : function()
34037     {
34038         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34039         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34040         
34041         if(!this.fieldLabel.length){
34042             this.labelEl.hide();
34043         }
34044         
34045         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34046         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34047         
34048         this.indicator = this.indicatorEl();
34049         
34050         if(this.indicator){
34051             this.indicator.addClass('invisible');
34052         }
34053         
34054         this.originalValue = this.getValue();
34055         
34056     },
34057     
34058     inputEl: function ()
34059     {
34060         return this.el.select('.roo-radio-set-input', true).first();
34061     },
34062     
34063     getChildContainer : function()
34064     {
34065         return this.itemsEl;
34066     },
34067     
34068     register : function(item)
34069     {
34070         this.radioes.push(item);
34071         
34072     },
34073     
34074     validate : function()
34075     {   
34076         if(this.getVisibilityEl().hasClass('hidden')){
34077             return true;
34078         }
34079         
34080         var valid = false;
34081         
34082         Roo.each(this.radioes, function(i){
34083             if(!i.checked){
34084                 return;
34085             }
34086             
34087             valid = true;
34088             return false;
34089         });
34090         
34091         if(this.allowBlank) {
34092             return true;
34093         }
34094         
34095         if(this.disabled || valid){
34096             this.markValid();
34097             return true;
34098         }
34099         
34100         this.markInvalid();
34101         return false;
34102         
34103     },
34104     
34105     markValid : function()
34106     {
34107         if(this.labelEl.isVisible(true)){
34108             this.indicatorEl().removeClass('visible');
34109             this.indicatorEl().addClass('invisible');
34110         }
34111         
34112         this.el.removeClass([this.invalidClass, this.validClass]);
34113         this.el.addClass(this.validClass);
34114         
34115         this.fireEvent('valid', this);
34116     },
34117     
34118     markInvalid : function(msg)
34119     {
34120         if(this.allowBlank || this.disabled){
34121             return;
34122         }
34123         
34124         if(this.labelEl.isVisible(true)){
34125             this.indicatorEl().removeClass('invisible');
34126             this.indicatorEl().addClass('visible');
34127         }
34128         
34129         this.el.removeClass([this.invalidClass, this.validClass]);
34130         this.el.addClass(this.invalidClass);
34131         
34132         this.fireEvent('invalid', this, msg);
34133         
34134     },
34135     
34136     setValue : function(v, suppressEvent)
34137     {   
34138         if(this.value === v){
34139             return;
34140         }
34141         
34142         this.value = v;
34143         
34144         if(this.rendered){
34145             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34146         }
34147         
34148         Roo.each(this.radioes, function(i){
34149             i.checked = false;
34150             i.el.removeClass('checked');
34151         });
34152         
34153         Roo.each(this.radioes, function(i){
34154             
34155             if(i.value === v || i.value.toString() === v.toString()){
34156                 i.checked = true;
34157                 i.el.addClass('checked');
34158                 
34159                 if(suppressEvent !== true){
34160                     this.fireEvent('check', this, i);
34161                 }
34162                 
34163                 return false;
34164             }
34165             
34166         }, this);
34167         
34168         this.validate();
34169     },
34170     
34171     clearInvalid : function(){
34172         
34173         if(!this.el || this.preventMark){
34174             return;
34175         }
34176         
34177         this.el.removeClass([this.invalidClass]);
34178         
34179         this.fireEvent('valid', this);
34180     }
34181     
34182 });
34183
34184 Roo.apply(Roo.bootstrap.RadioSet, {
34185     
34186     groups: {},
34187     
34188     register : function(set)
34189     {
34190         this.groups[set.name] = set;
34191     },
34192     
34193     get: function(name) 
34194     {
34195         if (typeof(this.groups[name]) == 'undefined') {
34196             return false;
34197         }
34198         
34199         return this.groups[name] ;
34200     }
34201     
34202 });
34203 /*
34204  * Based on:
34205  * Ext JS Library 1.1.1
34206  * Copyright(c) 2006-2007, Ext JS, LLC.
34207  *
34208  * Originally Released Under LGPL - original licence link has changed is not relivant.
34209  *
34210  * Fork - LGPL
34211  * <script type="text/javascript">
34212  */
34213
34214
34215 /**
34216  * @class Roo.bootstrap.SplitBar
34217  * @extends Roo.util.Observable
34218  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34219  * <br><br>
34220  * Usage:
34221  * <pre><code>
34222 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34223                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34224 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34225 split.minSize = 100;
34226 split.maxSize = 600;
34227 split.animate = true;
34228 split.on('moved', splitterMoved);
34229 </code></pre>
34230  * @constructor
34231  * Create a new SplitBar
34232  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34233  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34234  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34235  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34236                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34237                         position of the SplitBar).
34238  */
34239 Roo.bootstrap.SplitBar = function(cfg){
34240     
34241     /** @private */
34242     
34243     //{
34244     //  dragElement : elm
34245     //  resizingElement: el,
34246         // optional..
34247     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34248     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34249         // existingProxy ???
34250     //}
34251     
34252     this.el = Roo.get(cfg.dragElement, true);
34253     this.el.dom.unselectable = "on";
34254     /** @private */
34255     this.resizingEl = Roo.get(cfg.resizingElement, true);
34256
34257     /**
34258      * @private
34259      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34260      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34261      * @type Number
34262      */
34263     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34264     
34265     /**
34266      * The minimum size of the resizing element. (Defaults to 0)
34267      * @type Number
34268      */
34269     this.minSize = 0;
34270     
34271     /**
34272      * The maximum size of the resizing element. (Defaults to 2000)
34273      * @type Number
34274      */
34275     this.maxSize = 2000;
34276     
34277     /**
34278      * Whether to animate the transition to the new size
34279      * @type Boolean
34280      */
34281     this.animate = false;
34282     
34283     /**
34284      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34285      * @type Boolean
34286      */
34287     this.useShim = false;
34288     
34289     /** @private */
34290     this.shim = null;
34291     
34292     if(!cfg.existingProxy){
34293         /** @private */
34294         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34295     }else{
34296         this.proxy = Roo.get(cfg.existingProxy).dom;
34297     }
34298     /** @private */
34299     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34300     
34301     /** @private */
34302     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34303     
34304     /** @private */
34305     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34306     
34307     /** @private */
34308     this.dragSpecs = {};
34309     
34310     /**
34311      * @private The adapter to use to positon and resize elements
34312      */
34313     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34314     this.adapter.init(this);
34315     
34316     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34317         /** @private */
34318         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34319         this.el.addClass("roo-splitbar-h");
34320     }else{
34321         /** @private */
34322         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34323         this.el.addClass("roo-splitbar-v");
34324     }
34325     
34326     this.addEvents({
34327         /**
34328          * @event resize
34329          * Fires when the splitter is moved (alias for {@link #event-moved})
34330          * @param {Roo.bootstrap.SplitBar} this
34331          * @param {Number} newSize the new width or height
34332          */
34333         "resize" : true,
34334         /**
34335          * @event moved
34336          * Fires when the splitter is moved
34337          * @param {Roo.bootstrap.SplitBar} this
34338          * @param {Number} newSize the new width or height
34339          */
34340         "moved" : true,
34341         /**
34342          * @event beforeresize
34343          * Fires before the splitter is dragged
34344          * @param {Roo.bootstrap.SplitBar} this
34345          */
34346         "beforeresize" : true,
34347
34348         "beforeapply" : true
34349     });
34350
34351     Roo.util.Observable.call(this);
34352 };
34353
34354 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34355     onStartProxyDrag : function(x, y){
34356         this.fireEvent("beforeresize", this);
34357         if(!this.overlay){
34358             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34359             o.unselectable();
34360             o.enableDisplayMode("block");
34361             // all splitbars share the same overlay
34362             Roo.bootstrap.SplitBar.prototype.overlay = o;
34363         }
34364         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34365         this.overlay.show();
34366         Roo.get(this.proxy).setDisplayed("block");
34367         var size = this.adapter.getElementSize(this);
34368         this.activeMinSize = this.getMinimumSize();;
34369         this.activeMaxSize = this.getMaximumSize();;
34370         var c1 = size - this.activeMinSize;
34371         var c2 = Math.max(this.activeMaxSize - size, 0);
34372         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34373             this.dd.resetConstraints();
34374             this.dd.setXConstraint(
34375                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34376                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34377             );
34378             this.dd.setYConstraint(0, 0);
34379         }else{
34380             this.dd.resetConstraints();
34381             this.dd.setXConstraint(0, 0);
34382             this.dd.setYConstraint(
34383                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34384                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34385             );
34386          }
34387         this.dragSpecs.startSize = size;
34388         this.dragSpecs.startPoint = [x, y];
34389         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34390     },
34391     
34392     /** 
34393      * @private Called after the drag operation by the DDProxy
34394      */
34395     onEndProxyDrag : function(e){
34396         Roo.get(this.proxy).setDisplayed(false);
34397         var endPoint = Roo.lib.Event.getXY(e);
34398         if(this.overlay){
34399             this.overlay.hide();
34400         }
34401         var newSize;
34402         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34403             newSize = this.dragSpecs.startSize + 
34404                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34405                     endPoint[0] - this.dragSpecs.startPoint[0] :
34406                     this.dragSpecs.startPoint[0] - endPoint[0]
34407                 );
34408         }else{
34409             newSize = this.dragSpecs.startSize + 
34410                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34411                     endPoint[1] - this.dragSpecs.startPoint[1] :
34412                     this.dragSpecs.startPoint[1] - endPoint[1]
34413                 );
34414         }
34415         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34416         if(newSize != this.dragSpecs.startSize){
34417             if(this.fireEvent('beforeapply', this, newSize) !== false){
34418                 this.adapter.setElementSize(this, newSize);
34419                 this.fireEvent("moved", this, newSize);
34420                 this.fireEvent("resize", this, newSize);
34421             }
34422         }
34423     },
34424     
34425     /**
34426      * Get the adapter this SplitBar uses
34427      * @return The adapter object
34428      */
34429     getAdapter : function(){
34430         return this.adapter;
34431     },
34432     
34433     /**
34434      * Set the adapter this SplitBar uses
34435      * @param {Object} adapter A SplitBar adapter object
34436      */
34437     setAdapter : function(adapter){
34438         this.adapter = adapter;
34439         this.adapter.init(this);
34440     },
34441     
34442     /**
34443      * Gets the minimum size for the resizing element
34444      * @return {Number} The minimum size
34445      */
34446     getMinimumSize : function(){
34447         return this.minSize;
34448     },
34449     
34450     /**
34451      * Sets the minimum size for the resizing element
34452      * @param {Number} minSize The minimum size
34453      */
34454     setMinimumSize : function(minSize){
34455         this.minSize = minSize;
34456     },
34457     
34458     /**
34459      * Gets the maximum size for the resizing element
34460      * @return {Number} The maximum size
34461      */
34462     getMaximumSize : function(){
34463         return this.maxSize;
34464     },
34465     
34466     /**
34467      * Sets the maximum size for the resizing element
34468      * @param {Number} maxSize The maximum size
34469      */
34470     setMaximumSize : function(maxSize){
34471         this.maxSize = maxSize;
34472     },
34473     
34474     /**
34475      * Sets the initialize size for the resizing element
34476      * @param {Number} size The initial size
34477      */
34478     setCurrentSize : function(size){
34479         var oldAnimate = this.animate;
34480         this.animate = false;
34481         this.adapter.setElementSize(this, size);
34482         this.animate = oldAnimate;
34483     },
34484     
34485     /**
34486      * Destroy this splitbar. 
34487      * @param {Boolean} removeEl True to remove the element
34488      */
34489     destroy : function(removeEl){
34490         if(this.shim){
34491             this.shim.remove();
34492         }
34493         this.dd.unreg();
34494         this.proxy.parentNode.removeChild(this.proxy);
34495         if(removeEl){
34496             this.el.remove();
34497         }
34498     }
34499 });
34500
34501 /**
34502  * @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.
34503  */
34504 Roo.bootstrap.SplitBar.createProxy = function(dir){
34505     var proxy = new Roo.Element(document.createElement("div"));
34506     proxy.unselectable();
34507     var cls = 'roo-splitbar-proxy';
34508     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34509     document.body.appendChild(proxy.dom);
34510     return proxy.dom;
34511 };
34512
34513 /** 
34514  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34515  * Default Adapter. It assumes the splitter and resizing element are not positioned
34516  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34517  */
34518 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34519 };
34520
34521 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34522     // do nothing for now
34523     init : function(s){
34524     
34525     },
34526     /**
34527      * Called before drag operations to get the current size of the resizing element. 
34528      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34529      */
34530      getElementSize : function(s){
34531         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34532             return s.resizingEl.getWidth();
34533         }else{
34534             return s.resizingEl.getHeight();
34535         }
34536     },
34537     
34538     /**
34539      * Called after drag operations to set the size of the resizing element.
34540      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34541      * @param {Number} newSize The new size to set
34542      * @param {Function} onComplete A function to be invoked when resizing is complete
34543      */
34544     setElementSize : function(s, newSize, onComplete){
34545         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34546             if(!s.animate){
34547                 s.resizingEl.setWidth(newSize);
34548                 if(onComplete){
34549                     onComplete(s, newSize);
34550                 }
34551             }else{
34552                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34553             }
34554         }else{
34555             
34556             if(!s.animate){
34557                 s.resizingEl.setHeight(newSize);
34558                 if(onComplete){
34559                     onComplete(s, newSize);
34560                 }
34561             }else{
34562                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34563             }
34564         }
34565     }
34566 };
34567
34568 /** 
34569  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34570  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34571  * Adapter that  moves the splitter element to align with the resized sizing element. 
34572  * Used with an absolute positioned SplitBar.
34573  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34574  * document.body, make sure you assign an id to the body element.
34575  */
34576 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34577     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34578     this.container = Roo.get(container);
34579 };
34580
34581 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34582     init : function(s){
34583         this.basic.init(s);
34584     },
34585     
34586     getElementSize : function(s){
34587         return this.basic.getElementSize(s);
34588     },
34589     
34590     setElementSize : function(s, newSize, onComplete){
34591         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34592     },
34593     
34594     moveSplitter : function(s){
34595         var yes = Roo.bootstrap.SplitBar;
34596         switch(s.placement){
34597             case yes.LEFT:
34598                 s.el.setX(s.resizingEl.getRight());
34599                 break;
34600             case yes.RIGHT:
34601                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34602                 break;
34603             case yes.TOP:
34604                 s.el.setY(s.resizingEl.getBottom());
34605                 break;
34606             case yes.BOTTOM:
34607                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34608                 break;
34609         }
34610     }
34611 };
34612
34613 /**
34614  * Orientation constant - Create a vertical SplitBar
34615  * @static
34616  * @type Number
34617  */
34618 Roo.bootstrap.SplitBar.VERTICAL = 1;
34619
34620 /**
34621  * Orientation constant - Create a horizontal SplitBar
34622  * @static
34623  * @type Number
34624  */
34625 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34626
34627 /**
34628  * Placement constant - The resizing element is to the left of the splitter element
34629  * @static
34630  * @type Number
34631  */
34632 Roo.bootstrap.SplitBar.LEFT = 1;
34633
34634 /**
34635  * Placement constant - The resizing element is to the right of the splitter element
34636  * @static
34637  * @type Number
34638  */
34639 Roo.bootstrap.SplitBar.RIGHT = 2;
34640
34641 /**
34642  * Placement constant - The resizing element is positioned above the splitter element
34643  * @static
34644  * @type Number
34645  */
34646 Roo.bootstrap.SplitBar.TOP = 3;
34647
34648 /**
34649  * Placement constant - The resizing element is positioned under splitter element
34650  * @static
34651  * @type Number
34652  */
34653 Roo.bootstrap.SplitBar.BOTTOM = 4;
34654 Roo.namespace("Roo.bootstrap.layout");/*
34655  * Based on:
34656  * Ext JS Library 1.1.1
34657  * Copyright(c) 2006-2007, Ext JS, LLC.
34658  *
34659  * Originally Released Under LGPL - original licence link has changed is not relivant.
34660  *
34661  * Fork - LGPL
34662  * <script type="text/javascript">
34663  */
34664
34665 /**
34666  * @class Roo.bootstrap.layout.Manager
34667  * @extends Roo.bootstrap.Component
34668  * Base class for layout managers.
34669  */
34670 Roo.bootstrap.layout.Manager = function(config)
34671 {
34672     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34673
34674
34675
34676
34677
34678     /** false to disable window resize monitoring @type Boolean */
34679     this.monitorWindowResize = true;
34680     this.regions = {};
34681     this.addEvents({
34682         /**
34683          * @event layout
34684          * Fires when a layout is performed.
34685          * @param {Roo.LayoutManager} this
34686          */
34687         "layout" : true,
34688         /**
34689          * @event regionresized
34690          * Fires when the user resizes a region.
34691          * @param {Roo.LayoutRegion} region The resized region
34692          * @param {Number} newSize The new size (width for east/west, height for north/south)
34693          */
34694         "regionresized" : true,
34695         /**
34696          * @event regioncollapsed
34697          * Fires when a region is collapsed.
34698          * @param {Roo.LayoutRegion} region The collapsed region
34699          */
34700         "regioncollapsed" : true,
34701         /**
34702          * @event regionexpanded
34703          * Fires when a region is expanded.
34704          * @param {Roo.LayoutRegion} region The expanded region
34705          */
34706         "regionexpanded" : true
34707     });
34708     this.updating = false;
34709
34710     if (config.el) {
34711         this.el = Roo.get(config.el);
34712         this.initEvents();
34713     }
34714
34715 };
34716
34717 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34718
34719
34720     regions : null,
34721
34722     monitorWindowResize : true,
34723
34724
34725     updating : false,
34726
34727
34728     onRender : function(ct, position)
34729     {
34730         if(!this.el){
34731             this.el = Roo.get(ct);
34732             this.initEvents();
34733         }
34734         //this.fireEvent('render',this);
34735     },
34736
34737
34738     initEvents: function()
34739     {
34740
34741
34742         // ie scrollbar fix
34743         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34744             document.body.scroll = "no";
34745         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34746             this.el.position('relative');
34747         }
34748         this.id = this.el.id;
34749         this.el.addClass("roo-layout-container");
34750         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34751         if(this.el.dom != document.body ) {
34752             this.el.on('resize', this.layout,this);
34753             this.el.on('show', this.layout,this);
34754         }
34755
34756     },
34757
34758     /**
34759      * Returns true if this layout is currently being updated
34760      * @return {Boolean}
34761      */
34762     isUpdating : function(){
34763         return this.updating;
34764     },
34765
34766     /**
34767      * Suspend the LayoutManager from doing auto-layouts while
34768      * making multiple add or remove calls
34769      */
34770     beginUpdate : function(){
34771         this.updating = true;
34772     },
34773
34774     /**
34775      * Restore auto-layouts and optionally disable the manager from performing a layout
34776      * @param {Boolean} noLayout true to disable a layout update
34777      */
34778     endUpdate : function(noLayout){
34779         this.updating = false;
34780         if(!noLayout){
34781             this.layout();
34782         }
34783     },
34784
34785     layout: function(){
34786         // abstract...
34787     },
34788
34789     onRegionResized : function(region, newSize){
34790         this.fireEvent("regionresized", region, newSize);
34791         this.layout();
34792     },
34793
34794     onRegionCollapsed : function(region){
34795         this.fireEvent("regioncollapsed", region);
34796     },
34797
34798     onRegionExpanded : function(region){
34799         this.fireEvent("regionexpanded", region);
34800     },
34801
34802     /**
34803      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34804      * performs box-model adjustments.
34805      * @return {Object} The size as an object {width: (the width), height: (the height)}
34806      */
34807     getViewSize : function()
34808     {
34809         var size;
34810         if(this.el.dom != document.body){
34811             size = this.el.getSize();
34812         }else{
34813             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34814         }
34815         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34816         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34817         return size;
34818     },
34819
34820     /**
34821      * Returns the Element this layout is bound to.
34822      * @return {Roo.Element}
34823      */
34824     getEl : function(){
34825         return this.el;
34826     },
34827
34828     /**
34829      * Returns the specified region.
34830      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34831      * @return {Roo.LayoutRegion}
34832      */
34833     getRegion : function(target){
34834         return this.regions[target.toLowerCase()];
34835     },
34836
34837     onWindowResize : function(){
34838         if(this.monitorWindowResize){
34839             this.layout();
34840         }
34841     }
34842 });
34843 /*
34844  * Based on:
34845  * Ext JS Library 1.1.1
34846  * Copyright(c) 2006-2007, Ext JS, LLC.
34847  *
34848  * Originally Released Under LGPL - original licence link has changed is not relivant.
34849  *
34850  * Fork - LGPL
34851  * <script type="text/javascript">
34852  */
34853 /**
34854  * @class Roo.bootstrap.layout.Border
34855  * @extends Roo.bootstrap.layout.Manager
34856  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34857  * please see: examples/bootstrap/nested.html<br><br>
34858  
34859 <b>The container the layout is rendered into can be either the body element or any other element.
34860 If it is not the body element, the container needs to either be an absolute positioned element,
34861 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34862 the container size if it is not the body element.</b>
34863
34864 * @constructor
34865 * Create a new Border
34866 * @param {Object} config Configuration options
34867  */
34868 Roo.bootstrap.layout.Border = function(config){
34869     config = config || {};
34870     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34871     
34872     
34873     
34874     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34875         if(config[region]){
34876             config[region].region = region;
34877             this.addRegion(config[region]);
34878         }
34879     },this);
34880     
34881 };
34882
34883 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34884
34885 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34886     /**
34887      * Creates and adds a new region if it doesn't already exist.
34888      * @param {String} target The target region key (north, south, east, west or center).
34889      * @param {Object} config The regions config object
34890      * @return {BorderLayoutRegion} The new region
34891      */
34892     addRegion : function(config)
34893     {
34894         if(!this.regions[config.region]){
34895             var r = this.factory(config);
34896             this.bindRegion(r);
34897         }
34898         return this.regions[config.region];
34899     },
34900
34901     // private (kinda)
34902     bindRegion : function(r){
34903         this.regions[r.config.region] = r;
34904         
34905         r.on("visibilitychange",    this.layout, this);
34906         r.on("paneladded",          this.layout, this);
34907         r.on("panelremoved",        this.layout, this);
34908         r.on("invalidated",         this.layout, this);
34909         r.on("resized",             this.onRegionResized, this);
34910         r.on("collapsed",           this.onRegionCollapsed, this);
34911         r.on("expanded",            this.onRegionExpanded, this);
34912     },
34913
34914     /**
34915      * Performs a layout update.
34916      */
34917     layout : function()
34918     {
34919         if(this.updating) {
34920             return;
34921         }
34922         
34923         // render all the rebions if they have not been done alreayd?
34924         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34925             if(this.regions[region] && !this.regions[region].bodyEl){
34926                 this.regions[region].onRender(this.el)
34927             }
34928         },this);
34929         
34930         var size = this.getViewSize();
34931         var w = size.width;
34932         var h = size.height;
34933         var centerW = w;
34934         var centerH = h;
34935         var centerY = 0;
34936         var centerX = 0;
34937         //var x = 0, y = 0;
34938
34939         var rs = this.regions;
34940         var north = rs["north"];
34941         var south = rs["south"]; 
34942         var west = rs["west"];
34943         var east = rs["east"];
34944         var center = rs["center"];
34945         //if(this.hideOnLayout){ // not supported anymore
34946             //c.el.setStyle("display", "none");
34947         //}
34948         if(north && north.isVisible()){
34949             var b = north.getBox();
34950             var m = north.getMargins();
34951             b.width = w - (m.left+m.right);
34952             b.x = m.left;
34953             b.y = m.top;
34954             centerY = b.height + b.y + m.bottom;
34955             centerH -= centerY;
34956             north.updateBox(this.safeBox(b));
34957         }
34958         if(south && south.isVisible()){
34959             var b = south.getBox();
34960             var m = south.getMargins();
34961             b.width = w - (m.left+m.right);
34962             b.x = m.left;
34963             var totalHeight = (b.height + m.top + m.bottom);
34964             b.y = h - totalHeight + m.top;
34965             centerH -= totalHeight;
34966             south.updateBox(this.safeBox(b));
34967         }
34968         if(west && west.isVisible()){
34969             var b = west.getBox();
34970             var m = west.getMargins();
34971             b.height = centerH - (m.top+m.bottom);
34972             b.x = m.left;
34973             b.y = centerY + m.top;
34974             var totalWidth = (b.width + m.left + m.right);
34975             centerX += totalWidth;
34976             centerW -= totalWidth;
34977             west.updateBox(this.safeBox(b));
34978         }
34979         if(east && east.isVisible()){
34980             var b = east.getBox();
34981             var m = east.getMargins();
34982             b.height = centerH - (m.top+m.bottom);
34983             var totalWidth = (b.width + m.left + m.right);
34984             b.x = w - totalWidth + m.left;
34985             b.y = centerY + m.top;
34986             centerW -= totalWidth;
34987             east.updateBox(this.safeBox(b));
34988         }
34989         if(center){
34990             var m = center.getMargins();
34991             var centerBox = {
34992                 x: centerX + m.left,
34993                 y: centerY + m.top,
34994                 width: centerW - (m.left+m.right),
34995                 height: centerH - (m.top+m.bottom)
34996             };
34997             //if(this.hideOnLayout){
34998                 //center.el.setStyle("display", "block");
34999             //}
35000             center.updateBox(this.safeBox(centerBox));
35001         }
35002         this.el.repaint();
35003         this.fireEvent("layout", this);
35004     },
35005
35006     // private
35007     safeBox : function(box){
35008         box.width = Math.max(0, box.width);
35009         box.height = Math.max(0, box.height);
35010         return box;
35011     },
35012
35013     /**
35014      * Adds a ContentPanel (or subclass) to this layout.
35015      * @param {String} target The target region key (north, south, east, west or center).
35016      * @param {Roo.ContentPanel} panel The panel to add
35017      * @return {Roo.ContentPanel} The added panel
35018      */
35019     add : function(target, panel){
35020          
35021         target = target.toLowerCase();
35022         return this.regions[target].add(panel);
35023     },
35024
35025     /**
35026      * Remove a ContentPanel (or subclass) to this layout.
35027      * @param {String} target The target region key (north, south, east, west or center).
35028      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35029      * @return {Roo.ContentPanel} The removed panel
35030      */
35031     remove : function(target, panel){
35032         target = target.toLowerCase();
35033         return this.regions[target].remove(panel);
35034     },
35035
35036     /**
35037      * Searches all regions for a panel with the specified id
35038      * @param {String} panelId
35039      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35040      */
35041     findPanel : function(panelId){
35042         var rs = this.regions;
35043         for(var target in rs){
35044             if(typeof rs[target] != "function"){
35045                 var p = rs[target].getPanel(panelId);
35046                 if(p){
35047                     return p;
35048                 }
35049             }
35050         }
35051         return null;
35052     },
35053
35054     /**
35055      * Searches all regions for a panel with the specified id and activates (shows) it.
35056      * @param {String/ContentPanel} panelId The panels id or the panel itself
35057      * @return {Roo.ContentPanel} The shown panel or null
35058      */
35059     showPanel : function(panelId) {
35060       var rs = this.regions;
35061       for(var target in rs){
35062          var r = rs[target];
35063          if(typeof r != "function"){
35064             if(r.hasPanel(panelId)){
35065                return r.showPanel(panelId);
35066             }
35067          }
35068       }
35069       return null;
35070    },
35071
35072    /**
35073      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35074      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35075      */
35076    /*
35077     restoreState : function(provider){
35078         if(!provider){
35079             provider = Roo.state.Manager;
35080         }
35081         var sm = new Roo.LayoutStateManager();
35082         sm.init(this, provider);
35083     },
35084 */
35085  
35086  
35087     /**
35088      * Adds a xtype elements to the layout.
35089      * <pre><code>
35090
35091 layout.addxtype({
35092        xtype : 'ContentPanel',
35093        region: 'west',
35094        items: [ .... ]
35095    }
35096 );
35097
35098 layout.addxtype({
35099         xtype : 'NestedLayoutPanel',
35100         region: 'west',
35101         layout: {
35102            center: { },
35103            west: { }   
35104         },
35105         items : [ ... list of content panels or nested layout panels.. ]
35106    }
35107 );
35108 </code></pre>
35109      * @param {Object} cfg Xtype definition of item to add.
35110      */
35111     addxtype : function(cfg)
35112     {
35113         // basically accepts a pannel...
35114         // can accept a layout region..!?!?
35115         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35116         
35117         
35118         // theory?  children can only be panels??
35119         
35120         //if (!cfg.xtype.match(/Panel$/)) {
35121         //    return false;
35122         //}
35123         var ret = false;
35124         
35125         if (typeof(cfg.region) == 'undefined') {
35126             Roo.log("Failed to add Panel, region was not set");
35127             Roo.log(cfg);
35128             return false;
35129         }
35130         var region = cfg.region;
35131         delete cfg.region;
35132         
35133           
35134         var xitems = [];
35135         if (cfg.items) {
35136             xitems = cfg.items;
35137             delete cfg.items;
35138         }
35139         var nb = false;
35140         
35141         switch(cfg.xtype) 
35142         {
35143             case 'Content':  // ContentPanel (el, cfg)
35144             case 'Scroll':  // ContentPanel (el, cfg)
35145             case 'View': 
35146                 cfg.autoCreate = true;
35147                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35148                 //} else {
35149                 //    var el = this.el.createChild();
35150                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35151                 //}
35152                 
35153                 this.add(region, ret);
35154                 break;
35155             
35156             /*
35157             case 'TreePanel': // our new panel!
35158                 cfg.el = this.el.createChild();
35159                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35160                 this.add(region, ret);
35161                 break;
35162             */
35163             
35164             case 'Nest': 
35165                 // create a new Layout (which is  a Border Layout...
35166                 
35167                 var clayout = cfg.layout;
35168                 clayout.el  = this.el.createChild();
35169                 clayout.items   = clayout.items  || [];
35170                 
35171                 delete cfg.layout;
35172                 
35173                 // replace this exitems with the clayout ones..
35174                 xitems = clayout.items;
35175                  
35176                 // force background off if it's in center...
35177                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35178                     cfg.background = false;
35179                 }
35180                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35181                 
35182                 
35183                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35184                 //console.log('adding nested layout panel '  + cfg.toSource());
35185                 this.add(region, ret);
35186                 nb = {}; /// find first...
35187                 break;
35188             
35189             case 'Grid':
35190                 
35191                 // needs grid and region
35192                 
35193                 //var el = this.getRegion(region).el.createChild();
35194                 /*
35195                  *var el = this.el.createChild();
35196                 // create the grid first...
35197                 cfg.grid.container = el;
35198                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35199                 */
35200                 
35201                 if (region == 'center' && this.active ) {
35202                     cfg.background = false;
35203                 }
35204                 
35205                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35206                 
35207                 this.add(region, ret);
35208                 /*
35209                 if (cfg.background) {
35210                     // render grid on panel activation (if panel background)
35211                     ret.on('activate', function(gp) {
35212                         if (!gp.grid.rendered) {
35213                     //        gp.grid.render(el);
35214                         }
35215                     });
35216                 } else {
35217                   //  cfg.grid.render(el);
35218                 }
35219                 */
35220                 break;
35221            
35222            
35223             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35224                 // it was the old xcomponent building that caused this before.
35225                 // espeically if border is the top element in the tree.
35226                 ret = this;
35227                 break; 
35228                 
35229                     
35230                 
35231                 
35232                 
35233             default:
35234                 /*
35235                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35236                     
35237                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35238                     this.add(region, ret);
35239                 } else {
35240                 */
35241                     Roo.log(cfg);
35242                     throw "Can not add '" + cfg.xtype + "' to Border";
35243                     return null;
35244              
35245                                 
35246              
35247         }
35248         this.beginUpdate();
35249         // add children..
35250         var region = '';
35251         var abn = {};
35252         Roo.each(xitems, function(i)  {
35253             region = nb && i.region ? i.region : false;
35254             
35255             var add = ret.addxtype(i);
35256            
35257             if (region) {
35258                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35259                 if (!i.background) {
35260                     abn[region] = nb[region] ;
35261                 }
35262             }
35263             
35264         });
35265         this.endUpdate();
35266
35267         // make the last non-background panel active..
35268         //if (nb) { Roo.log(abn); }
35269         if (nb) {
35270             
35271             for(var r in abn) {
35272                 region = this.getRegion(r);
35273                 if (region) {
35274                     // tried using nb[r], but it does not work..
35275                      
35276                     region.showPanel(abn[r]);
35277                    
35278                 }
35279             }
35280         }
35281         return ret;
35282         
35283     },
35284     
35285     
35286 // private
35287     factory : function(cfg)
35288     {
35289         
35290         var validRegions = Roo.bootstrap.layout.Border.regions;
35291
35292         var target = cfg.region;
35293         cfg.mgr = this;
35294         
35295         var r = Roo.bootstrap.layout;
35296         Roo.log(target);
35297         switch(target){
35298             case "north":
35299                 return new r.North(cfg);
35300             case "south":
35301                 return new r.South(cfg);
35302             case "east":
35303                 return new r.East(cfg);
35304             case "west":
35305                 return new r.West(cfg);
35306             case "center":
35307                 return new r.Center(cfg);
35308         }
35309         throw 'Layout region "'+target+'" not supported.';
35310     }
35311     
35312     
35313 });
35314  /*
35315  * Based on:
35316  * Ext JS Library 1.1.1
35317  * Copyright(c) 2006-2007, Ext JS, LLC.
35318  *
35319  * Originally Released Under LGPL - original licence link has changed is not relivant.
35320  *
35321  * Fork - LGPL
35322  * <script type="text/javascript">
35323  */
35324  
35325 /**
35326  * @class Roo.bootstrap.layout.Basic
35327  * @extends Roo.util.Observable
35328  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35329  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35330  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35331  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35332  * @cfg {string}   region  the region that it inhabits..
35333  * @cfg {bool}   skipConfig skip config?
35334  * 
35335
35336  */
35337 Roo.bootstrap.layout.Basic = function(config){
35338     
35339     this.mgr = config.mgr;
35340     
35341     this.position = config.region;
35342     
35343     var skipConfig = config.skipConfig;
35344     
35345     this.events = {
35346         /**
35347          * @scope Roo.BasicLayoutRegion
35348          */
35349         
35350         /**
35351          * @event beforeremove
35352          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35353          * @param {Roo.LayoutRegion} this
35354          * @param {Roo.ContentPanel} panel The panel
35355          * @param {Object} e The cancel event object
35356          */
35357         "beforeremove" : true,
35358         /**
35359          * @event invalidated
35360          * Fires when the layout for this region is changed.
35361          * @param {Roo.LayoutRegion} this
35362          */
35363         "invalidated" : true,
35364         /**
35365          * @event visibilitychange
35366          * Fires when this region is shown or hidden 
35367          * @param {Roo.LayoutRegion} this
35368          * @param {Boolean} visibility true or false
35369          */
35370         "visibilitychange" : true,
35371         /**
35372          * @event paneladded
35373          * Fires when a panel is added. 
35374          * @param {Roo.LayoutRegion} this
35375          * @param {Roo.ContentPanel} panel The panel
35376          */
35377         "paneladded" : true,
35378         /**
35379          * @event panelremoved
35380          * Fires when a panel is removed. 
35381          * @param {Roo.LayoutRegion} this
35382          * @param {Roo.ContentPanel} panel The panel
35383          */
35384         "panelremoved" : true,
35385         /**
35386          * @event beforecollapse
35387          * Fires when this region before collapse.
35388          * @param {Roo.LayoutRegion} this
35389          */
35390         "beforecollapse" : true,
35391         /**
35392          * @event collapsed
35393          * Fires when this region is collapsed.
35394          * @param {Roo.LayoutRegion} this
35395          */
35396         "collapsed" : true,
35397         /**
35398          * @event expanded
35399          * Fires when this region is expanded.
35400          * @param {Roo.LayoutRegion} this
35401          */
35402         "expanded" : true,
35403         /**
35404          * @event slideshow
35405          * Fires when this region is slid into view.
35406          * @param {Roo.LayoutRegion} this
35407          */
35408         "slideshow" : true,
35409         /**
35410          * @event slidehide
35411          * Fires when this region slides out of view. 
35412          * @param {Roo.LayoutRegion} this
35413          */
35414         "slidehide" : true,
35415         /**
35416          * @event panelactivated
35417          * Fires when a panel is activated. 
35418          * @param {Roo.LayoutRegion} this
35419          * @param {Roo.ContentPanel} panel The activated panel
35420          */
35421         "panelactivated" : true,
35422         /**
35423          * @event resized
35424          * Fires when the user resizes this region. 
35425          * @param {Roo.LayoutRegion} this
35426          * @param {Number} newSize The new size (width for east/west, height for north/south)
35427          */
35428         "resized" : true
35429     };
35430     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35431     this.panels = new Roo.util.MixedCollection();
35432     this.panels.getKey = this.getPanelId.createDelegate(this);
35433     this.box = null;
35434     this.activePanel = null;
35435     // ensure listeners are added...
35436     
35437     if (config.listeners || config.events) {
35438         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35439             listeners : config.listeners || {},
35440             events : config.events || {}
35441         });
35442     }
35443     
35444     if(skipConfig !== true){
35445         this.applyConfig(config);
35446     }
35447 };
35448
35449 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35450 {
35451     getPanelId : function(p){
35452         return p.getId();
35453     },
35454     
35455     applyConfig : function(config){
35456         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35457         this.config = config;
35458         
35459     },
35460     
35461     /**
35462      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35463      * the width, for horizontal (north, south) the height.
35464      * @param {Number} newSize The new width or height
35465      */
35466     resizeTo : function(newSize){
35467         var el = this.el ? this.el :
35468                  (this.activePanel ? this.activePanel.getEl() : null);
35469         if(el){
35470             switch(this.position){
35471                 case "east":
35472                 case "west":
35473                     el.setWidth(newSize);
35474                     this.fireEvent("resized", this, newSize);
35475                 break;
35476                 case "north":
35477                 case "south":
35478                     el.setHeight(newSize);
35479                     this.fireEvent("resized", this, newSize);
35480                 break;                
35481             }
35482         }
35483     },
35484     
35485     getBox : function(){
35486         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35487     },
35488     
35489     getMargins : function(){
35490         return this.margins;
35491     },
35492     
35493     updateBox : function(box){
35494         this.box = box;
35495         var el = this.activePanel.getEl();
35496         el.dom.style.left = box.x + "px";
35497         el.dom.style.top = box.y + "px";
35498         this.activePanel.setSize(box.width, box.height);
35499     },
35500     
35501     /**
35502      * Returns the container element for this region.
35503      * @return {Roo.Element}
35504      */
35505     getEl : function(){
35506         return this.activePanel;
35507     },
35508     
35509     /**
35510      * Returns true if this region is currently visible.
35511      * @return {Boolean}
35512      */
35513     isVisible : function(){
35514         return this.activePanel ? true : false;
35515     },
35516     
35517     setActivePanel : function(panel){
35518         panel = this.getPanel(panel);
35519         if(this.activePanel && this.activePanel != panel){
35520             this.activePanel.setActiveState(false);
35521             this.activePanel.getEl().setLeftTop(-10000,-10000);
35522         }
35523         this.activePanel = panel;
35524         panel.setActiveState(true);
35525         if(this.box){
35526             panel.setSize(this.box.width, this.box.height);
35527         }
35528         this.fireEvent("panelactivated", this, panel);
35529         this.fireEvent("invalidated");
35530     },
35531     
35532     /**
35533      * Show the specified panel.
35534      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35535      * @return {Roo.ContentPanel} The shown panel or null
35536      */
35537     showPanel : function(panel){
35538         panel = this.getPanel(panel);
35539         if(panel){
35540             this.setActivePanel(panel);
35541         }
35542         return panel;
35543     },
35544     
35545     /**
35546      * Get the active panel for this region.
35547      * @return {Roo.ContentPanel} The active panel or null
35548      */
35549     getActivePanel : function(){
35550         return this.activePanel;
35551     },
35552     
35553     /**
35554      * Add the passed ContentPanel(s)
35555      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35556      * @return {Roo.ContentPanel} The panel added (if only one was added)
35557      */
35558     add : function(panel){
35559         if(arguments.length > 1){
35560             for(var i = 0, len = arguments.length; i < len; i++) {
35561                 this.add(arguments[i]);
35562             }
35563             return null;
35564         }
35565         if(this.hasPanel(panel)){
35566             this.showPanel(panel);
35567             return panel;
35568         }
35569         var el = panel.getEl();
35570         if(el.dom.parentNode != this.mgr.el.dom){
35571             this.mgr.el.dom.appendChild(el.dom);
35572         }
35573         if(panel.setRegion){
35574             panel.setRegion(this);
35575         }
35576         this.panels.add(panel);
35577         el.setStyle("position", "absolute");
35578         if(!panel.background){
35579             this.setActivePanel(panel);
35580             if(this.config.initialSize && this.panels.getCount()==1){
35581                 this.resizeTo(this.config.initialSize);
35582             }
35583         }
35584         this.fireEvent("paneladded", this, panel);
35585         return panel;
35586     },
35587     
35588     /**
35589      * Returns true if the panel is in this region.
35590      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35591      * @return {Boolean}
35592      */
35593     hasPanel : function(panel){
35594         if(typeof panel == "object"){ // must be panel obj
35595             panel = panel.getId();
35596         }
35597         return this.getPanel(panel) ? true : false;
35598     },
35599     
35600     /**
35601      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35602      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35603      * @param {Boolean} preservePanel Overrides the config preservePanel option
35604      * @return {Roo.ContentPanel} The panel that was removed
35605      */
35606     remove : function(panel, preservePanel){
35607         panel = this.getPanel(panel);
35608         if(!panel){
35609             return null;
35610         }
35611         var e = {};
35612         this.fireEvent("beforeremove", this, panel, e);
35613         if(e.cancel === true){
35614             return null;
35615         }
35616         var panelId = panel.getId();
35617         this.panels.removeKey(panelId);
35618         return panel;
35619     },
35620     
35621     /**
35622      * Returns the panel specified or null if it's not in this region.
35623      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35624      * @return {Roo.ContentPanel}
35625      */
35626     getPanel : function(id){
35627         if(typeof id == "object"){ // must be panel obj
35628             return id;
35629         }
35630         return this.panels.get(id);
35631     },
35632     
35633     /**
35634      * Returns this regions position (north/south/east/west/center).
35635      * @return {String} 
35636      */
35637     getPosition: function(){
35638         return this.position;    
35639     }
35640 });/*
35641  * Based on:
35642  * Ext JS Library 1.1.1
35643  * Copyright(c) 2006-2007, Ext JS, LLC.
35644  *
35645  * Originally Released Under LGPL - original licence link has changed is not relivant.
35646  *
35647  * Fork - LGPL
35648  * <script type="text/javascript">
35649  */
35650  
35651 /**
35652  * @class Roo.bootstrap.layout.Region
35653  * @extends Roo.bootstrap.layout.Basic
35654  * This class represents a region in a layout manager.
35655  
35656  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35657  * @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})
35658  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35659  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35660  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35661  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35662  * @cfg {String}    title           The title for the region (overrides panel titles)
35663  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35664  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35665  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35666  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35667  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35668  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35669  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35670  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35671  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35672  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35673
35674  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35675  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35676  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35677  * @cfg {Number}    width           For East/West panels
35678  * @cfg {Number}    height          For North/South panels
35679  * @cfg {Boolean}   split           To show the splitter
35680  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35681  * 
35682  * @cfg {string}   cls             Extra CSS classes to add to region
35683  * 
35684  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35685  * @cfg {string}   region  the region that it inhabits..
35686  *
35687
35688  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35689  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35690
35691  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35692  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35693  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35694  */
35695 Roo.bootstrap.layout.Region = function(config)
35696 {
35697     this.applyConfig(config);
35698
35699     var mgr = config.mgr;
35700     var pos = config.region;
35701     config.skipConfig = true;
35702     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35703     
35704     if (mgr.el) {
35705         this.onRender(mgr.el);   
35706     }
35707      
35708     this.visible = true;
35709     this.collapsed = false;
35710     this.unrendered_panels = [];
35711 };
35712
35713 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35714
35715     position: '', // set by wrapper (eg. north/south etc..)
35716     unrendered_panels : null,  // unrendered panels.
35717     createBody : function(){
35718         /** This region's body element 
35719         * @type Roo.Element */
35720         this.bodyEl = this.el.createChild({
35721                 tag: "div",
35722                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35723         });
35724     },
35725
35726     onRender: function(ctr, pos)
35727     {
35728         var dh = Roo.DomHelper;
35729         /** This region's container element 
35730         * @type Roo.Element */
35731         this.el = dh.append(ctr.dom, {
35732                 tag: "div",
35733                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35734             }, true);
35735         /** This region's title element 
35736         * @type Roo.Element */
35737     
35738         this.titleEl = dh.append(this.el.dom,
35739             {
35740                     tag: "div",
35741                     unselectable: "on",
35742                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35743                     children:[
35744                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35745                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35746                     ]}, true);
35747         
35748         this.titleEl.enableDisplayMode();
35749         /** This region's title text element 
35750         * @type HTMLElement */
35751         this.titleTextEl = this.titleEl.dom.firstChild;
35752         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35753         /*
35754         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35755         this.closeBtn.enableDisplayMode();
35756         this.closeBtn.on("click", this.closeClicked, this);
35757         this.closeBtn.hide();
35758     */
35759         this.createBody(this.config);
35760         if(this.config.hideWhenEmpty){
35761             this.hide();
35762             this.on("paneladded", this.validateVisibility, this);
35763             this.on("panelremoved", this.validateVisibility, this);
35764         }
35765         if(this.autoScroll){
35766             this.bodyEl.setStyle("overflow", "auto");
35767         }else{
35768             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35769         }
35770         //if(c.titlebar !== false){
35771             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35772                 this.titleEl.hide();
35773             }else{
35774                 this.titleEl.show();
35775                 if(this.config.title){
35776                     this.titleTextEl.innerHTML = this.config.title;
35777                 }
35778             }
35779         //}
35780         if(this.config.collapsed){
35781             this.collapse(true);
35782         }
35783         if(this.config.hidden){
35784             this.hide();
35785         }
35786         
35787         if (this.unrendered_panels && this.unrendered_panels.length) {
35788             for (var i =0;i< this.unrendered_panels.length; i++) {
35789                 this.add(this.unrendered_panels[i]);
35790             }
35791             this.unrendered_panels = null;
35792             
35793         }
35794         
35795     },
35796     
35797     applyConfig : function(c)
35798     {
35799         /*
35800          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35801             var dh = Roo.DomHelper;
35802             if(c.titlebar !== false){
35803                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35804                 this.collapseBtn.on("click", this.collapse, this);
35805                 this.collapseBtn.enableDisplayMode();
35806                 /*
35807                 if(c.showPin === true || this.showPin){
35808                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35809                     this.stickBtn.enableDisplayMode();
35810                     this.stickBtn.on("click", this.expand, this);
35811                     this.stickBtn.hide();
35812                 }
35813                 
35814             }
35815             */
35816             /** This region's collapsed element
35817             * @type Roo.Element */
35818             /*
35819              *
35820             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35821                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35822             ]}, true);
35823             
35824             if(c.floatable !== false){
35825                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35826                this.collapsedEl.on("click", this.collapseClick, this);
35827             }
35828
35829             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35830                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35831                    id: "message", unselectable: "on", style:{"float":"left"}});
35832                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35833              }
35834             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35835             this.expandBtn.on("click", this.expand, this);
35836             
35837         }
35838         
35839         if(this.collapseBtn){
35840             this.collapseBtn.setVisible(c.collapsible == true);
35841         }
35842         
35843         this.cmargins = c.cmargins || this.cmargins ||
35844                          (this.position == "west" || this.position == "east" ?
35845                              {top: 0, left: 2, right:2, bottom: 0} :
35846                              {top: 2, left: 0, right:0, bottom: 2});
35847         */
35848         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35849         
35850         
35851         this.bottomTabs = c.tabPosition != "top";
35852         
35853         this.autoScroll = c.autoScroll || false;
35854         
35855         
35856        
35857         
35858         this.duration = c.duration || .30;
35859         this.slideDuration = c.slideDuration || .45;
35860         this.config = c;
35861        
35862     },
35863     /**
35864      * Returns true if this region is currently visible.
35865      * @return {Boolean}
35866      */
35867     isVisible : function(){
35868         return this.visible;
35869     },
35870
35871     /**
35872      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35873      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35874      */
35875     //setCollapsedTitle : function(title){
35876     //    title = title || "&#160;";
35877      //   if(this.collapsedTitleTextEl){
35878       //      this.collapsedTitleTextEl.innerHTML = title;
35879        // }
35880     //},
35881
35882     getBox : function(){
35883         var b;
35884       //  if(!this.collapsed){
35885             b = this.el.getBox(false, true);
35886        // }else{
35887           //  b = this.collapsedEl.getBox(false, true);
35888         //}
35889         return b;
35890     },
35891
35892     getMargins : function(){
35893         return this.margins;
35894         //return this.collapsed ? this.cmargins : this.margins;
35895     },
35896 /*
35897     highlight : function(){
35898         this.el.addClass("x-layout-panel-dragover");
35899     },
35900
35901     unhighlight : function(){
35902         this.el.removeClass("x-layout-panel-dragover");
35903     },
35904 */
35905     updateBox : function(box)
35906     {
35907         if (!this.bodyEl) {
35908             return; // not rendered yet..
35909         }
35910         
35911         this.box = box;
35912         if(!this.collapsed){
35913             this.el.dom.style.left = box.x + "px";
35914             this.el.dom.style.top = box.y + "px";
35915             this.updateBody(box.width, box.height);
35916         }else{
35917             this.collapsedEl.dom.style.left = box.x + "px";
35918             this.collapsedEl.dom.style.top = box.y + "px";
35919             this.collapsedEl.setSize(box.width, box.height);
35920         }
35921         if(this.tabs){
35922             this.tabs.autoSizeTabs();
35923         }
35924     },
35925
35926     updateBody : function(w, h)
35927     {
35928         if(w !== null){
35929             this.el.setWidth(w);
35930             w -= this.el.getBorderWidth("rl");
35931             if(this.config.adjustments){
35932                 w += this.config.adjustments[0];
35933             }
35934         }
35935         if(h !== null && h > 0){
35936             this.el.setHeight(h);
35937             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35938             h -= this.el.getBorderWidth("tb");
35939             if(this.config.adjustments){
35940                 h += this.config.adjustments[1];
35941             }
35942             this.bodyEl.setHeight(h);
35943             if(this.tabs){
35944                 h = this.tabs.syncHeight(h);
35945             }
35946         }
35947         if(this.panelSize){
35948             w = w !== null ? w : this.panelSize.width;
35949             h = h !== null ? h : this.panelSize.height;
35950         }
35951         if(this.activePanel){
35952             var el = this.activePanel.getEl();
35953             w = w !== null ? w : el.getWidth();
35954             h = h !== null ? h : el.getHeight();
35955             this.panelSize = {width: w, height: h};
35956             this.activePanel.setSize(w, h);
35957         }
35958         if(Roo.isIE && this.tabs){
35959             this.tabs.el.repaint();
35960         }
35961     },
35962
35963     /**
35964      * Returns the container element for this region.
35965      * @return {Roo.Element}
35966      */
35967     getEl : function(){
35968         return this.el;
35969     },
35970
35971     /**
35972      * Hides this region.
35973      */
35974     hide : function(){
35975         //if(!this.collapsed){
35976             this.el.dom.style.left = "-2000px";
35977             this.el.hide();
35978         //}else{
35979          //   this.collapsedEl.dom.style.left = "-2000px";
35980          //   this.collapsedEl.hide();
35981        // }
35982         this.visible = false;
35983         this.fireEvent("visibilitychange", this, false);
35984     },
35985
35986     /**
35987      * Shows this region if it was previously hidden.
35988      */
35989     show : function(){
35990         //if(!this.collapsed){
35991             this.el.show();
35992         //}else{
35993         //    this.collapsedEl.show();
35994        // }
35995         this.visible = true;
35996         this.fireEvent("visibilitychange", this, true);
35997     },
35998 /*
35999     closeClicked : function(){
36000         if(this.activePanel){
36001             this.remove(this.activePanel);
36002         }
36003     },
36004
36005     collapseClick : function(e){
36006         if(this.isSlid){
36007            e.stopPropagation();
36008            this.slideIn();
36009         }else{
36010            e.stopPropagation();
36011            this.slideOut();
36012         }
36013     },
36014 */
36015     /**
36016      * Collapses this region.
36017      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36018      */
36019     /*
36020     collapse : function(skipAnim, skipCheck = false){
36021         if(this.collapsed) {
36022             return;
36023         }
36024         
36025         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36026             
36027             this.collapsed = true;
36028             if(this.split){
36029                 this.split.el.hide();
36030             }
36031             if(this.config.animate && skipAnim !== true){
36032                 this.fireEvent("invalidated", this);
36033                 this.animateCollapse();
36034             }else{
36035                 this.el.setLocation(-20000,-20000);
36036                 this.el.hide();
36037                 this.collapsedEl.show();
36038                 this.fireEvent("collapsed", this);
36039                 this.fireEvent("invalidated", this);
36040             }
36041         }
36042         
36043     },
36044 */
36045     animateCollapse : function(){
36046         // overridden
36047     },
36048
36049     /**
36050      * Expands this region if it was previously collapsed.
36051      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36052      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36053      */
36054     /*
36055     expand : function(e, skipAnim){
36056         if(e) {
36057             e.stopPropagation();
36058         }
36059         if(!this.collapsed || this.el.hasActiveFx()) {
36060             return;
36061         }
36062         if(this.isSlid){
36063             this.afterSlideIn();
36064             skipAnim = true;
36065         }
36066         this.collapsed = false;
36067         if(this.config.animate && skipAnim !== true){
36068             this.animateExpand();
36069         }else{
36070             this.el.show();
36071             if(this.split){
36072                 this.split.el.show();
36073             }
36074             this.collapsedEl.setLocation(-2000,-2000);
36075             this.collapsedEl.hide();
36076             this.fireEvent("invalidated", this);
36077             this.fireEvent("expanded", this);
36078         }
36079     },
36080 */
36081     animateExpand : function(){
36082         // overridden
36083     },
36084
36085     initTabs : function()
36086     {
36087         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36088         
36089         var ts = new Roo.bootstrap.panel.Tabs({
36090                 el: this.bodyEl.dom,
36091                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36092                 disableTooltips: this.config.disableTabTips,
36093                 toolbar : this.config.toolbar
36094             });
36095         
36096         if(this.config.hideTabs){
36097             ts.stripWrap.setDisplayed(false);
36098         }
36099         this.tabs = ts;
36100         ts.resizeTabs = this.config.resizeTabs === true;
36101         ts.minTabWidth = this.config.minTabWidth || 40;
36102         ts.maxTabWidth = this.config.maxTabWidth || 250;
36103         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36104         ts.monitorResize = false;
36105         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36106         ts.bodyEl.addClass('roo-layout-tabs-body');
36107         this.panels.each(this.initPanelAsTab, this);
36108     },
36109
36110     initPanelAsTab : function(panel){
36111         var ti = this.tabs.addTab(
36112             panel.getEl().id,
36113             panel.getTitle(),
36114             null,
36115             this.config.closeOnTab && panel.isClosable(),
36116             panel.tpl
36117         );
36118         if(panel.tabTip !== undefined){
36119             ti.setTooltip(panel.tabTip);
36120         }
36121         ti.on("activate", function(){
36122               this.setActivePanel(panel);
36123         }, this);
36124         
36125         if(this.config.closeOnTab){
36126             ti.on("beforeclose", function(t, e){
36127                 e.cancel = true;
36128                 this.remove(panel);
36129             }, this);
36130         }
36131         
36132         panel.tabItem = ti;
36133         
36134         return ti;
36135     },
36136
36137     updatePanelTitle : function(panel, title)
36138     {
36139         if(this.activePanel == panel){
36140             this.updateTitle(title);
36141         }
36142         if(this.tabs){
36143             var ti = this.tabs.getTab(panel.getEl().id);
36144             ti.setText(title);
36145             if(panel.tabTip !== undefined){
36146                 ti.setTooltip(panel.tabTip);
36147             }
36148         }
36149     },
36150
36151     updateTitle : function(title){
36152         if(this.titleTextEl && !this.config.title){
36153             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36154         }
36155     },
36156
36157     setActivePanel : function(panel)
36158     {
36159         panel = this.getPanel(panel);
36160         if(this.activePanel && this.activePanel != panel){
36161             if(this.activePanel.setActiveState(false) === false){
36162                 return;
36163             }
36164         }
36165         this.activePanel = panel;
36166         panel.setActiveState(true);
36167         if(this.panelSize){
36168             panel.setSize(this.panelSize.width, this.panelSize.height);
36169         }
36170         if(this.closeBtn){
36171             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36172         }
36173         this.updateTitle(panel.getTitle());
36174         if(this.tabs){
36175             this.fireEvent("invalidated", this);
36176         }
36177         this.fireEvent("panelactivated", this, panel);
36178     },
36179
36180     /**
36181      * Shows the specified panel.
36182      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36183      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36184      */
36185     showPanel : function(panel)
36186     {
36187         panel = this.getPanel(panel);
36188         if(panel){
36189             if(this.tabs){
36190                 var tab = this.tabs.getTab(panel.getEl().id);
36191                 if(tab.isHidden()){
36192                     this.tabs.unhideTab(tab.id);
36193                 }
36194                 tab.activate();
36195             }else{
36196                 this.setActivePanel(panel);
36197             }
36198         }
36199         return panel;
36200     },
36201
36202     /**
36203      * Get the active panel for this region.
36204      * @return {Roo.ContentPanel} The active panel or null
36205      */
36206     getActivePanel : function(){
36207         return this.activePanel;
36208     },
36209
36210     validateVisibility : function(){
36211         if(this.panels.getCount() < 1){
36212             this.updateTitle("&#160;");
36213             this.closeBtn.hide();
36214             this.hide();
36215         }else{
36216             if(!this.isVisible()){
36217                 this.show();
36218             }
36219         }
36220     },
36221
36222     /**
36223      * Adds the passed ContentPanel(s) to this region.
36224      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36225      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36226      */
36227     add : function(panel)
36228     {
36229         if(arguments.length > 1){
36230             for(var i = 0, len = arguments.length; i < len; i++) {
36231                 this.add(arguments[i]);
36232             }
36233             return null;
36234         }
36235         
36236         // if we have not been rendered yet, then we can not really do much of this..
36237         if (!this.bodyEl) {
36238             this.unrendered_panels.push(panel);
36239             return panel;
36240         }
36241         
36242         
36243         
36244         
36245         if(this.hasPanel(panel)){
36246             this.showPanel(panel);
36247             return panel;
36248         }
36249         panel.setRegion(this);
36250         this.panels.add(panel);
36251        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36252             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36253             // and hide them... ???
36254             this.bodyEl.dom.appendChild(panel.getEl().dom);
36255             if(panel.background !== true){
36256                 this.setActivePanel(panel);
36257             }
36258             this.fireEvent("paneladded", this, panel);
36259             return panel;
36260         }
36261         */
36262         if(!this.tabs){
36263             this.initTabs();
36264         }else{
36265             this.initPanelAsTab(panel);
36266         }
36267         
36268         
36269         if(panel.background !== true){
36270             this.tabs.activate(panel.getEl().id);
36271         }
36272         this.fireEvent("paneladded", this, panel);
36273         return panel;
36274     },
36275
36276     /**
36277      * Hides the tab for the specified panel.
36278      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36279      */
36280     hidePanel : function(panel){
36281         if(this.tabs && (panel = this.getPanel(panel))){
36282             this.tabs.hideTab(panel.getEl().id);
36283         }
36284     },
36285
36286     /**
36287      * Unhides the tab for a previously hidden panel.
36288      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36289      */
36290     unhidePanel : function(panel){
36291         if(this.tabs && (panel = this.getPanel(panel))){
36292             this.tabs.unhideTab(panel.getEl().id);
36293         }
36294     },
36295
36296     clearPanels : function(){
36297         while(this.panels.getCount() > 0){
36298              this.remove(this.panels.first());
36299         }
36300     },
36301
36302     /**
36303      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36304      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36305      * @param {Boolean} preservePanel Overrides the config preservePanel option
36306      * @return {Roo.ContentPanel} The panel that was removed
36307      */
36308     remove : function(panel, preservePanel)
36309     {
36310         panel = this.getPanel(panel);
36311         if(!panel){
36312             return null;
36313         }
36314         var e = {};
36315         this.fireEvent("beforeremove", this, panel, e);
36316         if(e.cancel === true){
36317             return null;
36318         }
36319         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36320         var panelId = panel.getId();
36321         this.panels.removeKey(panelId);
36322         if(preservePanel){
36323             document.body.appendChild(panel.getEl().dom);
36324         }
36325         if(this.tabs){
36326             this.tabs.removeTab(panel.getEl().id);
36327         }else if (!preservePanel){
36328             this.bodyEl.dom.removeChild(panel.getEl().dom);
36329         }
36330         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36331             var p = this.panels.first();
36332             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36333             tempEl.appendChild(p.getEl().dom);
36334             this.bodyEl.update("");
36335             this.bodyEl.dom.appendChild(p.getEl().dom);
36336             tempEl = null;
36337             this.updateTitle(p.getTitle());
36338             this.tabs = null;
36339             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36340             this.setActivePanel(p);
36341         }
36342         panel.setRegion(null);
36343         if(this.activePanel == panel){
36344             this.activePanel = null;
36345         }
36346         if(this.config.autoDestroy !== false && preservePanel !== true){
36347             try{panel.destroy();}catch(e){}
36348         }
36349         this.fireEvent("panelremoved", this, panel);
36350         return panel;
36351     },
36352
36353     /**
36354      * Returns the TabPanel component used by this region
36355      * @return {Roo.TabPanel}
36356      */
36357     getTabs : function(){
36358         return this.tabs;
36359     },
36360
36361     createTool : function(parentEl, className){
36362         var btn = Roo.DomHelper.append(parentEl, {
36363             tag: "div",
36364             cls: "x-layout-tools-button",
36365             children: [ {
36366                 tag: "div",
36367                 cls: "roo-layout-tools-button-inner " + className,
36368                 html: "&#160;"
36369             }]
36370         }, true);
36371         btn.addClassOnOver("roo-layout-tools-button-over");
36372         return btn;
36373     }
36374 });/*
36375  * Based on:
36376  * Ext JS Library 1.1.1
36377  * Copyright(c) 2006-2007, Ext JS, LLC.
36378  *
36379  * Originally Released Under LGPL - original licence link has changed is not relivant.
36380  *
36381  * Fork - LGPL
36382  * <script type="text/javascript">
36383  */
36384  
36385
36386
36387 /**
36388  * @class Roo.SplitLayoutRegion
36389  * @extends Roo.LayoutRegion
36390  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36391  */
36392 Roo.bootstrap.layout.Split = function(config){
36393     this.cursor = config.cursor;
36394     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36395 };
36396
36397 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36398 {
36399     splitTip : "Drag to resize.",
36400     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36401     useSplitTips : false,
36402
36403     applyConfig : function(config){
36404         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36405     },
36406     
36407     onRender : function(ctr,pos) {
36408         
36409         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36410         if(!this.config.split){
36411             return;
36412         }
36413         if(!this.split){
36414             
36415             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36416                             tag: "div",
36417                             id: this.el.id + "-split",
36418                             cls: "roo-layout-split roo-layout-split-"+this.position,
36419                             html: "&#160;"
36420             });
36421             /** The SplitBar for this region 
36422             * @type Roo.SplitBar */
36423             // does not exist yet...
36424             Roo.log([this.position, this.orientation]);
36425             
36426             this.split = new Roo.bootstrap.SplitBar({
36427                 dragElement : splitEl,
36428                 resizingElement: this.el,
36429                 orientation : this.orientation
36430             });
36431             
36432             this.split.on("moved", this.onSplitMove, this);
36433             this.split.useShim = this.config.useShim === true;
36434             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36435             if(this.useSplitTips){
36436                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36437             }
36438             //if(config.collapsible){
36439             //    this.split.el.on("dblclick", this.collapse,  this);
36440             //}
36441         }
36442         if(typeof this.config.minSize != "undefined"){
36443             this.split.minSize = this.config.minSize;
36444         }
36445         if(typeof this.config.maxSize != "undefined"){
36446             this.split.maxSize = this.config.maxSize;
36447         }
36448         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36449             this.hideSplitter();
36450         }
36451         
36452     },
36453
36454     getHMaxSize : function(){
36455          var cmax = this.config.maxSize || 10000;
36456          var center = this.mgr.getRegion("center");
36457          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36458     },
36459
36460     getVMaxSize : function(){
36461          var cmax = this.config.maxSize || 10000;
36462          var center = this.mgr.getRegion("center");
36463          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36464     },
36465
36466     onSplitMove : function(split, newSize){
36467         this.fireEvent("resized", this, newSize);
36468     },
36469     
36470     /** 
36471      * Returns the {@link Roo.SplitBar} for this region.
36472      * @return {Roo.SplitBar}
36473      */
36474     getSplitBar : function(){
36475         return this.split;
36476     },
36477     
36478     hide : function(){
36479         this.hideSplitter();
36480         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36481     },
36482
36483     hideSplitter : function(){
36484         if(this.split){
36485             this.split.el.setLocation(-2000,-2000);
36486             this.split.el.hide();
36487         }
36488     },
36489
36490     show : function(){
36491         if(this.split){
36492             this.split.el.show();
36493         }
36494         Roo.bootstrap.layout.Split.superclass.show.call(this);
36495     },
36496     
36497     beforeSlide: function(){
36498         if(Roo.isGecko){// firefox overflow auto bug workaround
36499             this.bodyEl.clip();
36500             if(this.tabs) {
36501                 this.tabs.bodyEl.clip();
36502             }
36503             if(this.activePanel){
36504                 this.activePanel.getEl().clip();
36505                 
36506                 if(this.activePanel.beforeSlide){
36507                     this.activePanel.beforeSlide();
36508                 }
36509             }
36510         }
36511     },
36512     
36513     afterSlide : function(){
36514         if(Roo.isGecko){// firefox overflow auto bug workaround
36515             this.bodyEl.unclip();
36516             if(this.tabs) {
36517                 this.tabs.bodyEl.unclip();
36518             }
36519             if(this.activePanel){
36520                 this.activePanel.getEl().unclip();
36521                 if(this.activePanel.afterSlide){
36522                     this.activePanel.afterSlide();
36523                 }
36524             }
36525         }
36526     },
36527
36528     initAutoHide : function(){
36529         if(this.autoHide !== false){
36530             if(!this.autoHideHd){
36531                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36532                 this.autoHideHd = {
36533                     "mouseout": function(e){
36534                         if(!e.within(this.el, true)){
36535                             st.delay(500);
36536                         }
36537                     },
36538                     "mouseover" : function(e){
36539                         st.cancel();
36540                     },
36541                     scope : this
36542                 };
36543             }
36544             this.el.on(this.autoHideHd);
36545         }
36546     },
36547
36548     clearAutoHide : function(){
36549         if(this.autoHide !== false){
36550             this.el.un("mouseout", this.autoHideHd.mouseout);
36551             this.el.un("mouseover", this.autoHideHd.mouseover);
36552         }
36553     },
36554
36555     clearMonitor : function(){
36556         Roo.get(document).un("click", this.slideInIf, this);
36557     },
36558
36559     // these names are backwards but not changed for compat
36560     slideOut : function(){
36561         if(this.isSlid || this.el.hasActiveFx()){
36562             return;
36563         }
36564         this.isSlid = true;
36565         if(this.collapseBtn){
36566             this.collapseBtn.hide();
36567         }
36568         this.closeBtnState = this.closeBtn.getStyle('display');
36569         this.closeBtn.hide();
36570         if(this.stickBtn){
36571             this.stickBtn.show();
36572         }
36573         this.el.show();
36574         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36575         this.beforeSlide();
36576         this.el.setStyle("z-index", 10001);
36577         this.el.slideIn(this.getSlideAnchor(), {
36578             callback: function(){
36579                 this.afterSlide();
36580                 this.initAutoHide();
36581                 Roo.get(document).on("click", this.slideInIf, this);
36582                 this.fireEvent("slideshow", this);
36583             },
36584             scope: this,
36585             block: true
36586         });
36587     },
36588
36589     afterSlideIn : function(){
36590         this.clearAutoHide();
36591         this.isSlid = false;
36592         this.clearMonitor();
36593         this.el.setStyle("z-index", "");
36594         if(this.collapseBtn){
36595             this.collapseBtn.show();
36596         }
36597         this.closeBtn.setStyle('display', this.closeBtnState);
36598         if(this.stickBtn){
36599             this.stickBtn.hide();
36600         }
36601         this.fireEvent("slidehide", this);
36602     },
36603
36604     slideIn : function(cb){
36605         if(!this.isSlid || this.el.hasActiveFx()){
36606             Roo.callback(cb);
36607             return;
36608         }
36609         this.isSlid = false;
36610         this.beforeSlide();
36611         this.el.slideOut(this.getSlideAnchor(), {
36612             callback: function(){
36613                 this.el.setLeftTop(-10000, -10000);
36614                 this.afterSlide();
36615                 this.afterSlideIn();
36616                 Roo.callback(cb);
36617             },
36618             scope: this,
36619             block: true
36620         });
36621     },
36622     
36623     slideInIf : function(e){
36624         if(!e.within(this.el)){
36625             this.slideIn();
36626         }
36627     },
36628
36629     animateCollapse : function(){
36630         this.beforeSlide();
36631         this.el.setStyle("z-index", 20000);
36632         var anchor = this.getSlideAnchor();
36633         this.el.slideOut(anchor, {
36634             callback : function(){
36635                 this.el.setStyle("z-index", "");
36636                 this.collapsedEl.slideIn(anchor, {duration:.3});
36637                 this.afterSlide();
36638                 this.el.setLocation(-10000,-10000);
36639                 this.el.hide();
36640                 this.fireEvent("collapsed", this);
36641             },
36642             scope: this,
36643             block: true
36644         });
36645     },
36646
36647     animateExpand : function(){
36648         this.beforeSlide();
36649         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36650         this.el.setStyle("z-index", 20000);
36651         this.collapsedEl.hide({
36652             duration:.1
36653         });
36654         this.el.slideIn(this.getSlideAnchor(), {
36655             callback : function(){
36656                 this.el.setStyle("z-index", "");
36657                 this.afterSlide();
36658                 if(this.split){
36659                     this.split.el.show();
36660                 }
36661                 this.fireEvent("invalidated", this);
36662                 this.fireEvent("expanded", this);
36663             },
36664             scope: this,
36665             block: true
36666         });
36667     },
36668
36669     anchors : {
36670         "west" : "left",
36671         "east" : "right",
36672         "north" : "top",
36673         "south" : "bottom"
36674     },
36675
36676     sanchors : {
36677         "west" : "l",
36678         "east" : "r",
36679         "north" : "t",
36680         "south" : "b"
36681     },
36682
36683     canchors : {
36684         "west" : "tl-tr",
36685         "east" : "tr-tl",
36686         "north" : "tl-bl",
36687         "south" : "bl-tl"
36688     },
36689
36690     getAnchor : function(){
36691         return this.anchors[this.position];
36692     },
36693
36694     getCollapseAnchor : function(){
36695         return this.canchors[this.position];
36696     },
36697
36698     getSlideAnchor : function(){
36699         return this.sanchors[this.position];
36700     },
36701
36702     getAlignAdj : function(){
36703         var cm = this.cmargins;
36704         switch(this.position){
36705             case "west":
36706                 return [0, 0];
36707             break;
36708             case "east":
36709                 return [0, 0];
36710             break;
36711             case "north":
36712                 return [0, 0];
36713             break;
36714             case "south":
36715                 return [0, 0];
36716             break;
36717         }
36718     },
36719
36720     getExpandAdj : function(){
36721         var c = this.collapsedEl, cm = this.cmargins;
36722         switch(this.position){
36723             case "west":
36724                 return [-(cm.right+c.getWidth()+cm.left), 0];
36725             break;
36726             case "east":
36727                 return [cm.right+c.getWidth()+cm.left, 0];
36728             break;
36729             case "north":
36730                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36731             break;
36732             case "south":
36733                 return [0, cm.top+cm.bottom+c.getHeight()];
36734             break;
36735         }
36736     }
36737 });/*
36738  * Based on:
36739  * Ext JS Library 1.1.1
36740  * Copyright(c) 2006-2007, Ext JS, LLC.
36741  *
36742  * Originally Released Under LGPL - original licence link has changed is not relivant.
36743  *
36744  * Fork - LGPL
36745  * <script type="text/javascript">
36746  */
36747 /*
36748  * These classes are private internal classes
36749  */
36750 Roo.bootstrap.layout.Center = function(config){
36751     config.region = "center";
36752     Roo.bootstrap.layout.Region.call(this, config);
36753     this.visible = true;
36754     this.minWidth = config.minWidth || 20;
36755     this.minHeight = config.minHeight || 20;
36756 };
36757
36758 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36759     hide : function(){
36760         // center panel can't be hidden
36761     },
36762     
36763     show : function(){
36764         // center panel can't be hidden
36765     },
36766     
36767     getMinWidth: function(){
36768         return this.minWidth;
36769     },
36770     
36771     getMinHeight: function(){
36772         return this.minHeight;
36773     }
36774 });
36775
36776
36777
36778
36779  
36780
36781
36782
36783
36784
36785 Roo.bootstrap.layout.North = function(config)
36786 {
36787     config.region = 'north';
36788     config.cursor = 'n-resize';
36789     
36790     Roo.bootstrap.layout.Split.call(this, config);
36791     
36792     
36793     if(this.split){
36794         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36795         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36796         this.split.el.addClass("roo-layout-split-v");
36797     }
36798     var size = config.initialSize || config.height;
36799     if(typeof size != "undefined"){
36800         this.el.setHeight(size);
36801     }
36802 };
36803 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36804 {
36805     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36806     
36807     
36808     
36809     getBox : function(){
36810         if(this.collapsed){
36811             return this.collapsedEl.getBox();
36812         }
36813         var box = this.el.getBox();
36814         if(this.split){
36815             box.height += this.split.el.getHeight();
36816         }
36817         return box;
36818     },
36819     
36820     updateBox : function(box){
36821         if(this.split && !this.collapsed){
36822             box.height -= this.split.el.getHeight();
36823             this.split.el.setLeft(box.x);
36824             this.split.el.setTop(box.y+box.height);
36825             this.split.el.setWidth(box.width);
36826         }
36827         if(this.collapsed){
36828             this.updateBody(box.width, null);
36829         }
36830         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36831     }
36832 });
36833
36834
36835
36836
36837
36838 Roo.bootstrap.layout.South = function(config){
36839     config.region = 'south';
36840     config.cursor = 's-resize';
36841     Roo.bootstrap.layout.Split.call(this, config);
36842     if(this.split){
36843         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36844         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36845         this.split.el.addClass("roo-layout-split-v");
36846     }
36847     var size = config.initialSize || config.height;
36848     if(typeof size != "undefined"){
36849         this.el.setHeight(size);
36850     }
36851 };
36852
36853 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36854     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36855     getBox : function(){
36856         if(this.collapsed){
36857             return this.collapsedEl.getBox();
36858         }
36859         var box = this.el.getBox();
36860         if(this.split){
36861             var sh = this.split.el.getHeight();
36862             box.height += sh;
36863             box.y -= sh;
36864         }
36865         return box;
36866     },
36867     
36868     updateBox : function(box){
36869         if(this.split && !this.collapsed){
36870             var sh = this.split.el.getHeight();
36871             box.height -= sh;
36872             box.y += sh;
36873             this.split.el.setLeft(box.x);
36874             this.split.el.setTop(box.y-sh);
36875             this.split.el.setWidth(box.width);
36876         }
36877         if(this.collapsed){
36878             this.updateBody(box.width, null);
36879         }
36880         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36881     }
36882 });
36883
36884 Roo.bootstrap.layout.East = function(config){
36885     config.region = "east";
36886     config.cursor = "e-resize";
36887     Roo.bootstrap.layout.Split.call(this, config);
36888     if(this.split){
36889         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36890         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36891         this.split.el.addClass("roo-layout-split-h");
36892     }
36893     var size = config.initialSize || config.width;
36894     if(typeof size != "undefined"){
36895         this.el.setWidth(size);
36896     }
36897 };
36898 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36899     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36900     getBox : function(){
36901         if(this.collapsed){
36902             return this.collapsedEl.getBox();
36903         }
36904         var box = this.el.getBox();
36905         if(this.split){
36906             var sw = this.split.el.getWidth();
36907             box.width += sw;
36908             box.x -= sw;
36909         }
36910         return box;
36911     },
36912
36913     updateBox : function(box){
36914         if(this.split && !this.collapsed){
36915             var sw = this.split.el.getWidth();
36916             box.width -= sw;
36917             this.split.el.setLeft(box.x);
36918             this.split.el.setTop(box.y);
36919             this.split.el.setHeight(box.height);
36920             box.x += sw;
36921         }
36922         if(this.collapsed){
36923             this.updateBody(null, box.height);
36924         }
36925         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36926     }
36927 });
36928
36929 Roo.bootstrap.layout.West = function(config){
36930     config.region = "west";
36931     config.cursor = "w-resize";
36932     
36933     Roo.bootstrap.layout.Split.call(this, config);
36934     if(this.split){
36935         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36936         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36937         this.split.el.addClass("roo-layout-split-h");
36938     }
36939     
36940 };
36941 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36942     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36943     
36944     onRender: function(ctr, pos)
36945     {
36946         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36947         var size = this.config.initialSize || this.config.width;
36948         if(typeof size != "undefined"){
36949             this.el.setWidth(size);
36950         }
36951     },
36952     
36953     getBox : function(){
36954         if(this.collapsed){
36955             return this.collapsedEl.getBox();
36956         }
36957         var box = this.el.getBox();
36958         if(this.split){
36959             box.width += this.split.el.getWidth();
36960         }
36961         return box;
36962     },
36963     
36964     updateBox : function(box){
36965         if(this.split && !this.collapsed){
36966             var sw = this.split.el.getWidth();
36967             box.width -= sw;
36968             this.split.el.setLeft(box.x+box.width);
36969             this.split.el.setTop(box.y);
36970             this.split.el.setHeight(box.height);
36971         }
36972         if(this.collapsed){
36973             this.updateBody(null, box.height);
36974         }
36975         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36976     }
36977 });
36978 Roo.namespace("Roo.bootstrap.panel");/*
36979  * Based on:
36980  * Ext JS Library 1.1.1
36981  * Copyright(c) 2006-2007, Ext JS, LLC.
36982  *
36983  * Originally Released Under LGPL - original licence link has changed is not relivant.
36984  *
36985  * Fork - LGPL
36986  * <script type="text/javascript">
36987  */
36988 /**
36989  * @class Roo.ContentPanel
36990  * @extends Roo.util.Observable
36991  * A basic ContentPanel element.
36992  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36993  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36994  * @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
36995  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36996  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36997  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36998  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36999  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37000  * @cfg {String} title          The title for this panel
37001  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37002  * @cfg {String} url            Calls {@link #setUrl} with this value
37003  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37004  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37005  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37006  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37007  * @cfg {Boolean} badges render the badges
37008
37009  * @constructor
37010  * Create a new ContentPanel.
37011  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37012  * @param {String/Object} config A string to set only the title or a config object
37013  * @param {String} content (optional) Set the HTML content for this panel
37014  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37015  */
37016 Roo.bootstrap.panel.Content = function( config){
37017     
37018     this.tpl = config.tpl || false;
37019     
37020     var el = config.el;
37021     var content = config.content;
37022
37023     if(config.autoCreate){ // xtype is available if this is called from factory
37024         el = Roo.id();
37025     }
37026     this.el = Roo.get(el);
37027     if(!this.el && config && config.autoCreate){
37028         if(typeof config.autoCreate == "object"){
37029             if(!config.autoCreate.id){
37030                 config.autoCreate.id = config.id||el;
37031             }
37032             this.el = Roo.DomHelper.append(document.body,
37033                         config.autoCreate, true);
37034         }else{
37035             var elcfg =  {   tag: "div",
37036                             cls: "roo-layout-inactive-content",
37037                             id: config.id||el
37038                             };
37039             if (config.html) {
37040                 elcfg.html = config.html;
37041                 
37042             }
37043                         
37044             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37045         }
37046     } 
37047     this.closable = false;
37048     this.loaded = false;
37049     this.active = false;
37050    
37051       
37052     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37053         
37054         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37055         
37056         this.wrapEl = this.el; //this.el.wrap();
37057         var ti = [];
37058         if (config.toolbar.items) {
37059             ti = config.toolbar.items ;
37060             delete config.toolbar.items ;
37061         }
37062         
37063         var nitems = [];
37064         this.toolbar.render(this.wrapEl, 'before');
37065         for(var i =0;i < ti.length;i++) {
37066           //  Roo.log(['add child', items[i]]);
37067             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37068         }
37069         this.toolbar.items = nitems;
37070         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37071         delete config.toolbar;
37072         
37073     }
37074     /*
37075     // xtype created footer. - not sure if will work as we normally have to render first..
37076     if (this.footer && !this.footer.el && this.footer.xtype) {
37077         if (!this.wrapEl) {
37078             this.wrapEl = this.el.wrap();
37079         }
37080     
37081         this.footer.container = this.wrapEl.createChild();
37082          
37083         this.footer = Roo.factory(this.footer, Roo);
37084         
37085     }
37086     */
37087     
37088      if(typeof config == "string"){
37089         this.title = config;
37090     }else{
37091         Roo.apply(this, config);
37092     }
37093     
37094     if(this.resizeEl){
37095         this.resizeEl = Roo.get(this.resizeEl, true);
37096     }else{
37097         this.resizeEl = this.el;
37098     }
37099     // handle view.xtype
37100     
37101  
37102     
37103     
37104     this.addEvents({
37105         /**
37106          * @event activate
37107          * Fires when this panel is activated. 
37108          * @param {Roo.ContentPanel} this
37109          */
37110         "activate" : true,
37111         /**
37112          * @event deactivate
37113          * Fires when this panel is activated. 
37114          * @param {Roo.ContentPanel} this
37115          */
37116         "deactivate" : true,
37117
37118         /**
37119          * @event resize
37120          * Fires when this panel is resized if fitToFrame is true.
37121          * @param {Roo.ContentPanel} this
37122          * @param {Number} width The width after any component adjustments
37123          * @param {Number} height The height after any component adjustments
37124          */
37125         "resize" : true,
37126         
37127          /**
37128          * @event render
37129          * Fires when this tab is created
37130          * @param {Roo.ContentPanel} this
37131          */
37132         "render" : true
37133         
37134         
37135         
37136     });
37137     
37138
37139     
37140     
37141     if(this.autoScroll){
37142         this.resizeEl.setStyle("overflow", "auto");
37143     } else {
37144         // fix randome scrolling
37145         //this.el.on('scroll', function() {
37146         //    Roo.log('fix random scolling');
37147         //    this.scrollTo('top',0); 
37148         //});
37149     }
37150     content = content || this.content;
37151     if(content){
37152         this.setContent(content);
37153     }
37154     if(config && config.url){
37155         this.setUrl(this.url, this.params, this.loadOnce);
37156     }
37157     
37158     
37159     
37160     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37161     
37162     if (this.view && typeof(this.view.xtype) != 'undefined') {
37163         this.view.el = this.el.appendChild(document.createElement("div"));
37164         this.view = Roo.factory(this.view); 
37165         this.view.render  &&  this.view.render(false, '');  
37166     }
37167     
37168     
37169     this.fireEvent('render', this);
37170 };
37171
37172 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37173     
37174     tabTip : '',
37175     
37176     setRegion : function(region){
37177         this.region = region;
37178         this.setActiveClass(region && !this.background);
37179     },
37180     
37181     
37182     setActiveClass: function(state)
37183     {
37184         if(state){
37185            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37186            this.el.setStyle('position','relative');
37187         }else{
37188            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37189            this.el.setStyle('position', 'absolute');
37190         } 
37191     },
37192     
37193     /**
37194      * Returns the toolbar for this Panel if one was configured. 
37195      * @return {Roo.Toolbar} 
37196      */
37197     getToolbar : function(){
37198         return this.toolbar;
37199     },
37200     
37201     setActiveState : function(active)
37202     {
37203         this.active = active;
37204         this.setActiveClass(active);
37205         if(!active){
37206             if(this.fireEvent("deactivate", this) === false){
37207                 return false;
37208             }
37209             return true;
37210         }
37211         this.fireEvent("activate", this);
37212         return true;
37213     },
37214     /**
37215      * Updates this panel's element
37216      * @param {String} content The new content
37217      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37218     */
37219     setContent : function(content, loadScripts){
37220         this.el.update(content, loadScripts);
37221     },
37222
37223     ignoreResize : function(w, h){
37224         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37225             return true;
37226         }else{
37227             this.lastSize = {width: w, height: h};
37228             return false;
37229         }
37230     },
37231     /**
37232      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37233      * @return {Roo.UpdateManager} The UpdateManager
37234      */
37235     getUpdateManager : function(){
37236         return this.el.getUpdateManager();
37237     },
37238      /**
37239      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37240      * @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:
37241 <pre><code>
37242 panel.load({
37243     url: "your-url.php",
37244     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37245     callback: yourFunction,
37246     scope: yourObject, //(optional scope)
37247     discardUrl: false,
37248     nocache: false,
37249     text: "Loading...",
37250     timeout: 30,
37251     scripts: false
37252 });
37253 </code></pre>
37254      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37255      * 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.
37256      * @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}
37257      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37258      * @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.
37259      * @return {Roo.ContentPanel} this
37260      */
37261     load : function(){
37262         var um = this.el.getUpdateManager();
37263         um.update.apply(um, arguments);
37264         return this;
37265     },
37266
37267
37268     /**
37269      * 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.
37270      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37271      * @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)
37272      * @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)
37273      * @return {Roo.UpdateManager} The UpdateManager
37274      */
37275     setUrl : function(url, params, loadOnce){
37276         if(this.refreshDelegate){
37277             this.removeListener("activate", this.refreshDelegate);
37278         }
37279         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37280         this.on("activate", this.refreshDelegate);
37281         return this.el.getUpdateManager();
37282     },
37283     
37284     _handleRefresh : function(url, params, loadOnce){
37285         if(!loadOnce || !this.loaded){
37286             var updater = this.el.getUpdateManager();
37287             updater.update(url, params, this._setLoaded.createDelegate(this));
37288         }
37289     },
37290     
37291     _setLoaded : function(){
37292         this.loaded = true;
37293     }, 
37294     
37295     /**
37296      * Returns this panel's id
37297      * @return {String} 
37298      */
37299     getId : function(){
37300         return this.el.id;
37301     },
37302     
37303     /** 
37304      * Returns this panel's element - used by regiosn to add.
37305      * @return {Roo.Element} 
37306      */
37307     getEl : function(){
37308         return this.wrapEl || this.el;
37309     },
37310     
37311    
37312     
37313     adjustForComponents : function(width, height)
37314     {
37315         //Roo.log('adjustForComponents ');
37316         if(this.resizeEl != this.el){
37317             width -= this.el.getFrameWidth('lr');
37318             height -= this.el.getFrameWidth('tb');
37319         }
37320         if(this.toolbar){
37321             var te = this.toolbar.getEl();
37322             te.setWidth(width);
37323             height -= te.getHeight();
37324         }
37325         if(this.footer){
37326             var te = this.footer.getEl();
37327             te.setWidth(width);
37328             height -= te.getHeight();
37329         }
37330         
37331         
37332         if(this.adjustments){
37333             width += this.adjustments[0];
37334             height += this.adjustments[1];
37335         }
37336         return {"width": width, "height": height};
37337     },
37338     
37339     setSize : function(width, height){
37340         if(this.fitToFrame && !this.ignoreResize(width, height)){
37341             if(this.fitContainer && this.resizeEl != this.el){
37342                 this.el.setSize(width, height);
37343             }
37344             var size = this.adjustForComponents(width, height);
37345             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37346             this.fireEvent('resize', this, size.width, size.height);
37347         }
37348     },
37349     
37350     /**
37351      * Returns this panel's title
37352      * @return {String} 
37353      */
37354     getTitle : function(){
37355         
37356         if (typeof(this.title) != 'object') {
37357             return this.title;
37358         }
37359         
37360         var t = '';
37361         for (var k in this.title) {
37362             if (!this.title.hasOwnProperty(k)) {
37363                 continue;
37364             }
37365             
37366             if (k.indexOf('-') >= 0) {
37367                 var s = k.split('-');
37368                 for (var i = 0; i<s.length; i++) {
37369                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37370                 }
37371             } else {
37372                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37373             }
37374         }
37375         return t;
37376     },
37377     
37378     /**
37379      * Set this panel's title
37380      * @param {String} title
37381      */
37382     setTitle : function(title){
37383         this.title = title;
37384         if(this.region){
37385             this.region.updatePanelTitle(this, title);
37386         }
37387     },
37388     
37389     /**
37390      * Returns true is this panel was configured to be closable
37391      * @return {Boolean} 
37392      */
37393     isClosable : function(){
37394         return this.closable;
37395     },
37396     
37397     beforeSlide : function(){
37398         this.el.clip();
37399         this.resizeEl.clip();
37400     },
37401     
37402     afterSlide : function(){
37403         this.el.unclip();
37404         this.resizeEl.unclip();
37405     },
37406     
37407     /**
37408      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37409      *   Will fail silently if the {@link #setUrl} method has not been called.
37410      *   This does not activate the panel, just updates its content.
37411      */
37412     refresh : function(){
37413         if(this.refreshDelegate){
37414            this.loaded = false;
37415            this.refreshDelegate();
37416         }
37417     },
37418     
37419     /**
37420      * Destroys this panel
37421      */
37422     destroy : function(){
37423         this.el.removeAllListeners();
37424         var tempEl = document.createElement("span");
37425         tempEl.appendChild(this.el.dom);
37426         tempEl.innerHTML = "";
37427         this.el.remove();
37428         this.el = null;
37429     },
37430     
37431     /**
37432      * form - if the content panel contains a form - this is a reference to it.
37433      * @type {Roo.form.Form}
37434      */
37435     form : false,
37436     /**
37437      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37438      *    This contains a reference to it.
37439      * @type {Roo.View}
37440      */
37441     view : false,
37442     
37443       /**
37444      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37445      * <pre><code>
37446
37447 layout.addxtype({
37448        xtype : 'Form',
37449        items: [ .... ]
37450    }
37451 );
37452
37453 </code></pre>
37454      * @param {Object} cfg Xtype definition of item to add.
37455      */
37456     
37457     
37458     getChildContainer: function () {
37459         return this.getEl();
37460     }
37461     
37462     
37463     /*
37464         var  ret = new Roo.factory(cfg);
37465         return ret;
37466         
37467         
37468         // add form..
37469         if (cfg.xtype.match(/^Form$/)) {
37470             
37471             var el;
37472             //if (this.footer) {
37473             //    el = this.footer.container.insertSibling(false, 'before');
37474             //} else {
37475                 el = this.el.createChild();
37476             //}
37477
37478             this.form = new  Roo.form.Form(cfg);
37479             
37480             
37481             if ( this.form.allItems.length) {
37482                 this.form.render(el.dom);
37483             }
37484             return this.form;
37485         }
37486         // should only have one of theses..
37487         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37488             // views.. should not be just added - used named prop 'view''
37489             
37490             cfg.el = this.el.appendChild(document.createElement("div"));
37491             // factory?
37492             
37493             var ret = new Roo.factory(cfg);
37494              
37495              ret.render && ret.render(false, ''); // render blank..
37496             this.view = ret;
37497             return ret;
37498         }
37499         return false;
37500     }
37501     \*/
37502 });
37503  
37504 /**
37505  * @class Roo.bootstrap.panel.Grid
37506  * @extends Roo.bootstrap.panel.Content
37507  * @constructor
37508  * Create a new GridPanel.
37509  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37510  * @param {Object} config A the config object
37511   
37512  */
37513
37514
37515
37516 Roo.bootstrap.panel.Grid = function(config)
37517 {
37518     
37519       
37520     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37521         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37522
37523     config.el = this.wrapper;
37524     //this.el = this.wrapper;
37525     
37526       if (config.container) {
37527         // ctor'ed from a Border/panel.grid
37528         
37529         
37530         this.wrapper.setStyle("overflow", "hidden");
37531         this.wrapper.addClass('roo-grid-container');
37532
37533     }
37534     
37535     
37536     if(config.toolbar){
37537         var tool_el = this.wrapper.createChild();    
37538         this.toolbar = Roo.factory(config.toolbar);
37539         var ti = [];
37540         if (config.toolbar.items) {
37541             ti = config.toolbar.items ;
37542             delete config.toolbar.items ;
37543         }
37544         
37545         var nitems = [];
37546         this.toolbar.render(tool_el);
37547         for(var i =0;i < ti.length;i++) {
37548           //  Roo.log(['add child', items[i]]);
37549             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37550         }
37551         this.toolbar.items = nitems;
37552         
37553         delete config.toolbar;
37554     }
37555     
37556     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37557     config.grid.scrollBody = true;;
37558     config.grid.monitorWindowResize = false; // turn off autosizing
37559     config.grid.autoHeight = false;
37560     config.grid.autoWidth = false;
37561     
37562     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37563     
37564     if (config.background) {
37565         // render grid on panel activation (if panel background)
37566         this.on('activate', function(gp) {
37567             if (!gp.grid.rendered) {
37568                 gp.grid.render(this.wrapper);
37569                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37570             }
37571         });
37572             
37573     } else {
37574         this.grid.render(this.wrapper);
37575         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37576
37577     }
37578     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37579     // ??? needed ??? config.el = this.wrapper;
37580     
37581     
37582     
37583   
37584     // xtype created footer. - not sure if will work as we normally have to render first..
37585     if (this.footer && !this.footer.el && this.footer.xtype) {
37586         
37587         var ctr = this.grid.getView().getFooterPanel(true);
37588         this.footer.dataSource = this.grid.dataSource;
37589         this.footer = Roo.factory(this.footer, Roo);
37590         this.footer.render(ctr);
37591         
37592     }
37593     
37594     
37595     
37596     
37597      
37598 };
37599
37600 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37601     getId : function(){
37602         return this.grid.id;
37603     },
37604     
37605     /**
37606      * Returns the grid for this panel
37607      * @return {Roo.bootstrap.Table} 
37608      */
37609     getGrid : function(){
37610         return this.grid;    
37611     },
37612     
37613     setSize : function(width, height){
37614         if(!this.ignoreResize(width, height)){
37615             var grid = this.grid;
37616             var size = this.adjustForComponents(width, height);
37617             var gridel = grid.getGridEl();
37618             gridel.setSize(size.width, size.height);
37619             /*
37620             var thd = grid.getGridEl().select('thead',true).first();
37621             var tbd = grid.getGridEl().select('tbody', true).first();
37622             if (tbd) {
37623                 tbd.setSize(width, height - thd.getHeight());
37624             }
37625             */
37626             grid.autoSize();
37627         }
37628     },
37629      
37630     
37631     
37632     beforeSlide : function(){
37633         this.grid.getView().scroller.clip();
37634     },
37635     
37636     afterSlide : function(){
37637         this.grid.getView().scroller.unclip();
37638     },
37639     
37640     destroy : function(){
37641         this.grid.destroy();
37642         delete this.grid;
37643         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37644     }
37645 });
37646
37647 /**
37648  * @class Roo.bootstrap.panel.Nest
37649  * @extends Roo.bootstrap.panel.Content
37650  * @constructor
37651  * Create a new Panel, that can contain a layout.Border.
37652  * 
37653  * 
37654  * @param {Roo.BorderLayout} layout The layout for this panel
37655  * @param {String/Object} config A string to set only the title or a config object
37656  */
37657 Roo.bootstrap.panel.Nest = function(config)
37658 {
37659     // construct with only one argument..
37660     /* FIXME - implement nicer consturctors
37661     if (layout.layout) {
37662         config = layout;
37663         layout = config.layout;
37664         delete config.layout;
37665     }
37666     if (layout.xtype && !layout.getEl) {
37667         // then layout needs constructing..
37668         layout = Roo.factory(layout, Roo);
37669     }
37670     */
37671     
37672     config.el =  config.layout.getEl();
37673     
37674     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37675     
37676     config.layout.monitorWindowResize = false; // turn off autosizing
37677     this.layout = config.layout;
37678     this.layout.getEl().addClass("roo-layout-nested-layout");
37679     
37680     
37681     
37682     
37683 };
37684
37685 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37686
37687     setSize : function(width, height){
37688         if(!this.ignoreResize(width, height)){
37689             var size = this.adjustForComponents(width, height);
37690             var el = this.layout.getEl();
37691             if (size.height < 1) {
37692                 el.setWidth(size.width);   
37693             } else {
37694                 el.setSize(size.width, size.height);
37695             }
37696             var touch = el.dom.offsetWidth;
37697             this.layout.layout();
37698             // ie requires a double layout on the first pass
37699             if(Roo.isIE && !this.initialized){
37700                 this.initialized = true;
37701                 this.layout.layout();
37702             }
37703         }
37704     },
37705     
37706     // activate all subpanels if not currently active..
37707     
37708     setActiveState : function(active){
37709         this.active = active;
37710         this.setActiveClass(active);
37711         
37712         if(!active){
37713             this.fireEvent("deactivate", this);
37714             return;
37715         }
37716         
37717         this.fireEvent("activate", this);
37718         // not sure if this should happen before or after..
37719         if (!this.layout) {
37720             return; // should not happen..
37721         }
37722         var reg = false;
37723         for (var r in this.layout.regions) {
37724             reg = this.layout.getRegion(r);
37725             if (reg.getActivePanel()) {
37726                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37727                 reg.setActivePanel(reg.getActivePanel());
37728                 continue;
37729             }
37730             if (!reg.panels.length) {
37731                 continue;
37732             }
37733             reg.showPanel(reg.getPanel(0));
37734         }
37735         
37736         
37737         
37738         
37739     },
37740     
37741     /**
37742      * Returns the nested BorderLayout for this panel
37743      * @return {Roo.BorderLayout} 
37744      */
37745     getLayout : function(){
37746         return this.layout;
37747     },
37748     
37749      /**
37750      * Adds a xtype elements to the layout of the nested panel
37751      * <pre><code>
37752
37753 panel.addxtype({
37754        xtype : 'ContentPanel',
37755        region: 'west',
37756        items: [ .... ]
37757    }
37758 );
37759
37760 panel.addxtype({
37761         xtype : 'NestedLayoutPanel',
37762         region: 'west',
37763         layout: {
37764            center: { },
37765            west: { }   
37766         },
37767         items : [ ... list of content panels or nested layout panels.. ]
37768    }
37769 );
37770 </code></pre>
37771      * @param {Object} cfg Xtype definition of item to add.
37772      */
37773     addxtype : function(cfg) {
37774         return this.layout.addxtype(cfg);
37775     
37776     }
37777 });        /*
37778  * Based on:
37779  * Ext JS Library 1.1.1
37780  * Copyright(c) 2006-2007, Ext JS, LLC.
37781  *
37782  * Originally Released Under LGPL - original licence link has changed is not relivant.
37783  *
37784  * Fork - LGPL
37785  * <script type="text/javascript">
37786  */
37787 /**
37788  * @class Roo.TabPanel
37789  * @extends Roo.util.Observable
37790  * A lightweight tab container.
37791  * <br><br>
37792  * Usage:
37793  * <pre><code>
37794 // basic tabs 1, built from existing content
37795 var tabs = new Roo.TabPanel("tabs1");
37796 tabs.addTab("script", "View Script");
37797 tabs.addTab("markup", "View Markup");
37798 tabs.activate("script");
37799
37800 // more advanced tabs, built from javascript
37801 var jtabs = new Roo.TabPanel("jtabs");
37802 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37803
37804 // set up the UpdateManager
37805 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37806 var updater = tab2.getUpdateManager();
37807 updater.setDefaultUrl("ajax1.htm");
37808 tab2.on('activate', updater.refresh, updater, true);
37809
37810 // Use setUrl for Ajax loading
37811 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37812 tab3.setUrl("ajax2.htm", null, true);
37813
37814 // Disabled tab
37815 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37816 tab4.disable();
37817
37818 jtabs.activate("jtabs-1");
37819  * </code></pre>
37820  * @constructor
37821  * Create a new TabPanel.
37822  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37823  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37824  */
37825 Roo.bootstrap.panel.Tabs = function(config){
37826     /**
37827     * The container element for this TabPanel.
37828     * @type Roo.Element
37829     */
37830     this.el = Roo.get(config.el);
37831     delete config.el;
37832     if(config){
37833         if(typeof config == "boolean"){
37834             this.tabPosition = config ? "bottom" : "top";
37835         }else{
37836             Roo.apply(this, config);
37837         }
37838     }
37839     
37840     if(this.tabPosition == "bottom"){
37841         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37842         this.el.addClass("roo-tabs-bottom");
37843     }
37844     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37845     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37846     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37847     if(Roo.isIE){
37848         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37849     }
37850     if(this.tabPosition != "bottom"){
37851         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37852          * @type Roo.Element
37853          */
37854         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37855         this.el.addClass("roo-tabs-top");
37856     }
37857     this.items = [];
37858
37859     this.bodyEl.setStyle("position", "relative");
37860
37861     this.active = null;
37862     this.activateDelegate = this.activate.createDelegate(this);
37863
37864     this.addEvents({
37865         /**
37866          * @event tabchange
37867          * Fires when the active tab changes
37868          * @param {Roo.TabPanel} this
37869          * @param {Roo.TabPanelItem} activePanel The new active tab
37870          */
37871         "tabchange": true,
37872         /**
37873          * @event beforetabchange
37874          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37875          * @param {Roo.TabPanel} this
37876          * @param {Object} e Set cancel to true on this object to cancel the tab change
37877          * @param {Roo.TabPanelItem} tab The tab being changed to
37878          */
37879         "beforetabchange" : true
37880     });
37881
37882     Roo.EventManager.onWindowResize(this.onResize, this);
37883     this.cpad = this.el.getPadding("lr");
37884     this.hiddenCount = 0;
37885
37886
37887     // toolbar on the tabbar support...
37888     if (this.toolbar) {
37889         alert("no toolbar support yet");
37890         this.toolbar  = false;
37891         /*
37892         var tcfg = this.toolbar;
37893         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37894         this.toolbar = new Roo.Toolbar(tcfg);
37895         if (Roo.isSafari) {
37896             var tbl = tcfg.container.child('table', true);
37897             tbl.setAttribute('width', '100%');
37898         }
37899         */
37900         
37901     }
37902    
37903
37904
37905     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37906 };
37907
37908 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37909     /*
37910      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37911      */
37912     tabPosition : "top",
37913     /*
37914      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37915      */
37916     currentTabWidth : 0,
37917     /*
37918      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37919      */
37920     minTabWidth : 40,
37921     /*
37922      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37923      */
37924     maxTabWidth : 250,
37925     /*
37926      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37927      */
37928     preferredTabWidth : 175,
37929     /*
37930      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37931      */
37932     resizeTabs : false,
37933     /*
37934      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37935      */
37936     monitorResize : true,
37937     /*
37938      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37939      */
37940     toolbar : false,
37941
37942     /**
37943      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37944      * @param {String} id The id of the div to use <b>or create</b>
37945      * @param {String} text The text for the tab
37946      * @param {String} content (optional) Content to put in the TabPanelItem body
37947      * @param {Boolean} closable (optional) True to create a close icon on the tab
37948      * @return {Roo.TabPanelItem} The created TabPanelItem
37949      */
37950     addTab : function(id, text, content, closable, tpl)
37951     {
37952         var item = new Roo.bootstrap.panel.TabItem({
37953             panel: this,
37954             id : id,
37955             text : text,
37956             closable : closable,
37957             tpl : tpl
37958         });
37959         this.addTabItem(item);
37960         if(content){
37961             item.setContent(content);
37962         }
37963         return item;
37964     },
37965
37966     /**
37967      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37968      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37969      * @return {Roo.TabPanelItem}
37970      */
37971     getTab : function(id){
37972         return this.items[id];
37973     },
37974
37975     /**
37976      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37977      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37978      */
37979     hideTab : function(id){
37980         var t = this.items[id];
37981         if(!t.isHidden()){
37982            t.setHidden(true);
37983            this.hiddenCount++;
37984            this.autoSizeTabs();
37985         }
37986     },
37987
37988     /**
37989      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37990      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37991      */
37992     unhideTab : function(id){
37993         var t = this.items[id];
37994         if(t.isHidden()){
37995            t.setHidden(false);
37996            this.hiddenCount--;
37997            this.autoSizeTabs();
37998         }
37999     },
38000
38001     /**
38002      * Adds an existing {@link Roo.TabPanelItem}.
38003      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38004      */
38005     addTabItem : function(item){
38006         this.items[item.id] = item;
38007         this.items.push(item);
38008       //  if(this.resizeTabs){
38009     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38010   //         this.autoSizeTabs();
38011 //        }else{
38012 //            item.autoSize();
38013        // }
38014     },
38015
38016     /**
38017      * Removes a {@link Roo.TabPanelItem}.
38018      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38019      */
38020     removeTab : function(id){
38021         var items = this.items;
38022         var tab = items[id];
38023         if(!tab) { return; }
38024         var index = items.indexOf(tab);
38025         if(this.active == tab && items.length > 1){
38026             var newTab = this.getNextAvailable(index);
38027             if(newTab) {
38028                 newTab.activate();
38029             }
38030         }
38031         this.stripEl.dom.removeChild(tab.pnode.dom);
38032         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38033             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38034         }
38035         items.splice(index, 1);
38036         delete this.items[tab.id];
38037         tab.fireEvent("close", tab);
38038         tab.purgeListeners();
38039         this.autoSizeTabs();
38040     },
38041
38042     getNextAvailable : function(start){
38043         var items = this.items;
38044         var index = start;
38045         // look for a next tab that will slide over to
38046         // replace the one being removed
38047         while(index < items.length){
38048             var item = items[++index];
38049             if(item && !item.isHidden()){
38050                 return item;
38051             }
38052         }
38053         // if one isn't found select the previous tab (on the left)
38054         index = start;
38055         while(index >= 0){
38056             var item = items[--index];
38057             if(item && !item.isHidden()){
38058                 return item;
38059             }
38060         }
38061         return null;
38062     },
38063
38064     /**
38065      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38066      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38067      */
38068     disableTab : function(id){
38069         var tab = this.items[id];
38070         if(tab && this.active != tab){
38071             tab.disable();
38072         }
38073     },
38074
38075     /**
38076      * Enables a {@link Roo.TabPanelItem} that is disabled.
38077      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38078      */
38079     enableTab : function(id){
38080         var tab = this.items[id];
38081         tab.enable();
38082     },
38083
38084     /**
38085      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38086      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38087      * @return {Roo.TabPanelItem} The TabPanelItem.
38088      */
38089     activate : function(id){
38090         var tab = this.items[id];
38091         if(!tab){
38092             return null;
38093         }
38094         if(tab == this.active || tab.disabled){
38095             return tab;
38096         }
38097         var e = {};
38098         this.fireEvent("beforetabchange", this, e, tab);
38099         if(e.cancel !== true && !tab.disabled){
38100             if(this.active){
38101                 this.active.hide();
38102             }
38103             this.active = this.items[id];
38104             this.active.show();
38105             this.fireEvent("tabchange", this, this.active);
38106         }
38107         return tab;
38108     },
38109
38110     /**
38111      * Gets the active {@link Roo.TabPanelItem}.
38112      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38113      */
38114     getActiveTab : function(){
38115         return this.active;
38116     },
38117
38118     /**
38119      * Updates the tab body element to fit the height of the container element
38120      * for overflow scrolling
38121      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38122      */
38123     syncHeight : function(targetHeight){
38124         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38125         var bm = this.bodyEl.getMargins();
38126         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38127         this.bodyEl.setHeight(newHeight);
38128         return newHeight;
38129     },
38130
38131     onResize : function(){
38132         if(this.monitorResize){
38133             this.autoSizeTabs();
38134         }
38135     },
38136
38137     /**
38138      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38139      */
38140     beginUpdate : function(){
38141         this.updating = true;
38142     },
38143
38144     /**
38145      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38146      */
38147     endUpdate : function(){
38148         this.updating = false;
38149         this.autoSizeTabs();
38150     },
38151
38152     /**
38153      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38154      */
38155     autoSizeTabs : function(){
38156         var count = this.items.length;
38157         var vcount = count - this.hiddenCount;
38158         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38159             return;
38160         }
38161         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38162         var availWidth = Math.floor(w / vcount);
38163         var b = this.stripBody;
38164         if(b.getWidth() > w){
38165             var tabs = this.items;
38166             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38167             if(availWidth < this.minTabWidth){
38168                 /*if(!this.sleft){    // incomplete scrolling code
38169                     this.createScrollButtons();
38170                 }
38171                 this.showScroll();
38172                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38173             }
38174         }else{
38175             if(this.currentTabWidth < this.preferredTabWidth){
38176                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38177             }
38178         }
38179     },
38180
38181     /**
38182      * Returns the number of tabs in this TabPanel.
38183      * @return {Number}
38184      */
38185      getCount : function(){
38186          return this.items.length;
38187      },
38188
38189     /**
38190      * Resizes all the tabs to the passed width
38191      * @param {Number} The new width
38192      */
38193     setTabWidth : function(width){
38194         this.currentTabWidth = width;
38195         for(var i = 0, len = this.items.length; i < len; i++) {
38196                 if(!this.items[i].isHidden()) {
38197                 this.items[i].setWidth(width);
38198             }
38199         }
38200     },
38201
38202     /**
38203      * Destroys this TabPanel
38204      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38205      */
38206     destroy : function(removeEl){
38207         Roo.EventManager.removeResizeListener(this.onResize, this);
38208         for(var i = 0, len = this.items.length; i < len; i++){
38209             this.items[i].purgeListeners();
38210         }
38211         if(removeEl === true){
38212             this.el.update("");
38213             this.el.remove();
38214         }
38215     },
38216     
38217     createStrip : function(container)
38218     {
38219         var strip = document.createElement("nav");
38220         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38221         container.appendChild(strip);
38222         return strip;
38223     },
38224     
38225     createStripList : function(strip)
38226     {
38227         // div wrapper for retard IE
38228         // returns the "tr" element.
38229         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38230         //'<div class="x-tabs-strip-wrap">'+
38231           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38232           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38233         return strip.firstChild; //.firstChild.firstChild.firstChild;
38234     },
38235     createBody : function(container)
38236     {
38237         var body = document.createElement("div");
38238         Roo.id(body, "tab-body");
38239         //Roo.fly(body).addClass("x-tabs-body");
38240         Roo.fly(body).addClass("tab-content");
38241         container.appendChild(body);
38242         return body;
38243     },
38244     createItemBody :function(bodyEl, id){
38245         var body = Roo.getDom(id);
38246         if(!body){
38247             body = document.createElement("div");
38248             body.id = id;
38249         }
38250         //Roo.fly(body).addClass("x-tabs-item-body");
38251         Roo.fly(body).addClass("tab-pane");
38252          bodyEl.insertBefore(body, bodyEl.firstChild);
38253         return body;
38254     },
38255     /** @private */
38256     createStripElements :  function(stripEl, text, closable, tpl)
38257     {
38258         var td = document.createElement("li"); // was td..
38259         
38260         
38261         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38262         
38263         
38264         stripEl.appendChild(td);
38265         /*if(closable){
38266             td.className = "x-tabs-closable";
38267             if(!this.closeTpl){
38268                 this.closeTpl = new Roo.Template(
38269                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38270                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38271                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38272                 );
38273             }
38274             var el = this.closeTpl.overwrite(td, {"text": text});
38275             var close = el.getElementsByTagName("div")[0];
38276             var inner = el.getElementsByTagName("em")[0];
38277             return {"el": el, "close": close, "inner": inner};
38278         } else {
38279         */
38280         // not sure what this is..
38281 //            if(!this.tabTpl){
38282                 //this.tabTpl = new Roo.Template(
38283                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38284                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38285                 //);
38286 //                this.tabTpl = new Roo.Template(
38287 //                   '<a href="#">' +
38288 //                   '<span unselectable="on"' +
38289 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38290 //                            ' >{text}</span></a>'
38291 //                );
38292 //                
38293 //            }
38294
38295
38296             var template = tpl || this.tabTpl || false;
38297             
38298             if(!template){
38299                 
38300                 template = new Roo.Template(
38301                    '<a href="#">' +
38302                    '<span unselectable="on"' +
38303                             (this.disableTooltips ? '' : ' title="{text}"') +
38304                             ' >{text}</span></a>'
38305                 );
38306             }
38307             
38308             switch (typeof(template)) {
38309                 case 'object' :
38310                     break;
38311                 case 'string' :
38312                     template = new Roo.Template(template);
38313                     break;
38314                 default :
38315                     break;
38316             }
38317             
38318             var el = template.overwrite(td, {"text": text});
38319             
38320             var inner = el.getElementsByTagName("span")[0];
38321             
38322             return {"el": el, "inner": inner};
38323             
38324     }
38325         
38326     
38327 });
38328
38329 /**
38330  * @class Roo.TabPanelItem
38331  * @extends Roo.util.Observable
38332  * Represents an individual item (tab plus body) in a TabPanel.
38333  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38334  * @param {String} id The id of this TabPanelItem
38335  * @param {String} text The text for the tab of this TabPanelItem
38336  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38337  */
38338 Roo.bootstrap.panel.TabItem = function(config){
38339     /**
38340      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38341      * @type Roo.TabPanel
38342      */
38343     this.tabPanel = config.panel;
38344     /**
38345      * The id for this TabPanelItem
38346      * @type String
38347      */
38348     this.id = config.id;
38349     /** @private */
38350     this.disabled = false;
38351     /** @private */
38352     this.text = config.text;
38353     /** @private */
38354     this.loaded = false;
38355     this.closable = config.closable;
38356
38357     /**
38358      * The body element for this TabPanelItem.
38359      * @type Roo.Element
38360      */
38361     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38362     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38363     this.bodyEl.setStyle("display", "block");
38364     this.bodyEl.setStyle("zoom", "1");
38365     //this.hideAction();
38366
38367     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38368     /** @private */
38369     this.el = Roo.get(els.el);
38370     this.inner = Roo.get(els.inner, true);
38371     this.textEl = Roo.get(this.el.dom.firstChild, true);
38372     this.pnode = Roo.get(els.el.parentNode, true);
38373 //    this.el.on("mousedown", this.onTabMouseDown, this);
38374     this.el.on("click", this.onTabClick, this);
38375     /** @private */
38376     if(config.closable){
38377         var c = Roo.get(els.close, true);
38378         c.dom.title = this.closeText;
38379         c.addClassOnOver("close-over");
38380         c.on("click", this.closeClick, this);
38381      }
38382
38383     this.addEvents({
38384          /**
38385          * @event activate
38386          * Fires when this tab becomes the active tab.
38387          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38388          * @param {Roo.TabPanelItem} this
38389          */
38390         "activate": true,
38391         /**
38392          * @event beforeclose
38393          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38394          * @param {Roo.TabPanelItem} this
38395          * @param {Object} e Set cancel to true on this object to cancel the close.
38396          */
38397         "beforeclose": true,
38398         /**
38399          * @event close
38400          * Fires when this tab is closed.
38401          * @param {Roo.TabPanelItem} this
38402          */
38403          "close": true,
38404         /**
38405          * @event deactivate
38406          * Fires when this tab is no longer the active tab.
38407          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38408          * @param {Roo.TabPanelItem} this
38409          */
38410          "deactivate" : true
38411     });
38412     this.hidden = false;
38413
38414     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38415 };
38416
38417 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38418            {
38419     purgeListeners : function(){
38420        Roo.util.Observable.prototype.purgeListeners.call(this);
38421        this.el.removeAllListeners();
38422     },
38423     /**
38424      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38425      */
38426     show : function(){
38427         this.pnode.addClass("active");
38428         this.showAction();
38429         if(Roo.isOpera){
38430             this.tabPanel.stripWrap.repaint();
38431         }
38432         this.fireEvent("activate", this.tabPanel, this);
38433     },
38434
38435     /**
38436      * Returns true if this tab is the active tab.
38437      * @return {Boolean}
38438      */
38439     isActive : function(){
38440         return this.tabPanel.getActiveTab() == this;
38441     },
38442
38443     /**
38444      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38445      */
38446     hide : function(){
38447         this.pnode.removeClass("active");
38448         this.hideAction();
38449         this.fireEvent("deactivate", this.tabPanel, this);
38450     },
38451
38452     hideAction : function(){
38453         this.bodyEl.hide();
38454         this.bodyEl.setStyle("position", "absolute");
38455         this.bodyEl.setLeft("-20000px");
38456         this.bodyEl.setTop("-20000px");
38457     },
38458
38459     showAction : function(){
38460         this.bodyEl.setStyle("position", "relative");
38461         this.bodyEl.setTop("");
38462         this.bodyEl.setLeft("");
38463         this.bodyEl.show();
38464     },
38465
38466     /**
38467      * Set the tooltip for the tab.
38468      * @param {String} tooltip The tab's tooltip
38469      */
38470     setTooltip : function(text){
38471         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38472             this.textEl.dom.qtip = text;
38473             this.textEl.dom.removeAttribute('title');
38474         }else{
38475             this.textEl.dom.title = text;
38476         }
38477     },
38478
38479     onTabClick : function(e){
38480         e.preventDefault();
38481         this.tabPanel.activate(this.id);
38482     },
38483
38484     onTabMouseDown : function(e){
38485         e.preventDefault();
38486         this.tabPanel.activate(this.id);
38487     },
38488 /*
38489     getWidth : function(){
38490         return this.inner.getWidth();
38491     },
38492
38493     setWidth : function(width){
38494         var iwidth = width - this.pnode.getPadding("lr");
38495         this.inner.setWidth(iwidth);
38496         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38497         this.pnode.setWidth(width);
38498     },
38499 */
38500     /**
38501      * Show or hide the tab
38502      * @param {Boolean} hidden True to hide or false to show.
38503      */
38504     setHidden : function(hidden){
38505         this.hidden = hidden;
38506         this.pnode.setStyle("display", hidden ? "none" : "");
38507     },
38508
38509     /**
38510      * Returns true if this tab is "hidden"
38511      * @return {Boolean}
38512      */
38513     isHidden : function(){
38514         return this.hidden;
38515     },
38516
38517     /**
38518      * Returns the text for this tab
38519      * @return {String}
38520      */
38521     getText : function(){
38522         return this.text;
38523     },
38524     /*
38525     autoSize : function(){
38526         //this.el.beginMeasure();
38527         this.textEl.setWidth(1);
38528         /*
38529          *  #2804 [new] Tabs in Roojs
38530          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38531          */
38532         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38533         //this.el.endMeasure();
38534     //},
38535
38536     /**
38537      * Sets the text for the tab (Note: this also sets the tooltip text)
38538      * @param {String} text The tab's text and tooltip
38539      */
38540     setText : function(text){
38541         this.text = text;
38542         this.textEl.update(text);
38543         this.setTooltip(text);
38544         //if(!this.tabPanel.resizeTabs){
38545         //    this.autoSize();
38546         //}
38547     },
38548     /**
38549      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38550      */
38551     activate : function(){
38552         this.tabPanel.activate(this.id);
38553     },
38554
38555     /**
38556      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38557      */
38558     disable : function(){
38559         if(this.tabPanel.active != this){
38560             this.disabled = true;
38561             this.pnode.addClass("disabled");
38562         }
38563     },
38564
38565     /**
38566      * Enables this TabPanelItem if it was previously disabled.
38567      */
38568     enable : function(){
38569         this.disabled = false;
38570         this.pnode.removeClass("disabled");
38571     },
38572
38573     /**
38574      * Sets the content for this TabPanelItem.
38575      * @param {String} content The content
38576      * @param {Boolean} loadScripts true to look for and load scripts
38577      */
38578     setContent : function(content, loadScripts){
38579         this.bodyEl.update(content, loadScripts);
38580     },
38581
38582     /**
38583      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38584      * @return {Roo.UpdateManager} The UpdateManager
38585      */
38586     getUpdateManager : function(){
38587         return this.bodyEl.getUpdateManager();
38588     },
38589
38590     /**
38591      * Set a URL to be used to load the content for this TabPanelItem.
38592      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38593      * @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)
38594      * @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)
38595      * @return {Roo.UpdateManager} The UpdateManager
38596      */
38597     setUrl : function(url, params, loadOnce){
38598         if(this.refreshDelegate){
38599             this.un('activate', this.refreshDelegate);
38600         }
38601         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38602         this.on("activate", this.refreshDelegate);
38603         return this.bodyEl.getUpdateManager();
38604     },
38605
38606     /** @private */
38607     _handleRefresh : function(url, params, loadOnce){
38608         if(!loadOnce || !this.loaded){
38609             var updater = this.bodyEl.getUpdateManager();
38610             updater.update(url, params, this._setLoaded.createDelegate(this));
38611         }
38612     },
38613
38614     /**
38615      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38616      *   Will fail silently if the setUrl method has not been called.
38617      *   This does not activate the panel, just updates its content.
38618      */
38619     refresh : function(){
38620         if(this.refreshDelegate){
38621            this.loaded = false;
38622            this.refreshDelegate();
38623         }
38624     },
38625
38626     /** @private */
38627     _setLoaded : function(){
38628         this.loaded = true;
38629     },
38630
38631     /** @private */
38632     closeClick : function(e){
38633         var o = {};
38634         e.stopEvent();
38635         this.fireEvent("beforeclose", this, o);
38636         if(o.cancel !== true){
38637             this.tabPanel.removeTab(this.id);
38638         }
38639     },
38640     /**
38641      * The text displayed in the tooltip for the close icon.
38642      * @type String
38643      */
38644     closeText : "Close this tab"
38645 });
38646 /**
38647 *    This script refer to:
38648 *    Title: International Telephone Input
38649 *    Author: Jack O'Connor
38650 *    Code version:  v12.1.12
38651 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38652 **/
38653
38654 Roo.bootstrap.PhoneInputData = function() {
38655     var d = [
38656       [
38657         "Afghanistan (‫افغانستان‬‎)",
38658         "af",
38659         "93"
38660       ],
38661       [
38662         "Albania (Shqipëri)",
38663         "al",
38664         "355"
38665       ],
38666       [
38667         "Algeria (‫الجزائر‬‎)",
38668         "dz",
38669         "213"
38670       ],
38671       [
38672         "American Samoa",
38673         "as",
38674         "1684"
38675       ],
38676       [
38677         "Andorra",
38678         "ad",
38679         "376"
38680       ],
38681       [
38682         "Angola",
38683         "ao",
38684         "244"
38685       ],
38686       [
38687         "Anguilla",
38688         "ai",
38689         "1264"
38690       ],
38691       [
38692         "Antigua and Barbuda",
38693         "ag",
38694         "1268"
38695       ],
38696       [
38697         "Argentina",
38698         "ar",
38699         "54"
38700       ],
38701       [
38702         "Armenia (Հայաստան)",
38703         "am",
38704         "374"
38705       ],
38706       [
38707         "Aruba",
38708         "aw",
38709         "297"
38710       ],
38711       [
38712         "Australia",
38713         "au",
38714         "61",
38715         0
38716       ],
38717       [
38718         "Austria (Österreich)",
38719         "at",
38720         "43"
38721       ],
38722       [
38723         "Azerbaijan (Azərbaycan)",
38724         "az",
38725         "994"
38726       ],
38727       [
38728         "Bahamas",
38729         "bs",
38730         "1242"
38731       ],
38732       [
38733         "Bahrain (‫البحرين‬‎)",
38734         "bh",
38735         "973"
38736       ],
38737       [
38738         "Bangladesh (বাংলাদেশ)",
38739         "bd",
38740         "880"
38741       ],
38742       [
38743         "Barbados",
38744         "bb",
38745         "1246"
38746       ],
38747       [
38748         "Belarus (Беларусь)",
38749         "by",
38750         "375"
38751       ],
38752       [
38753         "Belgium (België)",
38754         "be",
38755         "32"
38756       ],
38757       [
38758         "Belize",
38759         "bz",
38760         "501"
38761       ],
38762       [
38763         "Benin (Bénin)",
38764         "bj",
38765         "229"
38766       ],
38767       [
38768         "Bermuda",
38769         "bm",
38770         "1441"
38771       ],
38772       [
38773         "Bhutan (འབྲུག)",
38774         "bt",
38775         "975"
38776       ],
38777       [
38778         "Bolivia",
38779         "bo",
38780         "591"
38781       ],
38782       [
38783         "Bosnia and Herzegovina (Босна и Херцеговина)",
38784         "ba",
38785         "387"
38786       ],
38787       [
38788         "Botswana",
38789         "bw",
38790         "267"
38791       ],
38792       [
38793         "Brazil (Brasil)",
38794         "br",
38795         "55"
38796       ],
38797       [
38798         "British Indian Ocean Territory",
38799         "io",
38800         "246"
38801       ],
38802       [
38803         "British Virgin Islands",
38804         "vg",
38805         "1284"
38806       ],
38807       [
38808         "Brunei",
38809         "bn",
38810         "673"
38811       ],
38812       [
38813         "Bulgaria (България)",
38814         "bg",
38815         "359"
38816       ],
38817       [
38818         "Burkina Faso",
38819         "bf",
38820         "226"
38821       ],
38822       [
38823         "Burundi (Uburundi)",
38824         "bi",
38825         "257"
38826       ],
38827       [
38828         "Cambodia (កម្ពុជា)",
38829         "kh",
38830         "855"
38831       ],
38832       [
38833         "Cameroon (Cameroun)",
38834         "cm",
38835         "237"
38836       ],
38837       [
38838         "Canada",
38839         "ca",
38840         "1",
38841         1,
38842         ["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"]
38843       ],
38844       [
38845         "Cape Verde (Kabu Verdi)",
38846         "cv",
38847         "238"
38848       ],
38849       [
38850         "Caribbean Netherlands",
38851         "bq",
38852         "599",
38853         1
38854       ],
38855       [
38856         "Cayman Islands",
38857         "ky",
38858         "1345"
38859       ],
38860       [
38861         "Central African Republic (République centrafricaine)",
38862         "cf",
38863         "236"
38864       ],
38865       [
38866         "Chad (Tchad)",
38867         "td",
38868         "235"
38869       ],
38870       [
38871         "Chile",
38872         "cl",
38873         "56"
38874       ],
38875       [
38876         "China (中国)",
38877         "cn",
38878         "86"
38879       ],
38880       [
38881         "Christmas Island",
38882         "cx",
38883         "61",
38884         2
38885       ],
38886       [
38887         "Cocos (Keeling) Islands",
38888         "cc",
38889         "61",
38890         1
38891       ],
38892       [
38893         "Colombia",
38894         "co",
38895         "57"
38896       ],
38897       [
38898         "Comoros (‫جزر القمر‬‎)",
38899         "km",
38900         "269"
38901       ],
38902       [
38903         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38904         "cd",
38905         "243"
38906       ],
38907       [
38908         "Congo (Republic) (Congo-Brazzaville)",
38909         "cg",
38910         "242"
38911       ],
38912       [
38913         "Cook Islands",
38914         "ck",
38915         "682"
38916       ],
38917       [
38918         "Costa Rica",
38919         "cr",
38920         "506"
38921       ],
38922       [
38923         "Côte d’Ivoire",
38924         "ci",
38925         "225"
38926       ],
38927       [
38928         "Croatia (Hrvatska)",
38929         "hr",
38930         "385"
38931       ],
38932       [
38933         "Cuba",
38934         "cu",
38935         "53"
38936       ],
38937       [
38938         "Curaçao",
38939         "cw",
38940         "599",
38941         0
38942       ],
38943       [
38944         "Cyprus (Κύπρος)",
38945         "cy",
38946         "357"
38947       ],
38948       [
38949         "Czech Republic (Česká republika)",
38950         "cz",
38951         "420"
38952       ],
38953       [
38954         "Denmark (Danmark)",
38955         "dk",
38956         "45"
38957       ],
38958       [
38959         "Djibouti",
38960         "dj",
38961         "253"
38962       ],
38963       [
38964         "Dominica",
38965         "dm",
38966         "1767"
38967       ],
38968       [
38969         "Dominican Republic (República Dominicana)",
38970         "do",
38971         "1",
38972         2,
38973         ["809", "829", "849"]
38974       ],
38975       [
38976         "Ecuador",
38977         "ec",
38978         "593"
38979       ],
38980       [
38981         "Egypt (‫مصر‬‎)",
38982         "eg",
38983         "20"
38984       ],
38985       [
38986         "El Salvador",
38987         "sv",
38988         "503"
38989       ],
38990       [
38991         "Equatorial Guinea (Guinea Ecuatorial)",
38992         "gq",
38993         "240"
38994       ],
38995       [
38996         "Eritrea",
38997         "er",
38998         "291"
38999       ],
39000       [
39001         "Estonia (Eesti)",
39002         "ee",
39003         "372"
39004       ],
39005       [
39006         "Ethiopia",
39007         "et",
39008         "251"
39009       ],
39010       [
39011         "Falkland Islands (Islas Malvinas)",
39012         "fk",
39013         "500"
39014       ],
39015       [
39016         "Faroe Islands (Føroyar)",
39017         "fo",
39018         "298"
39019       ],
39020       [
39021         "Fiji",
39022         "fj",
39023         "679"
39024       ],
39025       [
39026         "Finland (Suomi)",
39027         "fi",
39028         "358",
39029         0
39030       ],
39031       [
39032         "France",
39033         "fr",
39034         "33"
39035       ],
39036       [
39037         "French Guiana (Guyane française)",
39038         "gf",
39039         "594"
39040       ],
39041       [
39042         "French Polynesia (Polynésie française)",
39043         "pf",
39044         "689"
39045       ],
39046       [
39047         "Gabon",
39048         "ga",
39049         "241"
39050       ],
39051       [
39052         "Gambia",
39053         "gm",
39054         "220"
39055       ],
39056       [
39057         "Georgia (საქართველო)",
39058         "ge",
39059         "995"
39060       ],
39061       [
39062         "Germany (Deutschland)",
39063         "de",
39064         "49"
39065       ],
39066       [
39067         "Ghana (Gaana)",
39068         "gh",
39069         "233"
39070       ],
39071       [
39072         "Gibraltar",
39073         "gi",
39074         "350"
39075       ],
39076       [
39077         "Greece (Ελλάδα)",
39078         "gr",
39079         "30"
39080       ],
39081       [
39082         "Greenland (Kalaallit Nunaat)",
39083         "gl",
39084         "299"
39085       ],
39086       [
39087         "Grenada",
39088         "gd",
39089         "1473"
39090       ],
39091       [
39092         "Guadeloupe",
39093         "gp",
39094         "590",
39095         0
39096       ],
39097       [
39098         "Guam",
39099         "gu",
39100         "1671"
39101       ],
39102       [
39103         "Guatemala",
39104         "gt",
39105         "502"
39106       ],
39107       [
39108         "Guernsey",
39109         "gg",
39110         "44",
39111         1
39112       ],
39113       [
39114         "Guinea (Guinée)",
39115         "gn",
39116         "224"
39117       ],
39118       [
39119         "Guinea-Bissau (Guiné Bissau)",
39120         "gw",
39121         "245"
39122       ],
39123       [
39124         "Guyana",
39125         "gy",
39126         "592"
39127       ],
39128       [
39129         "Haiti",
39130         "ht",
39131         "509"
39132       ],
39133       [
39134         "Honduras",
39135         "hn",
39136         "504"
39137       ],
39138       [
39139         "Hong Kong (香港)",
39140         "hk",
39141         "852"
39142       ],
39143       [
39144         "Hungary (Magyarország)",
39145         "hu",
39146         "36"
39147       ],
39148       [
39149         "Iceland (Ísland)",
39150         "is",
39151         "354"
39152       ],
39153       [
39154         "India (भारत)",
39155         "in",
39156         "91"
39157       ],
39158       [
39159         "Indonesia",
39160         "id",
39161         "62"
39162       ],
39163       [
39164         "Iran (‫ایران‬‎)",
39165         "ir",
39166         "98"
39167       ],
39168       [
39169         "Iraq (‫العراق‬‎)",
39170         "iq",
39171         "964"
39172       ],
39173       [
39174         "Ireland",
39175         "ie",
39176         "353"
39177       ],
39178       [
39179         "Isle of Man",
39180         "im",
39181         "44",
39182         2
39183       ],
39184       [
39185         "Israel (‫ישראל‬‎)",
39186         "il",
39187         "972"
39188       ],
39189       [
39190         "Italy (Italia)",
39191         "it",
39192         "39",
39193         0
39194       ],
39195       [
39196         "Jamaica",
39197         "jm",
39198         "1876"
39199       ],
39200       [
39201         "Japan (日本)",
39202         "jp",
39203         "81"
39204       ],
39205       [
39206         "Jersey",
39207         "je",
39208         "44",
39209         3
39210       ],
39211       [
39212         "Jordan (‫الأردن‬‎)",
39213         "jo",
39214         "962"
39215       ],
39216       [
39217         "Kazakhstan (Казахстан)",
39218         "kz",
39219         "7",
39220         1
39221       ],
39222       [
39223         "Kenya",
39224         "ke",
39225         "254"
39226       ],
39227       [
39228         "Kiribati",
39229         "ki",
39230         "686"
39231       ],
39232       [
39233         "Kosovo",
39234         "xk",
39235         "383"
39236       ],
39237       [
39238         "Kuwait (‫الكويت‬‎)",
39239         "kw",
39240         "965"
39241       ],
39242       [
39243         "Kyrgyzstan (Кыргызстан)",
39244         "kg",
39245         "996"
39246       ],
39247       [
39248         "Laos (ລາວ)",
39249         "la",
39250         "856"
39251       ],
39252       [
39253         "Latvia (Latvija)",
39254         "lv",
39255         "371"
39256       ],
39257       [
39258         "Lebanon (‫لبنان‬‎)",
39259         "lb",
39260         "961"
39261       ],
39262       [
39263         "Lesotho",
39264         "ls",
39265         "266"
39266       ],
39267       [
39268         "Liberia",
39269         "lr",
39270         "231"
39271       ],
39272       [
39273         "Libya (‫ليبيا‬‎)",
39274         "ly",
39275         "218"
39276       ],
39277       [
39278         "Liechtenstein",
39279         "li",
39280         "423"
39281       ],
39282       [
39283         "Lithuania (Lietuva)",
39284         "lt",
39285         "370"
39286       ],
39287       [
39288         "Luxembourg",
39289         "lu",
39290         "352"
39291       ],
39292       [
39293         "Macau (澳門)",
39294         "mo",
39295         "853"
39296       ],
39297       [
39298         "Macedonia (FYROM) (Македонија)",
39299         "mk",
39300         "389"
39301       ],
39302       [
39303         "Madagascar (Madagasikara)",
39304         "mg",
39305         "261"
39306       ],
39307       [
39308         "Malawi",
39309         "mw",
39310         "265"
39311       ],
39312       [
39313         "Malaysia",
39314         "my",
39315         "60"
39316       ],
39317       [
39318         "Maldives",
39319         "mv",
39320         "960"
39321       ],
39322       [
39323         "Mali",
39324         "ml",
39325         "223"
39326       ],
39327       [
39328         "Malta",
39329         "mt",
39330         "356"
39331       ],
39332       [
39333         "Marshall Islands",
39334         "mh",
39335         "692"
39336       ],
39337       [
39338         "Martinique",
39339         "mq",
39340         "596"
39341       ],
39342       [
39343         "Mauritania (‫موريتانيا‬‎)",
39344         "mr",
39345         "222"
39346       ],
39347       [
39348         "Mauritius (Moris)",
39349         "mu",
39350         "230"
39351       ],
39352       [
39353         "Mayotte",
39354         "yt",
39355         "262",
39356         1
39357       ],
39358       [
39359         "Mexico (México)",
39360         "mx",
39361         "52"
39362       ],
39363       [
39364         "Micronesia",
39365         "fm",
39366         "691"
39367       ],
39368       [
39369         "Moldova (Republica Moldova)",
39370         "md",
39371         "373"
39372       ],
39373       [
39374         "Monaco",
39375         "mc",
39376         "377"
39377       ],
39378       [
39379         "Mongolia (Монгол)",
39380         "mn",
39381         "976"
39382       ],
39383       [
39384         "Montenegro (Crna Gora)",
39385         "me",
39386         "382"
39387       ],
39388       [
39389         "Montserrat",
39390         "ms",
39391         "1664"
39392       ],
39393       [
39394         "Morocco (‫المغرب‬‎)",
39395         "ma",
39396         "212",
39397         0
39398       ],
39399       [
39400         "Mozambique (Moçambique)",
39401         "mz",
39402         "258"
39403       ],
39404       [
39405         "Myanmar (Burma) (မြန်မာ)",
39406         "mm",
39407         "95"
39408       ],
39409       [
39410         "Namibia (Namibië)",
39411         "na",
39412         "264"
39413       ],
39414       [
39415         "Nauru",
39416         "nr",
39417         "674"
39418       ],
39419       [
39420         "Nepal (नेपाल)",
39421         "np",
39422         "977"
39423       ],
39424       [
39425         "Netherlands (Nederland)",
39426         "nl",
39427         "31"
39428       ],
39429       [
39430         "New Caledonia (Nouvelle-Calédonie)",
39431         "nc",
39432         "687"
39433       ],
39434       [
39435         "New Zealand",
39436         "nz",
39437         "64"
39438       ],
39439       [
39440         "Nicaragua",
39441         "ni",
39442         "505"
39443       ],
39444       [
39445         "Niger (Nijar)",
39446         "ne",
39447         "227"
39448       ],
39449       [
39450         "Nigeria",
39451         "ng",
39452         "234"
39453       ],
39454       [
39455         "Niue",
39456         "nu",
39457         "683"
39458       ],
39459       [
39460         "Norfolk Island",
39461         "nf",
39462         "672"
39463       ],
39464       [
39465         "North Korea (조선 민주주의 인민 공화국)",
39466         "kp",
39467         "850"
39468       ],
39469       [
39470         "Northern Mariana Islands",
39471         "mp",
39472         "1670"
39473       ],
39474       [
39475         "Norway (Norge)",
39476         "no",
39477         "47",
39478         0
39479       ],
39480       [
39481         "Oman (‫عُمان‬‎)",
39482         "om",
39483         "968"
39484       ],
39485       [
39486         "Pakistan (‫پاکستان‬‎)",
39487         "pk",
39488         "92"
39489       ],
39490       [
39491         "Palau",
39492         "pw",
39493         "680"
39494       ],
39495       [
39496         "Palestine (‫فلسطين‬‎)",
39497         "ps",
39498         "970"
39499       ],
39500       [
39501         "Panama (Panamá)",
39502         "pa",
39503         "507"
39504       ],
39505       [
39506         "Papua New Guinea",
39507         "pg",
39508         "675"
39509       ],
39510       [
39511         "Paraguay",
39512         "py",
39513         "595"
39514       ],
39515       [
39516         "Peru (Perú)",
39517         "pe",
39518         "51"
39519       ],
39520       [
39521         "Philippines",
39522         "ph",
39523         "63"
39524       ],
39525       [
39526         "Poland (Polska)",
39527         "pl",
39528         "48"
39529       ],
39530       [
39531         "Portugal",
39532         "pt",
39533         "351"
39534       ],
39535       [
39536         "Puerto Rico",
39537         "pr",
39538         "1",
39539         3,
39540         ["787", "939"]
39541       ],
39542       [
39543         "Qatar (‫قطر‬‎)",
39544         "qa",
39545         "974"
39546       ],
39547       [
39548         "Réunion (La Réunion)",
39549         "re",
39550         "262",
39551         0
39552       ],
39553       [
39554         "Romania (România)",
39555         "ro",
39556         "40"
39557       ],
39558       [
39559         "Russia (Россия)",
39560         "ru",
39561         "7",
39562         0
39563       ],
39564       [
39565         "Rwanda",
39566         "rw",
39567         "250"
39568       ],
39569       [
39570         "Saint Barthélemy",
39571         "bl",
39572         "590",
39573         1
39574       ],
39575       [
39576         "Saint Helena",
39577         "sh",
39578         "290"
39579       ],
39580       [
39581         "Saint Kitts and Nevis",
39582         "kn",
39583         "1869"
39584       ],
39585       [
39586         "Saint Lucia",
39587         "lc",
39588         "1758"
39589       ],
39590       [
39591         "Saint Martin (Saint-Martin (partie française))",
39592         "mf",
39593         "590",
39594         2
39595       ],
39596       [
39597         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39598         "pm",
39599         "508"
39600       ],
39601       [
39602         "Saint Vincent and the Grenadines",
39603         "vc",
39604         "1784"
39605       ],
39606       [
39607         "Samoa",
39608         "ws",
39609         "685"
39610       ],
39611       [
39612         "San Marino",
39613         "sm",
39614         "378"
39615       ],
39616       [
39617         "São Tomé and Príncipe (São Tomé e Príncipe)",
39618         "st",
39619         "239"
39620       ],
39621       [
39622         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39623         "sa",
39624         "966"
39625       ],
39626       [
39627         "Senegal (Sénégal)",
39628         "sn",
39629         "221"
39630       ],
39631       [
39632         "Serbia (Србија)",
39633         "rs",
39634         "381"
39635       ],
39636       [
39637         "Seychelles",
39638         "sc",
39639         "248"
39640       ],
39641       [
39642         "Sierra Leone",
39643         "sl",
39644         "232"
39645       ],
39646       [
39647         "Singapore",
39648         "sg",
39649         "65"
39650       ],
39651       [
39652         "Sint Maarten",
39653         "sx",
39654         "1721"
39655       ],
39656       [
39657         "Slovakia (Slovensko)",
39658         "sk",
39659         "421"
39660       ],
39661       [
39662         "Slovenia (Slovenija)",
39663         "si",
39664         "386"
39665       ],
39666       [
39667         "Solomon Islands",
39668         "sb",
39669         "677"
39670       ],
39671       [
39672         "Somalia (Soomaaliya)",
39673         "so",
39674         "252"
39675       ],
39676       [
39677         "South Africa",
39678         "za",
39679         "27"
39680       ],
39681       [
39682         "South Korea (대한민국)",
39683         "kr",
39684         "82"
39685       ],
39686       [
39687         "South Sudan (‫جنوب السودان‬‎)",
39688         "ss",
39689         "211"
39690       ],
39691       [
39692         "Spain (España)",
39693         "es",
39694         "34"
39695       ],
39696       [
39697         "Sri Lanka (ශ්‍රී ලංකාව)",
39698         "lk",
39699         "94"
39700       ],
39701       [
39702         "Sudan (‫السودان‬‎)",
39703         "sd",
39704         "249"
39705       ],
39706       [
39707         "Suriname",
39708         "sr",
39709         "597"
39710       ],
39711       [
39712         "Svalbard and Jan Mayen",
39713         "sj",
39714         "47",
39715         1
39716       ],
39717       [
39718         "Swaziland",
39719         "sz",
39720         "268"
39721       ],
39722       [
39723         "Sweden (Sverige)",
39724         "se",
39725         "46"
39726       ],
39727       [
39728         "Switzerland (Schweiz)",
39729         "ch",
39730         "41"
39731       ],
39732       [
39733         "Syria (‫سوريا‬‎)",
39734         "sy",
39735         "963"
39736       ],
39737       [
39738         "Taiwan (台灣)",
39739         "tw",
39740         "886"
39741       ],
39742       [
39743         "Tajikistan",
39744         "tj",
39745         "992"
39746       ],
39747       [
39748         "Tanzania",
39749         "tz",
39750         "255"
39751       ],
39752       [
39753         "Thailand (ไทย)",
39754         "th",
39755         "66"
39756       ],
39757       [
39758         "Timor-Leste",
39759         "tl",
39760         "670"
39761       ],
39762       [
39763         "Togo",
39764         "tg",
39765         "228"
39766       ],
39767       [
39768         "Tokelau",
39769         "tk",
39770         "690"
39771       ],
39772       [
39773         "Tonga",
39774         "to",
39775         "676"
39776       ],
39777       [
39778         "Trinidad and Tobago",
39779         "tt",
39780         "1868"
39781       ],
39782       [
39783         "Tunisia (‫تونس‬‎)",
39784         "tn",
39785         "216"
39786       ],
39787       [
39788         "Turkey (Türkiye)",
39789         "tr",
39790         "90"
39791       ],
39792       [
39793         "Turkmenistan",
39794         "tm",
39795         "993"
39796       ],
39797       [
39798         "Turks and Caicos Islands",
39799         "tc",
39800         "1649"
39801       ],
39802       [
39803         "Tuvalu",
39804         "tv",
39805         "688"
39806       ],
39807       [
39808         "U.S. Virgin Islands",
39809         "vi",
39810         "1340"
39811       ],
39812       [
39813         "Uganda",
39814         "ug",
39815         "256"
39816       ],
39817       [
39818         "Ukraine (Україна)",
39819         "ua",
39820         "380"
39821       ],
39822       [
39823         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39824         "ae",
39825         "971"
39826       ],
39827       [
39828         "United Kingdom",
39829         "gb",
39830         "44",
39831         0
39832       ],
39833       [
39834         "United States",
39835         "us",
39836         "1",
39837         0
39838       ],
39839       [
39840         "Uruguay",
39841         "uy",
39842         "598"
39843       ],
39844       [
39845         "Uzbekistan (Oʻzbekiston)",
39846         "uz",
39847         "998"
39848       ],
39849       [
39850         "Vanuatu",
39851         "vu",
39852         "678"
39853       ],
39854       [
39855         "Vatican City (Città del Vaticano)",
39856         "va",
39857         "39",
39858         1
39859       ],
39860       [
39861         "Venezuela",
39862         "ve",
39863         "58"
39864       ],
39865       [
39866         "Vietnam (Việt Nam)",
39867         "vn",
39868         "84"
39869       ],
39870       [
39871         "Wallis and Futuna (Wallis-et-Futuna)",
39872         "wf",
39873         "681"
39874       ],
39875       [
39876         "Western Sahara (‫الصحراء الغربية‬‎)",
39877         "eh",
39878         "212",
39879         1
39880       ],
39881       [
39882         "Yemen (‫اليمن‬‎)",
39883         "ye",
39884         "967"
39885       ],
39886       [
39887         "Zambia",
39888         "zm",
39889         "260"
39890       ],
39891       [
39892         "Zimbabwe",
39893         "zw",
39894         "263"
39895       ],
39896       [
39897         "Åland Islands",
39898         "ax",
39899         "358",
39900         1
39901       ]
39902   ];
39903   
39904   return d;
39905 }/**
39906 *    This script refer to:
39907 *    Title: International Telephone Input
39908 *    Author: Jack O'Connor
39909 *    Code version:  v12.1.12
39910 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39911 **/
39912
39913 /**
39914  * @class Roo.bootstrap.PhoneInput
39915  * @extends Roo.bootstrap.TriggerField
39916  * An input with International dial-code selection
39917  
39918  * @cfg {String} defaultDialCode default '+852'
39919  * @cfg {Array} preferedCountries default []
39920   
39921  * @constructor
39922  * Create a new PhoneInput.
39923  * @param {Object} config Configuration options
39924  */
39925
39926 Roo.bootstrap.PhoneInput = function(config) {
39927     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39928 };
39929
39930 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39931         
39932         listWidth: undefined,
39933         
39934         selectedClass: 'active',
39935         
39936         invalidClass : "has-warning",
39937         
39938         validClass: 'has-success',
39939         
39940         allowed: '0123456789',
39941         
39942         max_length: 15,
39943         
39944         /**
39945          * @cfg {String} defaultDialCode The default dial code when initializing the input
39946          */
39947         defaultDialCode: '+852',
39948         
39949         /**
39950          * @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
39951          */
39952         preferedCountries: false,
39953         
39954         getAutoCreate : function()
39955         {
39956             var data = Roo.bootstrap.PhoneInputData();
39957             var align = this.labelAlign || this.parentLabelAlign();
39958             var id = Roo.id();
39959             
39960             this.allCountries = [];
39961             this.dialCodeMapping = [];
39962             
39963             for (var i = 0; i < data.length; i++) {
39964               var c = data[i];
39965               this.allCountries[i] = {
39966                 name: c[0],
39967                 iso2: c[1],
39968                 dialCode: c[2],
39969                 priority: c[3] || 0,
39970                 areaCodes: c[4] || null
39971               };
39972               this.dialCodeMapping[c[2]] = {
39973                   name: c[0],
39974                   iso2: c[1],
39975                   priority: c[3] || 0,
39976                   areaCodes: c[4] || null
39977               };
39978             }
39979             
39980             var cfg = {
39981                 cls: 'form-group',
39982                 cn: []
39983             };
39984             
39985             var input =  {
39986                 tag: 'input',
39987                 id : id,
39988                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39989                 maxlength: this.max_length,
39990                 cls : 'form-control tel-input',
39991                 autocomplete: 'new-password'
39992             };
39993             
39994             var hiddenInput = {
39995                 tag: 'input',
39996                 type: 'hidden',
39997                 cls: 'hidden-tel-input'
39998             };
39999             
40000             if (this.name) {
40001                 hiddenInput.name = this.name;
40002             }
40003             
40004             if (this.disabled) {
40005                 input.disabled = true;
40006             }
40007             
40008             var flag_container = {
40009                 tag: 'div',
40010                 cls: 'flag-box',
40011                 cn: [
40012                     {
40013                         tag: 'div',
40014                         cls: 'flag'
40015                     },
40016                     {
40017                         tag: 'div',
40018                         cls: 'caret'
40019                     }
40020                 ]
40021             };
40022             
40023             var box = {
40024                 tag: 'div',
40025                 cls: this.hasFeedback ? 'has-feedback' : '',
40026                 cn: [
40027                     hiddenInput,
40028                     input,
40029                     {
40030                         tag: 'input',
40031                         cls: 'dial-code-holder',
40032                         disabled: true
40033                     }
40034                 ]
40035             };
40036             
40037             var container = {
40038                 cls: 'roo-select2-container input-group',
40039                 cn: [
40040                     flag_container,
40041                     box
40042                 ]
40043             };
40044             
40045             if (this.fieldLabel.length) {
40046                 var indicator = {
40047                     tag: 'i',
40048                     tooltip: 'This field is required'
40049                 };
40050                 
40051                 var label = {
40052                     tag: 'label',
40053                     'for':  id,
40054                     cls: 'control-label',
40055                     cn: []
40056                 };
40057                 
40058                 var label_text = {
40059                     tag: 'span',
40060                     html: this.fieldLabel
40061                 };
40062                 
40063                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40064                 label.cn = [
40065                     indicator,
40066                     label_text
40067                 ];
40068                 
40069                 if(this.indicatorpos == 'right') {
40070                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40071                     label.cn = [
40072                         label_text,
40073                         indicator
40074                     ];
40075                 }
40076                 
40077                 if(align == 'left') {
40078                     container = {
40079                         tag: 'div',
40080                         cn: [
40081                             container
40082                         ]
40083                     };
40084                     
40085                     if(this.labelWidth > 12){
40086                         label.style = "width: " + this.labelWidth + 'px';
40087                     }
40088                     if(this.labelWidth < 13 && this.labelmd == 0){
40089                         this.labelmd = this.labelWidth;
40090                     }
40091                     if(this.labellg > 0){
40092                         label.cls += ' col-lg-' + this.labellg;
40093                         input.cls += ' col-lg-' + (12 - this.labellg);
40094                     }
40095                     if(this.labelmd > 0){
40096                         label.cls += ' col-md-' + this.labelmd;
40097                         container.cls += ' col-md-' + (12 - this.labelmd);
40098                     }
40099                     if(this.labelsm > 0){
40100                         label.cls += ' col-sm-' + this.labelsm;
40101                         container.cls += ' col-sm-' + (12 - this.labelsm);
40102                     }
40103                     if(this.labelxs > 0){
40104                         label.cls += ' col-xs-' + this.labelxs;
40105                         container.cls += ' col-xs-' + (12 - this.labelxs);
40106                     }
40107                 }
40108             }
40109             
40110             cfg.cn = [
40111                 label,
40112                 container
40113             ];
40114             
40115             var settings = this;
40116             
40117             ['xs','sm','md','lg'].map(function(size){
40118                 if (settings[size]) {
40119                     cfg.cls += ' col-' + size + '-' + settings[size];
40120                 }
40121             });
40122             
40123             this.store = new Roo.data.Store({
40124                 proxy : new Roo.data.MemoryProxy({}),
40125                 reader : new Roo.data.JsonReader({
40126                     fields : [
40127                         {
40128                             'name' : 'name',
40129                             'type' : 'string'
40130                         },
40131                         {
40132                             'name' : 'iso2',
40133                             'type' : 'string'
40134                         },
40135                         {
40136                             'name' : 'dialCode',
40137                             'type' : 'string'
40138                         },
40139                         {
40140                             'name' : 'priority',
40141                             'type' : 'string'
40142                         },
40143                         {
40144                             'name' : 'areaCodes',
40145                             'type' : 'string'
40146                         }
40147                     ]
40148                 })
40149             });
40150             
40151             if(!this.preferedCountries) {
40152                 this.preferedCountries = [
40153                     'hk',
40154                     'gb',
40155                     'us'
40156                 ];
40157             }
40158             
40159             var p = this.preferedCountries.reverse();
40160             
40161             if(p) {
40162                 for (var i = 0; i < p.length; i++) {
40163                     for (var j = 0; j < this.allCountries.length; j++) {
40164                         if(this.allCountries[j].iso2 == p[i]) {
40165                             var t = this.allCountries[j];
40166                             this.allCountries.splice(j,1);
40167                             this.allCountries.unshift(t);
40168                         }
40169                     } 
40170                 }
40171             }
40172             
40173             this.store.proxy.data = {
40174                 success: true,
40175                 data: this.allCountries
40176             };
40177             
40178             return cfg;
40179         },
40180         
40181         initEvents : function()
40182         {
40183             this.createList();
40184             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40185             
40186             this.indicator = this.indicatorEl();
40187             this.flag = this.flagEl();
40188             this.dialCodeHolder = this.dialCodeHolderEl();
40189             
40190             this.trigger = this.el.select('div.flag-box',true).first();
40191             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40192             
40193             var _this = this;
40194             
40195             (function(){
40196                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40197                 _this.list.setWidth(lw);
40198             }).defer(100);
40199             
40200             this.list.on('mouseover', this.onViewOver, this);
40201             this.list.on('mousemove', this.onViewMove, this);
40202             this.inputEl().on("keyup", this.onKeyUp, this);
40203             this.inputEl().on("keypress", this.onKeyPress, this);
40204             
40205             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40206
40207             this.view = new Roo.View(this.list, this.tpl, {
40208                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40209             });
40210             
40211             this.view.on('click', this.onViewClick, this);
40212             this.setValue(this.defaultDialCode);
40213         },
40214         
40215         onTriggerClick : function(e)
40216         {
40217             Roo.log('trigger click');
40218             if(this.disabled){
40219                 return;
40220             }
40221             
40222             if(this.isExpanded()){
40223                 this.collapse();
40224                 this.hasFocus = false;
40225             }else {
40226                 this.store.load({});
40227                 this.hasFocus = true;
40228                 this.expand();
40229             }
40230         },
40231         
40232         isExpanded : function()
40233         {
40234             return this.list.isVisible();
40235         },
40236         
40237         collapse : function()
40238         {
40239             if(!this.isExpanded()){
40240                 return;
40241             }
40242             this.list.hide();
40243             Roo.get(document).un('mousedown', this.collapseIf, this);
40244             Roo.get(document).un('mousewheel', this.collapseIf, this);
40245             this.fireEvent('collapse', this);
40246             this.validate();
40247         },
40248         
40249         expand : function()
40250         {
40251             Roo.log('expand');
40252
40253             if(this.isExpanded() || !this.hasFocus){
40254                 return;
40255             }
40256             
40257             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40258             this.list.setWidth(lw);
40259             
40260             this.list.show();
40261             this.restrictHeight();
40262             
40263             Roo.get(document).on('mousedown', this.collapseIf, this);
40264             Roo.get(document).on('mousewheel', this.collapseIf, this);
40265             
40266             this.fireEvent('expand', this);
40267         },
40268         
40269         restrictHeight : function()
40270         {
40271             this.list.alignTo(this.inputEl(), this.listAlign);
40272             this.list.alignTo(this.inputEl(), this.listAlign);
40273         },
40274         
40275         onViewOver : function(e, t)
40276         {
40277             if(this.inKeyMode){
40278                 return;
40279             }
40280             var item = this.view.findItemFromChild(t);
40281             
40282             if(item){
40283                 var index = this.view.indexOf(item);
40284                 this.select(index, false);
40285             }
40286         },
40287
40288         // private
40289         onViewClick : function(view, doFocus, el, e)
40290         {
40291             var index = this.view.getSelectedIndexes()[0];
40292             
40293             var r = this.store.getAt(index);
40294             
40295             if(r){
40296                 this.onSelect(r, index);
40297             }
40298             if(doFocus !== false && !this.blockFocus){
40299                 this.inputEl().focus();
40300             }
40301         },
40302         
40303         onViewMove : function(e, t)
40304         {
40305             this.inKeyMode = false;
40306         },
40307         
40308         select : function(index, scrollIntoView)
40309         {
40310             this.selectedIndex = index;
40311             this.view.select(index);
40312             if(scrollIntoView !== false){
40313                 var el = this.view.getNode(index);
40314                 if(el){
40315                     this.list.scrollChildIntoView(el, false);
40316                 }
40317             }
40318         },
40319         
40320         createList : function()
40321         {
40322             this.list = Roo.get(document.body).createChild({
40323                 tag: 'ul',
40324                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40325                 style: 'display:none'
40326             });
40327             
40328             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40329         },
40330         
40331         collapseIf : function(e)
40332         {
40333             var in_combo  = e.within(this.el);
40334             var in_list =  e.within(this.list);
40335             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40336             
40337             if (in_combo || in_list || is_list) {
40338                 return;
40339             }
40340             this.collapse();
40341         },
40342         
40343         onSelect : function(record, index)
40344         {
40345             if(this.fireEvent('beforeselect', this, record, index) !== false){
40346                 
40347                 this.setFlagClass(record.data.iso2);
40348                 this.setDialCode(record.data.dialCode);
40349                 this.hasFocus = false;
40350                 this.collapse();
40351                 this.fireEvent('select', this, record, index);
40352             }
40353         },
40354         
40355         flagEl : function()
40356         {
40357             var flag = this.el.select('div.flag',true).first();
40358             if(!flag){
40359                 return false;
40360             }
40361             return flag;
40362         },
40363         
40364         dialCodeHolderEl : function()
40365         {
40366             var d = this.el.select('input.dial-code-holder',true).first();
40367             if(!d){
40368                 return false;
40369             }
40370             return d;
40371         },
40372         
40373         setDialCode : function(v)
40374         {
40375             this.dialCodeHolder.dom.value = '+'+v;
40376         },
40377         
40378         setFlagClass : function(n)
40379         {
40380             this.flag.dom.className = 'flag '+n;
40381         },
40382         
40383         getValue : function()
40384         {
40385             var v = this.inputEl().getValue();
40386             if(this.dialCodeHolder) {
40387                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40388             }
40389             return v;
40390         },
40391         
40392         setValue : function(v)
40393         {
40394             var d = this.getDialCode(v);
40395             
40396             //invalid dial code
40397             if(v.length == 0 || !d || d.length == 0) {
40398                 if(this.rendered){
40399                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40400                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40401                 }
40402                 return;
40403             }
40404             
40405             //valid dial code
40406             this.setFlagClass(this.dialCodeMapping[d].iso2);
40407             this.setDialCode(d);
40408             this.inputEl().dom.value = v.replace('+'+d,'');
40409             this.hiddenEl().dom.value = this.getValue();
40410             
40411             this.validate();
40412         },
40413         
40414         getDialCode : function(v)
40415         {
40416             v = v ||  '';
40417             
40418             if (v.length == 0) {
40419                 return this.dialCodeHolder.dom.value;
40420             }
40421             
40422             var dialCode = "";
40423             if (v.charAt(0) != "+") {
40424                 return false;
40425             }
40426             var numericChars = "";
40427             for (var i = 1; i < v.length; i++) {
40428               var c = v.charAt(i);
40429               if (!isNaN(c)) {
40430                 numericChars += c;
40431                 if (this.dialCodeMapping[numericChars]) {
40432                   dialCode = v.substr(1, i);
40433                 }
40434                 if (numericChars.length == 4) {
40435                   break;
40436                 }
40437               }
40438             }
40439             return dialCode;
40440         },
40441         
40442         reset : function()
40443         {
40444             this.setValue(this.defaultDialCode);
40445             this.validate();
40446         },
40447         
40448         hiddenEl : function()
40449         {
40450             return this.el.select('input.hidden-tel-input',true).first();
40451         },
40452         
40453         // after setting val
40454         onKeyUp : function(e){
40455             this.setValue(this.getValue());
40456         },
40457         
40458         onKeyPress : function(e){
40459             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40460                 e.stopEvent();
40461             }
40462         }
40463         
40464 });
40465 /**
40466  * @class Roo.bootstrap.MoneyField
40467  * @extends Roo.bootstrap.ComboBox
40468  * Bootstrap MoneyField class
40469  * 
40470  * @constructor
40471  * Create a new MoneyField.
40472  * @param {Object} config Configuration options
40473  */
40474
40475 Roo.bootstrap.MoneyField = function(config) {
40476     
40477     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40478     
40479 };
40480
40481 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40482     
40483     /**
40484      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40485      */
40486     allowDecimals : true,
40487     /**
40488      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40489      */
40490     decimalSeparator : ".",
40491     /**
40492      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40493      */
40494     decimalPrecision : 0,
40495     /**
40496      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40497      */
40498     allowNegative : true,
40499     /**
40500      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40501      */
40502     allowZero: true,
40503     /**
40504      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40505      */
40506     minValue : Number.NEGATIVE_INFINITY,
40507     /**
40508      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40509      */
40510     maxValue : Number.MAX_VALUE,
40511     /**
40512      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40513      */
40514     minText : "The minimum value for this field is {0}",
40515     /**
40516      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40517      */
40518     maxText : "The maximum value for this field is {0}",
40519     /**
40520      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40521      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40522      */
40523     nanText : "{0} is not a valid number",
40524     /**
40525      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40526      */
40527     castInt : true,
40528     /**
40529      * @cfg {String} defaults currency of the MoneyField
40530      * value should be in lkey
40531      */
40532     defaultCurrency : false,
40533     /**
40534      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40535      */
40536     thousandsDelimiter : false,
40537     /**
40538      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40539      */
40540     max_length: false,
40541     
40542     inputlg : 9,
40543     inputmd : 9,
40544     inputsm : 9,
40545     inputxs : 6,
40546     
40547     store : false,
40548     
40549     getAutoCreate : function()
40550     {
40551         var align = this.labelAlign || this.parentLabelAlign();
40552         
40553         var id = Roo.id();
40554
40555         var cfg = {
40556             cls: 'form-group',
40557             cn: []
40558         };
40559
40560         var input =  {
40561             tag: 'input',
40562             id : id,
40563             cls : 'form-control roo-money-amount-input',
40564             autocomplete: 'new-password'
40565         };
40566         
40567         var hiddenInput = {
40568             tag: 'input',
40569             type: 'hidden',
40570             id: Roo.id(),
40571             cls: 'hidden-number-input'
40572         };
40573         
40574         if(this.max_length) {
40575             input.maxlength = this.max_length; 
40576         }
40577         
40578         if (this.name) {
40579             hiddenInput.name = this.name;
40580         }
40581
40582         if (this.disabled) {
40583             input.disabled = true;
40584         }
40585
40586         var clg = 12 - this.inputlg;
40587         var cmd = 12 - this.inputmd;
40588         var csm = 12 - this.inputsm;
40589         var cxs = 12 - this.inputxs;
40590         
40591         var container = {
40592             tag : 'div',
40593             cls : 'row roo-money-field',
40594             cn : [
40595                 {
40596                     tag : 'div',
40597                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40598                     cn : [
40599                         {
40600                             tag : 'div',
40601                             cls: 'roo-select2-container input-group',
40602                             cn: [
40603                                 {
40604                                     tag : 'input',
40605                                     cls : 'form-control roo-money-currency-input',
40606                                     autocomplete: 'new-password',
40607                                     readOnly : 1,
40608                                     name : this.currencyName
40609                                 },
40610                                 {
40611                                     tag :'span',
40612                                     cls : 'input-group-addon',
40613                                     cn : [
40614                                         {
40615                                             tag: 'span',
40616                                             cls: 'caret'
40617                                         }
40618                                     ]
40619                                 }
40620                             ]
40621                         }
40622                     ]
40623                 },
40624                 {
40625                     tag : 'div',
40626                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40627                     cn : [
40628                         {
40629                             tag: 'div',
40630                             cls: this.hasFeedback ? 'has-feedback' : '',
40631                             cn: [
40632                                 input
40633                             ]
40634                         }
40635                     ]
40636                 }
40637             ]
40638             
40639         };
40640         
40641         if (this.fieldLabel.length) {
40642             var indicator = {
40643                 tag: 'i',
40644                 tooltip: 'This field is required'
40645             };
40646
40647             var label = {
40648                 tag: 'label',
40649                 'for':  id,
40650                 cls: 'control-label',
40651                 cn: []
40652             };
40653
40654             var label_text = {
40655                 tag: 'span',
40656                 html: this.fieldLabel
40657             };
40658
40659             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40660             label.cn = [
40661                 indicator,
40662                 label_text
40663             ];
40664
40665             if(this.indicatorpos == 'right') {
40666                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40667                 label.cn = [
40668                     label_text,
40669                     indicator
40670                 ];
40671             }
40672
40673             if(align == 'left') {
40674                 container = {
40675                     tag: 'div',
40676                     cn: [
40677                         container
40678                     ]
40679                 };
40680
40681                 if(this.labelWidth > 12){
40682                     label.style = "width: " + this.labelWidth + 'px';
40683                 }
40684                 if(this.labelWidth < 13 && this.labelmd == 0){
40685                     this.labelmd = this.labelWidth;
40686                 }
40687                 if(this.labellg > 0){
40688                     label.cls += ' col-lg-' + this.labellg;
40689                     input.cls += ' col-lg-' + (12 - this.labellg);
40690                 }
40691                 if(this.labelmd > 0){
40692                     label.cls += ' col-md-' + this.labelmd;
40693                     container.cls += ' col-md-' + (12 - this.labelmd);
40694                 }
40695                 if(this.labelsm > 0){
40696                     label.cls += ' col-sm-' + this.labelsm;
40697                     container.cls += ' col-sm-' + (12 - this.labelsm);
40698                 }
40699                 if(this.labelxs > 0){
40700                     label.cls += ' col-xs-' + this.labelxs;
40701                     container.cls += ' col-xs-' + (12 - this.labelxs);
40702                 }
40703             }
40704         }
40705
40706         cfg.cn = [
40707             label,
40708             container,
40709             hiddenInput
40710         ];
40711         
40712         var settings = this;
40713
40714         ['xs','sm','md','lg'].map(function(size){
40715             if (settings[size]) {
40716                 cfg.cls += ' col-' + size + '-' + settings[size];
40717             }
40718         });
40719         
40720         return cfg;
40721     },
40722     
40723     initEvents : function()
40724     {
40725         this.indicator = this.indicatorEl();
40726         
40727         this.initCurrencyEvent();
40728         
40729         this.initNumberEvent();
40730     },
40731     
40732     initCurrencyEvent : function()
40733     {
40734         if (!this.store) {
40735             throw "can not find store for combo";
40736         }
40737         
40738         this.store = Roo.factory(this.store, Roo.data);
40739         this.store.parent = this;
40740         
40741         this.createList();
40742         
40743         this.triggerEl = this.el.select('.input-group-addon', true).first();
40744         
40745         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40746         
40747         var _this = this;
40748         
40749         (function(){
40750             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40751             _this.list.setWidth(lw);
40752         }).defer(100);
40753         
40754         this.list.on('mouseover', this.onViewOver, this);
40755         this.list.on('mousemove', this.onViewMove, this);
40756         this.list.on('scroll', this.onViewScroll, this);
40757         
40758         if(!this.tpl){
40759             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40760         }
40761         
40762         this.view = new Roo.View(this.list, this.tpl, {
40763             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40764         });
40765         
40766         this.view.on('click', this.onViewClick, this);
40767         
40768         this.store.on('beforeload', this.onBeforeLoad, this);
40769         this.store.on('load', this.onLoad, this);
40770         this.store.on('loadexception', this.onLoadException, this);
40771         
40772         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40773             "up" : function(e){
40774                 this.inKeyMode = true;
40775                 this.selectPrev();
40776             },
40777
40778             "down" : function(e){
40779                 if(!this.isExpanded()){
40780                     this.onTriggerClick();
40781                 }else{
40782                     this.inKeyMode = true;
40783                     this.selectNext();
40784                 }
40785             },
40786
40787             "enter" : function(e){
40788                 this.collapse();
40789                 
40790                 if(this.fireEvent("specialkey", this, e)){
40791                     this.onViewClick(false);
40792                 }
40793                 
40794                 return true;
40795             },
40796
40797             "esc" : function(e){
40798                 this.collapse();
40799             },
40800
40801             "tab" : function(e){
40802                 this.collapse();
40803                 
40804                 if(this.fireEvent("specialkey", this, e)){
40805                     this.onViewClick(false);
40806                 }
40807                 
40808                 return true;
40809             },
40810
40811             scope : this,
40812
40813             doRelay : function(foo, bar, hname){
40814                 if(hname == 'down' || this.scope.isExpanded()){
40815                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40816                 }
40817                 return true;
40818             },
40819
40820             forceKeyDown: true
40821         });
40822         
40823         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40824         
40825     },
40826     
40827     initNumberEvent : function(e)
40828     {
40829         this.inputEl().on("keydown" , this.fireKey,  this);
40830         this.inputEl().on("focus", this.onFocus,  this);
40831         this.inputEl().on("blur", this.onBlur,  this);
40832         
40833         this.inputEl().relayEvent('keyup', this);
40834         
40835         if(this.indicator){
40836             this.indicator.addClass('invisible');
40837         }
40838  
40839         this.originalValue = this.getValue();
40840         
40841         if(this.validationEvent == 'keyup'){
40842             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40843             this.inputEl().on('keyup', this.filterValidation, this);
40844         }
40845         else if(this.validationEvent !== false){
40846             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40847         }
40848         
40849         if(this.selectOnFocus){
40850             this.on("focus", this.preFocus, this);
40851             
40852         }
40853         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40854             this.inputEl().on("keypress", this.filterKeys, this);
40855         } else {
40856             this.inputEl().relayEvent('keypress', this);
40857         }
40858         
40859         var allowed = "0123456789";
40860         
40861         if(this.allowDecimals){
40862             allowed += this.decimalSeparator;
40863         }
40864         
40865         if(this.allowNegative){
40866             allowed += "-";
40867         }
40868         
40869         if(this.thousandsDelimiter) {
40870             allowed += ",";
40871         }
40872         
40873         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40874         
40875         var keyPress = function(e){
40876             
40877             var k = e.getKey();
40878             
40879             var c = e.getCharCode();
40880             
40881             if(
40882                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40883                     allowed.indexOf(String.fromCharCode(c)) === -1
40884             ){
40885                 e.stopEvent();
40886                 return;
40887             }
40888             
40889             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40890                 return;
40891             }
40892             
40893             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40894                 e.stopEvent();
40895             }
40896         };
40897         
40898         this.inputEl().on("keypress", keyPress, this);
40899         
40900     },
40901     
40902     onTriggerClick : function(e)
40903     {   
40904         if(this.disabled){
40905             return;
40906         }
40907         
40908         this.page = 0;
40909         this.loadNext = false;
40910         
40911         if(this.isExpanded()){
40912             this.collapse();
40913             return;
40914         }
40915         
40916         this.hasFocus = true;
40917         
40918         if(this.triggerAction == 'all') {
40919             this.doQuery(this.allQuery, true);
40920             return;
40921         }
40922         
40923         this.doQuery(this.getRawValue());
40924     },
40925     
40926     getCurrency : function()
40927     {   
40928         var v = this.currencyEl().getValue();
40929         
40930         return v;
40931     },
40932     
40933     restrictHeight : function()
40934     {
40935         this.list.alignTo(this.currencyEl(), this.listAlign);
40936         this.list.alignTo(this.currencyEl(), this.listAlign);
40937     },
40938     
40939     onViewClick : function(view, doFocus, el, e)
40940     {
40941         var index = this.view.getSelectedIndexes()[0];
40942         
40943         var r = this.store.getAt(index);
40944         
40945         if(r){
40946             this.onSelect(r, index);
40947         }
40948     },
40949     
40950     onSelect : function(record, index){
40951         
40952         if(this.fireEvent('beforeselect', this, record, index) !== false){
40953         
40954             this.setFromCurrencyData(index > -1 ? record.data : false);
40955             
40956             this.collapse();
40957             
40958             this.fireEvent('select', this, record, index);
40959         }
40960     },
40961     
40962     setFromCurrencyData : function(o)
40963     {
40964         var currency = '';
40965         
40966         this.lastCurrency = o;
40967         
40968         if (this.currencyField) {
40969             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40970         } else {
40971             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40972         }
40973         
40974         this.lastSelectionText = currency;
40975         
40976         //setting default currency
40977         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40978             this.setCurrency(this.defaultCurrency);
40979             return;
40980         }
40981         
40982         this.setCurrency(currency);
40983     },
40984     
40985     setFromData : function(o)
40986     {
40987         var c = {};
40988         
40989         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40990         
40991         this.setFromCurrencyData(c);
40992         
40993         var value = '';
40994         
40995         if (this.name) {
40996             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40997         } else {
40998             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40999         }
41000         
41001         this.setValue(value);
41002         
41003     },
41004     
41005     setCurrency : function(v)
41006     {   
41007         this.currencyValue = v;
41008         
41009         if(this.rendered){
41010             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41011             this.validate();
41012         }
41013     },
41014     
41015     setValue : function(v)
41016     {
41017         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41018         
41019         this.value = v;
41020         
41021         if(this.rendered){
41022             
41023             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41024             
41025             this.inputEl().dom.value = (v == '') ? '' :
41026                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41027             
41028             if(!this.allowZero && v === '0') {
41029                 this.hiddenEl().dom.value = '';
41030                 this.inputEl().dom.value = '';
41031             }
41032             
41033             this.validate();
41034         }
41035     },
41036     
41037     getRawValue : function()
41038     {
41039         var v = this.inputEl().getValue();
41040         
41041         return v;
41042     },
41043     
41044     getValue : function()
41045     {
41046         return this.fixPrecision(this.parseValue(this.getRawValue()));
41047     },
41048     
41049     parseValue : function(value)
41050     {
41051         if(this.thousandsDelimiter) {
41052             value += "";
41053             r = new RegExp(",", "g");
41054             value = value.replace(r, "");
41055         }
41056         
41057         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41058         return isNaN(value) ? '' : value;
41059         
41060     },
41061     
41062     fixPrecision : function(value)
41063     {
41064         if(this.thousandsDelimiter) {
41065             value += "";
41066             r = new RegExp(",", "g");
41067             value = value.replace(r, "");
41068         }
41069         
41070         var nan = isNaN(value);
41071         
41072         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41073             return nan ? '' : value;
41074         }
41075         return parseFloat(value).toFixed(this.decimalPrecision);
41076     },
41077     
41078     decimalPrecisionFcn : function(v)
41079     {
41080         return Math.floor(v);
41081     },
41082     
41083     validateValue : function(value)
41084     {
41085         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41086             return false;
41087         }
41088         
41089         var num = this.parseValue(value);
41090         
41091         if(isNaN(num)){
41092             this.markInvalid(String.format(this.nanText, value));
41093             return false;
41094         }
41095         
41096         if(num < this.minValue){
41097             this.markInvalid(String.format(this.minText, this.minValue));
41098             return false;
41099         }
41100         
41101         if(num > this.maxValue){
41102             this.markInvalid(String.format(this.maxText, this.maxValue));
41103             return false;
41104         }
41105         
41106         return true;
41107     },
41108     
41109     validate : function()
41110     {
41111         if(this.disabled || this.allowBlank){
41112             this.markValid();
41113             return true;
41114         }
41115         
41116         var currency = this.getCurrency();
41117         
41118         if(this.validateValue(this.getRawValue()) && currency.length){
41119             this.markValid();
41120             return true;
41121         }
41122         
41123         this.markInvalid();
41124         return false;
41125     },
41126     
41127     getName: function()
41128     {
41129         return this.name;
41130     },
41131     
41132     beforeBlur : function()
41133     {
41134         if(!this.castInt){
41135             return;
41136         }
41137         
41138         var v = this.parseValue(this.getRawValue());
41139         
41140         if(v || v == 0){
41141             this.setValue(v);
41142         }
41143     },
41144     
41145     onBlur : function()
41146     {
41147         this.beforeBlur();
41148         
41149         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41150             //this.el.removeClass(this.focusClass);
41151         }
41152         
41153         this.hasFocus = false;
41154         
41155         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41156             this.validate();
41157         }
41158         
41159         var v = this.getValue();
41160         
41161         if(String(v) !== String(this.startValue)){
41162             this.fireEvent('change', this, v, this.startValue);
41163         }
41164         
41165         this.fireEvent("blur", this);
41166     },
41167     
41168     inputEl : function()
41169     {
41170         return this.el.select('.roo-money-amount-input', true).first();
41171     },
41172     
41173     currencyEl : function()
41174     {
41175         return this.el.select('.roo-money-currency-input', true).first();
41176     },
41177     
41178     hiddenEl : function()
41179     {
41180         return this.el.select('input.hidden-number-input',true).first();
41181     }
41182     
41183 });