Roo/bootstrap/TriggerField.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         var box = {
10386             tag: 'div',
10387             cn: [
10388                 {
10389                     tag: 'input',
10390                     type : 'hidden',
10391                     cls: 'form-hidden-field'
10392                 },
10393                 inputblock
10394             ]
10395             
10396         };
10397         
10398         var ibwrap = inputbox;
10399         
10400         if(this.multiple){
10401             ibwrap = {
10402                 tag: 'ul',
10403                 cls: 'roo-select2-choices',
10404                 cn:[
10405                     {
10406                         tag: 'li',
10407                         cls: 'roo-select2-search-field',
10408                         cn: [
10409
10410                             inputblock
10411                         ]
10412                     }
10413                 ]
10414             };
10415                 
10416         }
10417         
10418         var combobox = {
10419             cls: 'roo-select2-container input-group',
10420             cn: [
10421                  {
10422                     tag: 'input',
10423                     type : 'hidden',
10424                     cls: 'form-hidden-field'
10425                 },
10426                 ibwrap
10427             ]
10428         };
10429         
10430         if(!this.multiple && this.showToggleBtn){
10431             
10432             var caret = {
10433                         tag: 'span',
10434                         cls: 'caret'
10435              };
10436             if (this.caret != false) {
10437                 caret = {
10438                      tag: 'i',
10439                      cls: 'fa fa-' + this.caret
10440                 };
10441                 
10442             }
10443             
10444             combobox.cn.push({
10445                 tag :'span',
10446                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10447                 cn : [
10448                     caret,
10449                     {
10450                         tag: 'span',
10451                         cls: 'combobox-clear',
10452                         cn  : [
10453                             {
10454                                 tag : 'i',
10455                                 cls: 'icon-remove'
10456                             }
10457                         ]
10458                     }
10459                 ]
10460
10461             })
10462         }
10463         
10464         if(this.multiple){
10465             combobox.cls += ' roo-select2-container-multi';
10466         }
10467          var indicator = {
10468             tag : 'i',
10469             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10470             tooltip : 'This field is required'
10471         };
10472         if (Roo.bootstrap.version == 4) {
10473             indicator = {
10474                 tag : 'i',
10475                 style : 'display:none'
10476             };
10477         }
10478         
10479         
10480         if (align ==='left' && this.fieldLabel.length) {
10481             
10482             cfg.cls += ' roo-form-group-label-left row';
10483
10484             cfg.cn = [
10485                 indicator,
10486                 {
10487                     tag: 'label',
10488                     'for' :  id,
10489                     cls : 'control-label',
10490                     html : this.fieldLabel
10491
10492                 },
10493                 {
10494                     cls : "", 
10495                     cn: [
10496                         combobox
10497                     ]
10498                 }
10499
10500             ];
10501             
10502             var labelCfg = cfg.cn[1];
10503             var contentCfg = cfg.cn[2];
10504             
10505             if(this.indicatorpos == 'right'){
10506                 cfg.cn = [
10507                     {
10508                         tag: 'label',
10509                         'for' :  id,
10510                         cls : 'control-label',
10511                         cn : [
10512                             {
10513                                 tag : 'span',
10514                                 html : this.fieldLabel
10515                             },
10516                             indicator
10517                         ]
10518                     },
10519                     {
10520                         cls : "", 
10521                         cn: [
10522                             combobox
10523                         ]
10524                     }
10525
10526                 ];
10527                 
10528                 labelCfg = cfg.cn[0];
10529                 contentCfg = cfg.cn[1];
10530             }
10531             
10532             if(this.labelWidth > 12){
10533                 labelCfg.style = "width: " + this.labelWidth + 'px';
10534             }
10535             
10536             if(this.labelWidth < 13 && this.labelmd == 0){
10537                 this.labelmd = this.labelWidth;
10538             }
10539             
10540             if(this.labellg > 0){
10541                 labelCfg.cls += ' col-lg-' + this.labellg;
10542                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10543             }
10544             
10545             if(this.labelmd > 0){
10546                 labelCfg.cls += ' col-md-' + this.labelmd;
10547                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10548             }
10549             
10550             if(this.labelsm > 0){
10551                 labelCfg.cls += ' col-sm-' + this.labelsm;
10552                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10553             }
10554             
10555             if(this.labelxs > 0){
10556                 labelCfg.cls += ' col-xs-' + this.labelxs;
10557                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10558             }
10559             
10560         } else if ( this.fieldLabel.length) {
10561 //                Roo.log(" label");
10562             cfg.cn = [
10563                 indicator,
10564                {
10565                    tag: 'label',
10566                    //cls : 'input-group-addon',
10567                    html : this.fieldLabel
10568
10569                },
10570
10571                combobox
10572
10573             ];
10574             
10575             if(this.indicatorpos == 'right'){
10576                 
10577                 cfg.cn = [
10578                     {
10579                        tag: 'label',
10580                        cn : [
10581                            {
10582                                tag : 'span',
10583                                html : this.fieldLabel
10584                            },
10585                            indicator
10586                        ]
10587
10588                     },
10589                     combobox
10590
10591                 ];
10592
10593             }
10594
10595         } else {
10596             
10597 //                Roo.log(" no label && no align");
10598                 cfg = combobox
10599                      
10600                 
10601         }
10602         
10603         var settings=this;
10604         ['xs','sm','md','lg'].map(function(size){
10605             if (settings[size]) {
10606                 cfg.cls += ' col-' + size + '-' + settings[size];
10607             }
10608         });
10609         
10610         return cfg;
10611         
10612     },
10613     
10614     
10615     
10616     // private
10617     onResize : function(w, h){
10618 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10619 //        if(typeof w == 'number'){
10620 //            var x = w - this.trigger.getWidth();
10621 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10622 //            this.trigger.setStyle('left', x+'px');
10623 //        }
10624     },
10625
10626     // private
10627     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10628
10629     // private
10630     getResizeEl : function(){
10631         return this.inputEl();
10632     },
10633
10634     // private
10635     getPositionEl : function(){
10636         return this.inputEl();
10637     },
10638
10639     // private
10640     alignErrorIcon : function(){
10641         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10642     },
10643
10644     // private
10645     initEvents : function(){
10646         
10647         this.createList();
10648         
10649         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10650         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10651         if(!this.multiple && this.showToggleBtn){
10652             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10653             if(this.hideTrigger){
10654                 this.trigger.setDisplayed(false);
10655             }
10656             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10657         }
10658         
10659         if(this.multiple){
10660             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10661         }
10662         
10663         if(this.removable && !this.editable && !this.tickable){
10664             var close = this.closeTriggerEl();
10665             
10666             if(close){
10667                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10668                 close.on('click', this.removeBtnClick, this, close);
10669             }
10670         }
10671         
10672         //this.trigger.addClassOnOver('x-form-trigger-over');
10673         //this.trigger.addClassOnClick('x-form-trigger-click');
10674         
10675         //if(!this.width){
10676         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10677         //}
10678     },
10679     
10680     closeTriggerEl : function()
10681     {
10682         var close = this.el.select('.roo-combo-removable-btn', true).first();
10683         return close ? close : false;
10684     },
10685     
10686     removeBtnClick : function(e, h, el)
10687     {
10688         e.preventDefault();
10689         
10690         if(this.fireEvent("remove", this) !== false){
10691             this.reset();
10692             this.fireEvent("afterremove", this)
10693         }
10694     },
10695     
10696     createList : function()
10697     {
10698         this.list = Roo.get(document.body).createChild({
10699             tag: 'ul',
10700             cls: 'typeahead typeahead-long dropdown-menu',
10701             style: 'display:none'
10702         });
10703         
10704         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10705         
10706     },
10707
10708     // private
10709     initTrigger : function(){
10710        
10711     },
10712
10713     // private
10714     onDestroy : function(){
10715         if(this.trigger){
10716             this.trigger.removeAllListeners();
10717           //  this.trigger.remove();
10718         }
10719         //if(this.wrap){
10720         //    this.wrap.remove();
10721         //}
10722         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10723     },
10724
10725     // private
10726     onFocus : function(){
10727         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10728         /*
10729         if(!this.mimicing){
10730             this.wrap.addClass('x-trigger-wrap-focus');
10731             this.mimicing = true;
10732             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10733             if(this.monitorTab){
10734                 this.el.on("keydown", this.checkTab, this);
10735             }
10736         }
10737         */
10738     },
10739
10740     // private
10741     checkTab : function(e){
10742         if(e.getKey() == e.TAB){
10743             this.triggerBlur();
10744         }
10745     },
10746
10747     // private
10748     onBlur : function(){
10749         // do nothing
10750     },
10751
10752     // private
10753     mimicBlur : function(e, t){
10754         /*
10755         if(!this.wrap.contains(t) && this.validateBlur()){
10756             this.triggerBlur();
10757         }
10758         */
10759     },
10760
10761     // private
10762     triggerBlur : function(){
10763         this.mimicing = false;
10764         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10765         if(this.monitorTab){
10766             this.el.un("keydown", this.checkTab, this);
10767         }
10768         //this.wrap.removeClass('x-trigger-wrap-focus');
10769         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10770     },
10771
10772     // private
10773     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10774     validateBlur : function(e, t){
10775         return true;
10776     },
10777
10778     // private
10779     onDisable : function(){
10780         this.inputEl().dom.disabled = true;
10781         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10782         //if(this.wrap){
10783         //    this.wrap.addClass('x-item-disabled');
10784         //}
10785     },
10786
10787     // private
10788     onEnable : function(){
10789         this.inputEl().dom.disabled = false;
10790         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10791         //if(this.wrap){
10792         //    this.el.removeClass('x-item-disabled');
10793         //}
10794     },
10795
10796     // private
10797     onShow : function(){
10798         var ae = this.getActionEl();
10799         
10800         if(ae){
10801             ae.dom.style.display = '';
10802             ae.dom.style.visibility = 'visible';
10803         }
10804     },
10805
10806     // private
10807     
10808     onHide : function(){
10809         var ae = this.getActionEl();
10810         ae.dom.style.display = 'none';
10811     },
10812
10813     /**
10814      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10815      * by an implementing function.
10816      * @method
10817      * @param {EventObject} e
10818      */
10819     onTriggerClick : Roo.emptyFn
10820 });
10821  /*
10822  * Based on:
10823  * Ext JS Library 1.1.1
10824  * Copyright(c) 2006-2007, Ext JS, LLC.
10825  *
10826  * Originally Released Under LGPL - original licence link has changed is not relivant.
10827  *
10828  * Fork - LGPL
10829  * <script type="text/javascript">
10830  */
10831
10832
10833 /**
10834  * @class Roo.data.SortTypes
10835  * @singleton
10836  * Defines the default sorting (casting?) comparison functions used when sorting data.
10837  */
10838 Roo.data.SortTypes = {
10839     /**
10840      * Default sort that does nothing
10841      * @param {Mixed} s The value being converted
10842      * @return {Mixed} The comparison value
10843      */
10844     none : function(s){
10845         return s;
10846     },
10847     
10848     /**
10849      * The regular expression used to strip tags
10850      * @type {RegExp}
10851      * @property
10852      */
10853     stripTagsRE : /<\/?[^>]+>/gi,
10854     
10855     /**
10856      * Strips all HTML tags to sort on text only
10857      * @param {Mixed} s The value being converted
10858      * @return {String} The comparison value
10859      */
10860     asText : function(s){
10861         return String(s).replace(this.stripTagsRE, "");
10862     },
10863     
10864     /**
10865      * Strips all HTML tags to sort on text only - Case insensitive
10866      * @param {Mixed} s The value being converted
10867      * @return {String} The comparison value
10868      */
10869     asUCText : function(s){
10870         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10871     },
10872     
10873     /**
10874      * Case insensitive string
10875      * @param {Mixed} s The value being converted
10876      * @return {String} The comparison value
10877      */
10878     asUCString : function(s) {
10879         return String(s).toUpperCase();
10880     },
10881     
10882     /**
10883      * Date sorting
10884      * @param {Mixed} s The value being converted
10885      * @return {Number} The comparison value
10886      */
10887     asDate : function(s) {
10888         if(!s){
10889             return 0;
10890         }
10891         if(s instanceof Date){
10892             return s.getTime();
10893         }
10894         return Date.parse(String(s));
10895     },
10896     
10897     /**
10898      * Float sorting
10899      * @param {Mixed} s The value being converted
10900      * @return {Float} The comparison value
10901      */
10902     asFloat : function(s) {
10903         var val = parseFloat(String(s).replace(/,/g, ""));
10904         if(isNaN(val)) {
10905             val = 0;
10906         }
10907         return val;
10908     },
10909     
10910     /**
10911      * Integer sorting
10912      * @param {Mixed} s The value being converted
10913      * @return {Number} The comparison value
10914      */
10915     asInt : function(s) {
10916         var val = parseInt(String(s).replace(/,/g, ""));
10917         if(isNaN(val)) {
10918             val = 0;
10919         }
10920         return val;
10921     }
10922 };/*
10923  * Based on:
10924  * Ext JS Library 1.1.1
10925  * Copyright(c) 2006-2007, Ext JS, LLC.
10926  *
10927  * Originally Released Under LGPL - original licence link has changed is not relivant.
10928  *
10929  * Fork - LGPL
10930  * <script type="text/javascript">
10931  */
10932
10933 /**
10934 * @class Roo.data.Record
10935  * Instances of this class encapsulate both record <em>definition</em> information, and record
10936  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10937  * to access Records cached in an {@link Roo.data.Store} object.<br>
10938  * <p>
10939  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10940  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10941  * objects.<br>
10942  * <p>
10943  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10944  * @constructor
10945  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10946  * {@link #create}. The parameters are the same.
10947  * @param {Array} data An associative Array of data values keyed by the field name.
10948  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10949  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10950  * not specified an integer id is generated.
10951  */
10952 Roo.data.Record = function(data, id){
10953     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10954     this.data = data;
10955 };
10956
10957 /**
10958  * Generate a constructor for a specific record layout.
10959  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10960  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10961  * Each field definition object may contain the following properties: <ul>
10962  * <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,
10963  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10964  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10965  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10966  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10967  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10968  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10969  * this may be omitted.</p></li>
10970  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10971  * <ul><li>auto (Default, implies no conversion)</li>
10972  * <li>string</li>
10973  * <li>int</li>
10974  * <li>float</li>
10975  * <li>boolean</li>
10976  * <li>date</li></ul></p></li>
10977  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10978  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10979  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10980  * by the Reader into an object that will be stored in the Record. It is passed the
10981  * following parameters:<ul>
10982  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10983  * </ul></p></li>
10984  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10985  * </ul>
10986  * <br>usage:<br><pre><code>
10987 var TopicRecord = Roo.data.Record.create(
10988     {name: 'title', mapping: 'topic_title'},
10989     {name: 'author', mapping: 'username'},
10990     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10991     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10992     {name: 'lastPoster', mapping: 'user2'},
10993     {name: 'excerpt', mapping: 'post_text'}
10994 );
10995
10996 var myNewRecord = new TopicRecord({
10997     title: 'Do my job please',
10998     author: 'noobie',
10999     totalPosts: 1,
11000     lastPost: new Date(),
11001     lastPoster: 'Animal',
11002     excerpt: 'No way dude!'
11003 });
11004 myStore.add(myNewRecord);
11005 </code></pre>
11006  * @method create
11007  * @static
11008  */
11009 Roo.data.Record.create = function(o){
11010     var f = function(){
11011         f.superclass.constructor.apply(this, arguments);
11012     };
11013     Roo.extend(f, Roo.data.Record);
11014     var p = f.prototype;
11015     p.fields = new Roo.util.MixedCollection(false, function(field){
11016         return field.name;
11017     });
11018     for(var i = 0, len = o.length; i < len; i++){
11019         p.fields.add(new Roo.data.Field(o[i]));
11020     }
11021     f.getField = function(name){
11022         return p.fields.get(name);  
11023     };
11024     return f;
11025 };
11026
11027 Roo.data.Record.AUTO_ID = 1000;
11028 Roo.data.Record.EDIT = 'edit';
11029 Roo.data.Record.REJECT = 'reject';
11030 Roo.data.Record.COMMIT = 'commit';
11031
11032 Roo.data.Record.prototype = {
11033     /**
11034      * Readonly flag - true if this record has been modified.
11035      * @type Boolean
11036      */
11037     dirty : false,
11038     editing : false,
11039     error: null,
11040     modified: null,
11041
11042     // private
11043     join : function(store){
11044         this.store = store;
11045     },
11046
11047     /**
11048      * Set the named field to the specified value.
11049      * @param {String} name The name of the field to set.
11050      * @param {Object} value The value to set the field to.
11051      */
11052     set : function(name, value){
11053         if(this.data[name] == value){
11054             return;
11055         }
11056         this.dirty = true;
11057         if(!this.modified){
11058             this.modified = {};
11059         }
11060         if(typeof this.modified[name] == 'undefined'){
11061             this.modified[name] = this.data[name];
11062         }
11063         this.data[name] = value;
11064         if(!this.editing && this.store){
11065             this.store.afterEdit(this);
11066         }       
11067     },
11068
11069     /**
11070      * Get the value of the named field.
11071      * @param {String} name The name of the field to get the value of.
11072      * @return {Object} The value of the field.
11073      */
11074     get : function(name){
11075         return this.data[name]; 
11076     },
11077
11078     // private
11079     beginEdit : function(){
11080         this.editing = true;
11081         this.modified = {}; 
11082     },
11083
11084     // private
11085     cancelEdit : function(){
11086         this.editing = false;
11087         delete this.modified;
11088     },
11089
11090     // private
11091     endEdit : function(){
11092         this.editing = false;
11093         if(this.dirty && this.store){
11094             this.store.afterEdit(this);
11095         }
11096     },
11097
11098     /**
11099      * Usually called by the {@link Roo.data.Store} which owns the Record.
11100      * Rejects all changes made to the Record since either creation, or the last commit operation.
11101      * Modified fields are reverted to their original values.
11102      * <p>
11103      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11104      * of reject operations.
11105      */
11106     reject : function(){
11107         var m = this.modified;
11108         for(var n in m){
11109             if(typeof m[n] != "function"){
11110                 this.data[n] = m[n];
11111             }
11112         }
11113         this.dirty = false;
11114         delete this.modified;
11115         this.editing = false;
11116         if(this.store){
11117             this.store.afterReject(this);
11118         }
11119     },
11120
11121     /**
11122      * Usually called by the {@link Roo.data.Store} which owns the Record.
11123      * Commits all changes made to the Record since either creation, or the last commit operation.
11124      * <p>
11125      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11126      * of commit operations.
11127      */
11128     commit : function(){
11129         this.dirty = false;
11130         delete this.modified;
11131         this.editing = false;
11132         if(this.store){
11133             this.store.afterCommit(this);
11134         }
11135     },
11136
11137     // private
11138     hasError : function(){
11139         return this.error != null;
11140     },
11141
11142     // private
11143     clearError : function(){
11144         this.error = null;
11145     },
11146
11147     /**
11148      * Creates a copy of this record.
11149      * @param {String} id (optional) A new record id if you don't want to use this record's id
11150      * @return {Record}
11151      */
11152     copy : function(newId) {
11153         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11154     }
11155 };/*
11156  * Based on:
11157  * Ext JS Library 1.1.1
11158  * Copyright(c) 2006-2007, Ext JS, LLC.
11159  *
11160  * Originally Released Under LGPL - original licence link has changed is not relivant.
11161  *
11162  * Fork - LGPL
11163  * <script type="text/javascript">
11164  */
11165
11166
11167
11168 /**
11169  * @class Roo.data.Store
11170  * @extends Roo.util.Observable
11171  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11172  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11173  * <p>
11174  * 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
11175  * has no knowledge of the format of the data returned by the Proxy.<br>
11176  * <p>
11177  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11178  * instances from the data object. These records are cached and made available through accessor functions.
11179  * @constructor
11180  * Creates a new Store.
11181  * @param {Object} config A config object containing the objects needed for the Store to access data,
11182  * and read the data into Records.
11183  */
11184 Roo.data.Store = function(config){
11185     this.data = new Roo.util.MixedCollection(false);
11186     this.data.getKey = function(o){
11187         return o.id;
11188     };
11189     this.baseParams = {};
11190     // private
11191     this.paramNames = {
11192         "start" : "start",
11193         "limit" : "limit",
11194         "sort" : "sort",
11195         "dir" : "dir",
11196         "multisort" : "_multisort"
11197     };
11198
11199     if(config && config.data){
11200         this.inlineData = config.data;
11201         delete config.data;
11202     }
11203
11204     Roo.apply(this, config);
11205     
11206     if(this.reader){ // reader passed
11207         this.reader = Roo.factory(this.reader, Roo.data);
11208         this.reader.xmodule = this.xmodule || false;
11209         if(!this.recordType){
11210             this.recordType = this.reader.recordType;
11211         }
11212         if(this.reader.onMetaChange){
11213             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11214         }
11215     }
11216
11217     if(this.recordType){
11218         this.fields = this.recordType.prototype.fields;
11219     }
11220     this.modified = [];
11221
11222     this.addEvents({
11223         /**
11224          * @event datachanged
11225          * Fires when the data cache has changed, and a widget which is using this Store
11226          * as a Record cache should refresh its view.
11227          * @param {Store} this
11228          */
11229         datachanged : true,
11230         /**
11231          * @event metachange
11232          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11233          * @param {Store} this
11234          * @param {Object} meta The JSON metadata
11235          */
11236         metachange : true,
11237         /**
11238          * @event add
11239          * Fires when Records have been added to the Store
11240          * @param {Store} this
11241          * @param {Roo.data.Record[]} records The array of Records added
11242          * @param {Number} index The index at which the record(s) were added
11243          */
11244         add : true,
11245         /**
11246          * @event remove
11247          * Fires when a Record has been removed from the Store
11248          * @param {Store} this
11249          * @param {Roo.data.Record} record The Record that was removed
11250          * @param {Number} index The index at which the record was removed
11251          */
11252         remove : true,
11253         /**
11254          * @event update
11255          * Fires when a Record has been updated
11256          * @param {Store} this
11257          * @param {Roo.data.Record} record The Record that was updated
11258          * @param {String} operation The update operation being performed.  Value may be one of:
11259          * <pre><code>
11260  Roo.data.Record.EDIT
11261  Roo.data.Record.REJECT
11262  Roo.data.Record.COMMIT
11263          * </code></pre>
11264          */
11265         update : true,
11266         /**
11267          * @event clear
11268          * Fires when the data cache has been cleared.
11269          * @param {Store} this
11270          */
11271         clear : true,
11272         /**
11273          * @event beforeload
11274          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11275          * the load action will be canceled.
11276          * @param {Store} this
11277          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11278          */
11279         beforeload : true,
11280         /**
11281          * @event beforeloadadd
11282          * Fires after a new set of Records has been loaded.
11283          * @param {Store} this
11284          * @param {Roo.data.Record[]} records The Records that were loaded
11285          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11286          */
11287         beforeloadadd : true,
11288         /**
11289          * @event load
11290          * Fires after a new set of Records has been loaded, before they are added to the store.
11291          * @param {Store} this
11292          * @param {Roo.data.Record[]} records The Records that were loaded
11293          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11294          * @params {Object} return from reader
11295          */
11296         load : true,
11297         /**
11298          * @event loadexception
11299          * Fires if an exception occurs in the Proxy during loading.
11300          * Called with the signature of the Proxy's "loadexception" event.
11301          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11302          * 
11303          * @param {Proxy} 
11304          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11305          * @param {Object} load options 
11306          * @param {Object} jsonData from your request (normally this contains the Exception)
11307          */
11308         loadexception : true
11309     });
11310     
11311     if(this.proxy){
11312         this.proxy = Roo.factory(this.proxy, Roo.data);
11313         this.proxy.xmodule = this.xmodule || false;
11314         this.relayEvents(this.proxy,  ["loadexception"]);
11315     }
11316     this.sortToggle = {};
11317     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11318
11319     Roo.data.Store.superclass.constructor.call(this);
11320
11321     if(this.inlineData){
11322         this.loadData(this.inlineData);
11323         delete this.inlineData;
11324     }
11325 };
11326
11327 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11328      /**
11329     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11330     * without a remote query - used by combo/forms at present.
11331     */
11332     
11333     /**
11334     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11335     */
11336     /**
11337     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11338     */
11339     /**
11340     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11341     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11342     */
11343     /**
11344     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11345     * on any HTTP request
11346     */
11347     /**
11348     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11349     */
11350     /**
11351     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11352     */
11353     multiSort: false,
11354     /**
11355     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11356     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11357     */
11358     remoteSort : false,
11359
11360     /**
11361     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11362      * loaded or when a record is removed. (defaults to false).
11363     */
11364     pruneModifiedRecords : false,
11365
11366     // private
11367     lastOptions : null,
11368
11369     /**
11370      * Add Records to the Store and fires the add event.
11371      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11372      */
11373     add : function(records){
11374         records = [].concat(records);
11375         for(var i = 0, len = records.length; i < len; i++){
11376             records[i].join(this);
11377         }
11378         var index = this.data.length;
11379         this.data.addAll(records);
11380         this.fireEvent("add", this, records, index);
11381     },
11382
11383     /**
11384      * Remove a Record from the Store and fires the remove event.
11385      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11386      */
11387     remove : function(record){
11388         var index = this.data.indexOf(record);
11389         this.data.removeAt(index);
11390  
11391         if(this.pruneModifiedRecords){
11392             this.modified.remove(record);
11393         }
11394         this.fireEvent("remove", this, record, index);
11395     },
11396
11397     /**
11398      * Remove all Records from the Store and fires the clear event.
11399      */
11400     removeAll : function(){
11401         this.data.clear();
11402         if(this.pruneModifiedRecords){
11403             this.modified = [];
11404         }
11405         this.fireEvent("clear", this);
11406     },
11407
11408     /**
11409      * Inserts Records to the Store at the given index and fires the add event.
11410      * @param {Number} index The start index at which to insert the passed Records.
11411      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11412      */
11413     insert : function(index, records){
11414         records = [].concat(records);
11415         for(var i = 0, len = records.length; i < len; i++){
11416             this.data.insert(index, records[i]);
11417             records[i].join(this);
11418         }
11419         this.fireEvent("add", this, records, index);
11420     },
11421
11422     /**
11423      * Get the index within the cache of the passed Record.
11424      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11425      * @return {Number} The index of the passed Record. Returns -1 if not found.
11426      */
11427     indexOf : function(record){
11428         return this.data.indexOf(record);
11429     },
11430
11431     /**
11432      * Get the index within the cache of the Record with the passed id.
11433      * @param {String} id The id of the Record to find.
11434      * @return {Number} The index of the Record. Returns -1 if not found.
11435      */
11436     indexOfId : function(id){
11437         return this.data.indexOfKey(id);
11438     },
11439
11440     /**
11441      * Get the Record with the specified id.
11442      * @param {String} id The id of the Record to find.
11443      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11444      */
11445     getById : function(id){
11446         return this.data.key(id);
11447     },
11448
11449     /**
11450      * Get the Record at the specified index.
11451      * @param {Number} index The index of the Record to find.
11452      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11453      */
11454     getAt : function(index){
11455         return this.data.itemAt(index);
11456     },
11457
11458     /**
11459      * Returns a range of Records between specified indices.
11460      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11461      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11462      * @return {Roo.data.Record[]} An array of Records
11463      */
11464     getRange : function(start, end){
11465         return this.data.getRange(start, end);
11466     },
11467
11468     // private
11469     storeOptions : function(o){
11470         o = Roo.apply({}, o);
11471         delete o.callback;
11472         delete o.scope;
11473         this.lastOptions = o;
11474     },
11475
11476     /**
11477      * Loads the Record cache from the configured Proxy using the configured Reader.
11478      * <p>
11479      * If using remote paging, then the first load call must specify the <em>start</em>
11480      * and <em>limit</em> properties in the options.params property to establish the initial
11481      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11482      * <p>
11483      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11484      * and this call will return before the new data has been loaded. Perform any post-processing
11485      * in a callback function, or in a "load" event handler.</strong>
11486      * <p>
11487      * @param {Object} options An object containing properties which control loading options:<ul>
11488      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11489      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11490      * passed the following arguments:<ul>
11491      * <li>r : Roo.data.Record[]</li>
11492      * <li>options: Options object from the load call</li>
11493      * <li>success: Boolean success indicator</li></ul></li>
11494      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11495      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11496      * </ul>
11497      */
11498     load : function(options){
11499         options = options || {};
11500         if(this.fireEvent("beforeload", this, options) !== false){
11501             this.storeOptions(options);
11502             var p = Roo.apply(options.params || {}, this.baseParams);
11503             // if meta was not loaded from remote source.. try requesting it.
11504             if (!this.reader.metaFromRemote) {
11505                 p._requestMeta = 1;
11506             }
11507             if(this.sortInfo && this.remoteSort){
11508                 var pn = this.paramNames;
11509                 p[pn["sort"]] = this.sortInfo.field;
11510                 p[pn["dir"]] = this.sortInfo.direction;
11511             }
11512             if (this.multiSort) {
11513                 var pn = this.paramNames;
11514                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11515             }
11516             
11517             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11518         }
11519     },
11520
11521     /**
11522      * Reloads the Record cache from the configured Proxy using the configured Reader and
11523      * the options from the last load operation performed.
11524      * @param {Object} options (optional) An object containing properties which may override the options
11525      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11526      * the most recently used options are reused).
11527      */
11528     reload : function(options){
11529         this.load(Roo.applyIf(options||{}, this.lastOptions));
11530     },
11531
11532     // private
11533     // Called as a callback by the Reader during a load operation.
11534     loadRecords : function(o, options, success){
11535         if(!o || success === false){
11536             if(success !== false){
11537                 this.fireEvent("load", this, [], options, o);
11538             }
11539             if(options.callback){
11540                 options.callback.call(options.scope || this, [], options, false);
11541             }
11542             return;
11543         }
11544         // if data returned failure - throw an exception.
11545         if (o.success === false) {
11546             // show a message if no listener is registered.
11547             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11548                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11549             }
11550             // loadmask wil be hooked into this..
11551             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11552             return;
11553         }
11554         var r = o.records, t = o.totalRecords || r.length;
11555         
11556         this.fireEvent("beforeloadadd", this, r, options, o);
11557         
11558         if(!options || options.add !== true){
11559             if(this.pruneModifiedRecords){
11560                 this.modified = [];
11561             }
11562             for(var i = 0, len = r.length; i < len; i++){
11563                 r[i].join(this);
11564             }
11565             if(this.snapshot){
11566                 this.data = this.snapshot;
11567                 delete this.snapshot;
11568             }
11569             this.data.clear();
11570             this.data.addAll(r);
11571             this.totalLength = t;
11572             this.applySort();
11573             this.fireEvent("datachanged", this);
11574         }else{
11575             this.totalLength = Math.max(t, this.data.length+r.length);
11576             this.add(r);
11577         }
11578         
11579         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11580                 
11581             var e = new Roo.data.Record({});
11582
11583             e.set(this.parent.displayField, this.parent.emptyTitle);
11584             e.set(this.parent.valueField, '');
11585
11586             this.insert(0, e);
11587         }
11588             
11589         this.fireEvent("load", this, r, options, o);
11590         if(options.callback){
11591             options.callback.call(options.scope || this, r, options, true);
11592         }
11593     },
11594
11595
11596     /**
11597      * Loads data from a passed data block. A Reader which understands the format of the data
11598      * must have been configured in the constructor.
11599      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11600      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11601      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11602      */
11603     loadData : function(o, append){
11604         var r = this.reader.readRecords(o);
11605         this.loadRecords(r, {add: append}, true);
11606     },
11607
11608     /**
11609      * Gets the number of cached records.
11610      * <p>
11611      * <em>If using paging, this may not be the total size of the dataset. If the data object
11612      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11613      * the data set size</em>
11614      */
11615     getCount : function(){
11616         return this.data.length || 0;
11617     },
11618
11619     /**
11620      * Gets the total number of records in the dataset as returned by the server.
11621      * <p>
11622      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11623      * the dataset size</em>
11624      */
11625     getTotalCount : function(){
11626         return this.totalLength || 0;
11627     },
11628
11629     /**
11630      * Returns the sort state of the Store as an object with two properties:
11631      * <pre><code>
11632  field {String} The name of the field by which the Records are sorted
11633  direction {String} The sort order, "ASC" or "DESC"
11634      * </code></pre>
11635      */
11636     getSortState : function(){
11637         return this.sortInfo;
11638     },
11639
11640     // private
11641     applySort : function(){
11642         if(this.sortInfo && !this.remoteSort){
11643             var s = this.sortInfo, f = s.field;
11644             var st = this.fields.get(f).sortType;
11645             var fn = function(r1, r2){
11646                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11647                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11648             };
11649             this.data.sort(s.direction, fn);
11650             if(this.snapshot && this.snapshot != this.data){
11651                 this.snapshot.sort(s.direction, fn);
11652             }
11653         }
11654     },
11655
11656     /**
11657      * Sets the default sort column and order to be used by the next load operation.
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     setDefaultSort : function(field, dir){
11662         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11663     },
11664
11665     /**
11666      * Sort the Records.
11667      * If remote sorting is used, the sort is performed on the server, and the cache is
11668      * reloaded. If local sorting is used, the cache is sorted internally.
11669      * @param {String} fieldName The name of the field to sort by.
11670      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11671      */
11672     sort : function(fieldName, dir){
11673         var f = this.fields.get(fieldName);
11674         if(!dir){
11675             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11676             
11677             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11678                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11679             }else{
11680                 dir = f.sortDir;
11681             }
11682         }
11683         this.sortToggle[f.name] = dir;
11684         this.sortInfo = {field: f.name, direction: dir};
11685         if(!this.remoteSort){
11686             this.applySort();
11687             this.fireEvent("datachanged", this);
11688         }else{
11689             this.load(this.lastOptions);
11690         }
11691     },
11692
11693     /**
11694      * Calls the specified function for each of the Records in the cache.
11695      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11696      * Returning <em>false</em> aborts and exits the iteration.
11697      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11698      */
11699     each : function(fn, scope){
11700         this.data.each(fn, scope);
11701     },
11702
11703     /**
11704      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11705      * (e.g., during paging).
11706      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11707      */
11708     getModifiedRecords : function(){
11709         return this.modified;
11710     },
11711
11712     // private
11713     createFilterFn : function(property, value, anyMatch){
11714         if(!value.exec){ // not a regex
11715             value = String(value);
11716             if(value.length == 0){
11717                 return false;
11718             }
11719             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11720         }
11721         return function(r){
11722             return value.test(r.data[property]);
11723         };
11724     },
11725
11726     /**
11727      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11728      * @param {String} property A field on your records
11729      * @param {Number} start The record index to start at (defaults to 0)
11730      * @param {Number} end The last record index to include (defaults to length - 1)
11731      * @return {Number} The sum
11732      */
11733     sum : function(property, start, end){
11734         var rs = this.data.items, v = 0;
11735         start = start || 0;
11736         end = (end || end === 0) ? end : rs.length-1;
11737
11738         for(var i = start; i <= end; i++){
11739             v += (rs[i].data[property] || 0);
11740         }
11741         return v;
11742     },
11743
11744     /**
11745      * Filter the records by a specified property.
11746      * @param {String} field A field on your records
11747      * @param {String/RegExp} value Either a string that the field
11748      * should start with or a RegExp to test against the field
11749      * @param {Boolean} anyMatch True to match any part not just the beginning
11750      */
11751     filter : function(property, value, anyMatch){
11752         var fn = this.createFilterFn(property, value, anyMatch);
11753         return fn ? this.filterBy(fn) : this.clearFilter();
11754     },
11755
11756     /**
11757      * Filter by a function. The specified function will be called with each
11758      * record in this data source. If the function returns true the record is included,
11759      * otherwise it is filtered.
11760      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11761      * @param {Object} scope (optional) The scope of the function (defaults to this)
11762      */
11763     filterBy : function(fn, scope){
11764         this.snapshot = this.snapshot || this.data;
11765         this.data = this.queryBy(fn, scope||this);
11766         this.fireEvent("datachanged", this);
11767     },
11768
11769     /**
11770      * Query the records by a specified property.
11771      * @param {String} field A field on your records
11772      * @param {String/RegExp} value Either a string that the field
11773      * should start with or a RegExp to test against the field
11774      * @param {Boolean} anyMatch True to match any part not just the beginning
11775      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11776      */
11777     query : function(property, value, anyMatch){
11778         var fn = this.createFilterFn(property, value, anyMatch);
11779         return fn ? this.queryBy(fn) : this.data.clone();
11780     },
11781
11782     /**
11783      * Query by a function. The specified function will be called with each
11784      * record in this data source. If the function returns true the record is included
11785      * in the results.
11786      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11787      * @param {Object} scope (optional) The scope of the function (defaults to this)
11788       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11789      **/
11790     queryBy : function(fn, scope){
11791         var data = this.snapshot || this.data;
11792         return data.filterBy(fn, scope||this);
11793     },
11794
11795     /**
11796      * Collects unique values for a particular dataIndex from this store.
11797      * @param {String} dataIndex The property to collect
11798      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11799      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11800      * @return {Array} An array of the unique values
11801      **/
11802     collect : function(dataIndex, allowNull, bypassFilter){
11803         var d = (bypassFilter === true && this.snapshot) ?
11804                 this.snapshot.items : this.data.items;
11805         var v, sv, r = [], l = {};
11806         for(var i = 0, len = d.length; i < len; i++){
11807             v = d[i].data[dataIndex];
11808             sv = String(v);
11809             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11810                 l[sv] = true;
11811                 r[r.length] = v;
11812             }
11813         }
11814         return r;
11815     },
11816
11817     /**
11818      * Revert to a view of the Record cache with no filtering applied.
11819      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11820      */
11821     clearFilter : function(suppressEvent){
11822         if(this.snapshot && this.snapshot != this.data){
11823             this.data = this.snapshot;
11824             delete this.snapshot;
11825             if(suppressEvent !== true){
11826                 this.fireEvent("datachanged", this);
11827             }
11828         }
11829     },
11830
11831     // private
11832     afterEdit : function(record){
11833         if(this.modified.indexOf(record) == -1){
11834             this.modified.push(record);
11835         }
11836         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11837     },
11838     
11839     // private
11840     afterReject : function(record){
11841         this.modified.remove(record);
11842         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11843     },
11844
11845     // private
11846     afterCommit : function(record){
11847         this.modified.remove(record);
11848         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11849     },
11850
11851     /**
11852      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11853      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11854      */
11855     commitChanges : 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].commit();
11860         }
11861     },
11862
11863     /**
11864      * Cancel outstanding changes on all changed records.
11865      */
11866     rejectChanges : function(){
11867         var m = this.modified.slice(0);
11868         this.modified = [];
11869         for(var i = 0, len = m.length; i < len; i++){
11870             m[i].reject();
11871         }
11872     },
11873
11874     onMetaChange : function(meta, rtype, o){
11875         this.recordType = rtype;
11876         this.fields = rtype.prototype.fields;
11877         delete this.snapshot;
11878         this.sortInfo = meta.sortInfo || this.sortInfo;
11879         this.modified = [];
11880         this.fireEvent('metachange', this, this.reader.meta);
11881     },
11882     
11883     moveIndex : function(data, type)
11884     {
11885         var index = this.indexOf(data);
11886         
11887         var newIndex = index + type;
11888         
11889         this.remove(data);
11890         
11891         this.insert(newIndex, data);
11892         
11893     }
11894 });/*
11895  * Based on:
11896  * Ext JS Library 1.1.1
11897  * Copyright(c) 2006-2007, Ext JS, LLC.
11898  *
11899  * Originally Released Under LGPL - original licence link has changed is not relivant.
11900  *
11901  * Fork - LGPL
11902  * <script type="text/javascript">
11903  */
11904
11905 /**
11906  * @class Roo.data.SimpleStore
11907  * @extends Roo.data.Store
11908  * Small helper class to make creating Stores from Array data easier.
11909  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11910  * @cfg {Array} fields An array of field definition objects, or field name strings.
11911  * @cfg {Array} data The multi-dimensional array of data
11912  * @constructor
11913  * @param {Object} config
11914  */
11915 Roo.data.SimpleStore = function(config){
11916     Roo.data.SimpleStore.superclass.constructor.call(this, {
11917         isLocal : true,
11918         reader: new Roo.data.ArrayReader({
11919                 id: config.id
11920             },
11921             Roo.data.Record.create(config.fields)
11922         ),
11923         proxy : new Roo.data.MemoryProxy(config.data)
11924     });
11925     this.load();
11926 };
11927 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11928  * Based on:
11929  * Ext JS Library 1.1.1
11930  * Copyright(c) 2006-2007, Ext JS, LLC.
11931  *
11932  * Originally Released Under LGPL - original licence link has changed is not relivant.
11933  *
11934  * Fork - LGPL
11935  * <script type="text/javascript">
11936  */
11937
11938 /**
11939 /**
11940  * @extends Roo.data.Store
11941  * @class Roo.data.JsonStore
11942  * Small helper class to make creating Stores for JSON data easier. <br/>
11943 <pre><code>
11944 var store = new Roo.data.JsonStore({
11945     url: 'get-images.php',
11946     root: 'images',
11947     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11948 });
11949 </code></pre>
11950  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11951  * JsonReader and HttpProxy (unless inline data is provided).</b>
11952  * @cfg {Array} fields An array of field definition objects, or field name strings.
11953  * @constructor
11954  * @param {Object} config
11955  */
11956 Roo.data.JsonStore = function(c){
11957     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11958         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11959         reader: new Roo.data.JsonReader(c, c.fields)
11960     }));
11961 };
11962 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11963  * Based on:
11964  * Ext JS Library 1.1.1
11965  * Copyright(c) 2006-2007, Ext JS, LLC.
11966  *
11967  * Originally Released Under LGPL - original licence link has changed is not relivant.
11968  *
11969  * Fork - LGPL
11970  * <script type="text/javascript">
11971  */
11972
11973  
11974 Roo.data.Field = function(config){
11975     if(typeof config == "string"){
11976         config = {name: config};
11977     }
11978     Roo.apply(this, config);
11979     
11980     if(!this.type){
11981         this.type = "auto";
11982     }
11983     
11984     var st = Roo.data.SortTypes;
11985     // named sortTypes are supported, here we look them up
11986     if(typeof this.sortType == "string"){
11987         this.sortType = st[this.sortType];
11988     }
11989     
11990     // set default sortType for strings and dates
11991     if(!this.sortType){
11992         switch(this.type){
11993             case "string":
11994                 this.sortType = st.asUCString;
11995                 break;
11996             case "date":
11997                 this.sortType = st.asDate;
11998                 break;
11999             default:
12000                 this.sortType = st.none;
12001         }
12002     }
12003
12004     // define once
12005     var stripRe = /[\$,%]/g;
12006
12007     // prebuilt conversion function for this field, instead of
12008     // switching every time we're reading a value
12009     if(!this.convert){
12010         var cv, dateFormat = this.dateFormat;
12011         switch(this.type){
12012             case "":
12013             case "auto":
12014             case undefined:
12015                 cv = function(v){ return v; };
12016                 break;
12017             case "string":
12018                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12019                 break;
12020             case "int":
12021                 cv = function(v){
12022                     return v !== undefined && v !== null && v !== '' ?
12023                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12024                     };
12025                 break;
12026             case "float":
12027                 cv = function(v){
12028                     return v !== undefined && v !== null && v !== '' ?
12029                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12030                     };
12031                 break;
12032             case "bool":
12033             case "boolean":
12034                 cv = function(v){ return v === true || v === "true" || v == 1; };
12035                 break;
12036             case "date":
12037                 cv = function(v){
12038                     if(!v){
12039                         return '';
12040                     }
12041                     if(v instanceof Date){
12042                         return v;
12043                     }
12044                     if(dateFormat){
12045                         if(dateFormat == "timestamp"){
12046                             return new Date(v*1000);
12047                         }
12048                         return Date.parseDate(v, dateFormat);
12049                     }
12050                     var parsed = Date.parse(v);
12051                     return parsed ? new Date(parsed) : null;
12052                 };
12053              break;
12054             
12055         }
12056         this.convert = cv;
12057     }
12058 };
12059
12060 Roo.data.Field.prototype = {
12061     dateFormat: null,
12062     defaultValue: "",
12063     mapping: null,
12064     sortType : null,
12065     sortDir : "ASC"
12066 };/*
12067  * Based on:
12068  * Ext JS Library 1.1.1
12069  * Copyright(c) 2006-2007, Ext JS, LLC.
12070  *
12071  * Originally Released Under LGPL - original licence link has changed is not relivant.
12072  *
12073  * Fork - LGPL
12074  * <script type="text/javascript">
12075  */
12076  
12077 // Base class for reading structured data from a data source.  This class is intended to be
12078 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12079
12080 /**
12081  * @class Roo.data.DataReader
12082  * Base class for reading structured data from a data source.  This class is intended to be
12083  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12084  */
12085
12086 Roo.data.DataReader = function(meta, recordType){
12087     
12088     this.meta = meta;
12089     
12090     this.recordType = recordType instanceof Array ? 
12091         Roo.data.Record.create(recordType) : recordType;
12092 };
12093
12094 Roo.data.DataReader.prototype = {
12095      /**
12096      * Create an empty record
12097      * @param {Object} data (optional) - overlay some values
12098      * @return {Roo.data.Record} record created.
12099      */
12100     newRow :  function(d) {
12101         var da =  {};
12102         this.recordType.prototype.fields.each(function(c) {
12103             switch( c.type) {
12104                 case 'int' : da[c.name] = 0; break;
12105                 case 'date' : da[c.name] = new Date(); break;
12106                 case 'float' : da[c.name] = 0.0; break;
12107                 case 'boolean' : da[c.name] = false; break;
12108                 default : da[c.name] = ""; break;
12109             }
12110             
12111         });
12112         return new this.recordType(Roo.apply(da, d));
12113     }
12114     
12115 };/*
12116  * Based on:
12117  * Ext JS Library 1.1.1
12118  * Copyright(c) 2006-2007, Ext JS, LLC.
12119  *
12120  * Originally Released Under LGPL - original licence link has changed is not relivant.
12121  *
12122  * Fork - LGPL
12123  * <script type="text/javascript">
12124  */
12125
12126 /**
12127  * @class Roo.data.DataProxy
12128  * @extends Roo.data.Observable
12129  * This class is an abstract base class for implementations which provide retrieval of
12130  * unformatted data objects.<br>
12131  * <p>
12132  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12133  * (of the appropriate type which knows how to parse the data object) to provide a block of
12134  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12135  * <p>
12136  * Custom implementations must implement the load method as described in
12137  * {@link Roo.data.HttpProxy#load}.
12138  */
12139 Roo.data.DataProxy = function(){
12140     this.addEvents({
12141         /**
12142          * @event beforeload
12143          * Fires before a network request is made to retrieve a data object.
12144          * @param {Object} This DataProxy object.
12145          * @param {Object} params The params parameter to the load function.
12146          */
12147         beforeload : true,
12148         /**
12149          * @event load
12150          * Fires before the load method's callback is called.
12151          * @param {Object} This DataProxy object.
12152          * @param {Object} o The data object.
12153          * @param {Object} arg The callback argument object passed to the load function.
12154          */
12155         load : true,
12156         /**
12157          * @event loadexception
12158          * Fires if an Exception occurs during data retrieval.
12159          * @param {Object} This DataProxy object.
12160          * @param {Object} o The data object.
12161          * @param {Object} arg The callback argument object passed to the load function.
12162          * @param {Object} e The Exception.
12163          */
12164         loadexception : true
12165     });
12166     Roo.data.DataProxy.superclass.constructor.call(this);
12167 };
12168
12169 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12170
12171     /**
12172      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12173      */
12174 /*
12175  * Based on:
12176  * Ext JS Library 1.1.1
12177  * Copyright(c) 2006-2007, Ext JS, LLC.
12178  *
12179  * Originally Released Under LGPL - original licence link has changed is not relivant.
12180  *
12181  * Fork - LGPL
12182  * <script type="text/javascript">
12183  */
12184 /**
12185  * @class Roo.data.MemoryProxy
12186  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12187  * to the Reader when its load method is called.
12188  * @constructor
12189  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12190  */
12191 Roo.data.MemoryProxy = function(data){
12192     if (data.data) {
12193         data = data.data;
12194     }
12195     Roo.data.MemoryProxy.superclass.constructor.call(this);
12196     this.data = data;
12197 };
12198
12199 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12200     
12201     /**
12202      * Load data from the requested source (in this case an in-memory
12203      * data object passed to the constructor), read the data object into
12204      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12205      * process that block using the passed callback.
12206      * @param {Object} params This parameter is not used by the MemoryProxy class.
12207      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12208      * object into a block of Roo.data.Records.
12209      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12210      * The function must be passed <ul>
12211      * <li>The Record block object</li>
12212      * <li>The "arg" argument from the load function</li>
12213      * <li>A boolean success indicator</li>
12214      * </ul>
12215      * @param {Object} scope The scope in which to call the callback
12216      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12217      */
12218     load : function(params, reader, callback, scope, arg){
12219         params = params || {};
12220         var result;
12221         try {
12222             result = reader.readRecords(this.data);
12223         }catch(e){
12224             this.fireEvent("loadexception", this, arg, null, e);
12225             callback.call(scope, null, arg, false);
12226             return;
12227         }
12228         callback.call(scope, result, arg, true);
12229     },
12230     
12231     // private
12232     update : function(params, records){
12233         
12234     }
12235 });/*
12236  * Based on:
12237  * Ext JS Library 1.1.1
12238  * Copyright(c) 2006-2007, Ext JS, LLC.
12239  *
12240  * Originally Released Under LGPL - original licence link has changed is not relivant.
12241  *
12242  * Fork - LGPL
12243  * <script type="text/javascript">
12244  */
12245 /**
12246  * @class Roo.data.HttpProxy
12247  * @extends Roo.data.DataProxy
12248  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12249  * configured to reference a certain URL.<br><br>
12250  * <p>
12251  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12252  * from which the running page was served.<br><br>
12253  * <p>
12254  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12255  * <p>
12256  * Be aware that to enable the browser to parse an XML document, the server must set
12257  * the Content-Type header in the HTTP response to "text/xml".
12258  * @constructor
12259  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12260  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12261  * will be used to make the request.
12262  */
12263 Roo.data.HttpProxy = function(conn){
12264     Roo.data.HttpProxy.superclass.constructor.call(this);
12265     // is conn a conn config or a real conn?
12266     this.conn = conn;
12267     this.useAjax = !conn || !conn.events;
12268   
12269 };
12270
12271 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12272     // thse are take from connection...
12273     
12274     /**
12275      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12276      */
12277     /**
12278      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12279      * extra parameters to each request made by this object. (defaults to undefined)
12280      */
12281     /**
12282      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12283      *  to each request made by this object. (defaults to undefined)
12284      */
12285     /**
12286      * @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)
12287      */
12288     /**
12289      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12290      */
12291      /**
12292      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12293      * @type Boolean
12294      */
12295   
12296
12297     /**
12298      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12299      * @type Boolean
12300      */
12301     /**
12302      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12303      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12304      * a finer-grained basis than the DataProxy events.
12305      */
12306     getConnection : function(){
12307         return this.useAjax ? Roo.Ajax : this.conn;
12308     },
12309
12310     /**
12311      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12312      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12313      * process that block using the passed callback.
12314      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12315      * for the request to the remote server.
12316      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12317      * object into a block of Roo.data.Records.
12318      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12319      * The function must be passed <ul>
12320      * <li>The Record block object</li>
12321      * <li>The "arg" argument from the load function</li>
12322      * <li>A boolean success indicator</li>
12323      * </ul>
12324      * @param {Object} scope The scope in which to call the callback
12325      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12326      */
12327     load : function(params, reader, callback, scope, arg){
12328         if(this.fireEvent("beforeload", this, params) !== false){
12329             var  o = {
12330                 params : params || {},
12331                 request: {
12332                     callback : callback,
12333                     scope : scope,
12334                     arg : arg
12335                 },
12336                 reader: reader,
12337                 callback : this.loadResponse,
12338                 scope: this
12339             };
12340             if(this.useAjax){
12341                 Roo.applyIf(o, this.conn);
12342                 if(this.activeRequest){
12343                     Roo.Ajax.abort(this.activeRequest);
12344                 }
12345                 this.activeRequest = Roo.Ajax.request(o);
12346             }else{
12347                 this.conn.request(o);
12348             }
12349         }else{
12350             callback.call(scope||this, null, arg, false);
12351         }
12352     },
12353
12354     // private
12355     loadResponse : function(o, success, response){
12356         delete this.activeRequest;
12357         if(!success){
12358             this.fireEvent("loadexception", this, o, response);
12359             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12360             return;
12361         }
12362         var result;
12363         try {
12364             result = o.reader.read(response);
12365         }catch(e){
12366             this.fireEvent("loadexception", this, o, response, e);
12367             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12368             return;
12369         }
12370         
12371         this.fireEvent("load", this, o, o.request.arg);
12372         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12373     },
12374
12375     // private
12376     update : function(dataSet){
12377
12378     },
12379
12380     // private
12381     updateResponse : function(dataSet){
12382
12383     }
12384 });/*
12385  * Based on:
12386  * Ext JS Library 1.1.1
12387  * Copyright(c) 2006-2007, Ext JS, LLC.
12388  *
12389  * Originally Released Under LGPL - original licence link has changed is not relivant.
12390  *
12391  * Fork - LGPL
12392  * <script type="text/javascript">
12393  */
12394
12395 /**
12396  * @class Roo.data.ScriptTagProxy
12397  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12398  * other than the originating domain of the running page.<br><br>
12399  * <p>
12400  * <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
12401  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12402  * <p>
12403  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12404  * source code that is used as the source inside a &lt;script> tag.<br><br>
12405  * <p>
12406  * In order for the browser to process the returned data, the server must wrap the data object
12407  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12408  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12409  * depending on whether the callback name was passed:
12410  * <p>
12411  * <pre><code>
12412 boolean scriptTag = false;
12413 String cb = request.getParameter("callback");
12414 if (cb != null) {
12415     scriptTag = true;
12416     response.setContentType("text/javascript");
12417 } else {
12418     response.setContentType("application/x-json");
12419 }
12420 Writer out = response.getWriter();
12421 if (scriptTag) {
12422     out.write(cb + "(");
12423 }
12424 out.print(dataBlock.toJsonString());
12425 if (scriptTag) {
12426     out.write(");");
12427 }
12428 </pre></code>
12429  *
12430  * @constructor
12431  * @param {Object} config A configuration object.
12432  */
12433 Roo.data.ScriptTagProxy = function(config){
12434     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12435     Roo.apply(this, config);
12436     this.head = document.getElementsByTagName("head")[0];
12437 };
12438
12439 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12440
12441 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12442     /**
12443      * @cfg {String} url The URL from which to request the data object.
12444      */
12445     /**
12446      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12447      */
12448     timeout : 30000,
12449     /**
12450      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12451      * the server the name of the callback function set up by the load call to process the returned data object.
12452      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12453      * javascript output which calls this named function passing the data object as its only parameter.
12454      */
12455     callbackParam : "callback",
12456     /**
12457      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12458      * name to the request.
12459      */
12460     nocache : true,
12461
12462     /**
12463      * Load data from the configured URL, read the data object into
12464      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12465      * process that block using the passed callback.
12466      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12467      * for the request to the remote server.
12468      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12469      * object into a block of Roo.data.Records.
12470      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12471      * The function must be passed <ul>
12472      * <li>The Record block object</li>
12473      * <li>The "arg" argument from the load function</li>
12474      * <li>A boolean success indicator</li>
12475      * </ul>
12476      * @param {Object} scope The scope in which to call the callback
12477      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12478      */
12479     load : function(params, reader, callback, scope, arg){
12480         if(this.fireEvent("beforeload", this, params) !== false){
12481
12482             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12483
12484             var url = this.url;
12485             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12486             if(this.nocache){
12487                 url += "&_dc=" + (new Date().getTime());
12488             }
12489             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12490             var trans = {
12491                 id : transId,
12492                 cb : "stcCallback"+transId,
12493                 scriptId : "stcScript"+transId,
12494                 params : params,
12495                 arg : arg,
12496                 url : url,
12497                 callback : callback,
12498                 scope : scope,
12499                 reader : reader
12500             };
12501             var conn = this;
12502
12503             window[trans.cb] = function(o){
12504                 conn.handleResponse(o, trans);
12505             };
12506
12507             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12508
12509             if(this.autoAbort !== false){
12510                 this.abort();
12511             }
12512
12513             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12514
12515             var script = document.createElement("script");
12516             script.setAttribute("src", url);
12517             script.setAttribute("type", "text/javascript");
12518             script.setAttribute("id", trans.scriptId);
12519             this.head.appendChild(script);
12520
12521             this.trans = trans;
12522         }else{
12523             callback.call(scope||this, null, arg, false);
12524         }
12525     },
12526
12527     // private
12528     isLoading : function(){
12529         return this.trans ? true : false;
12530     },
12531
12532     /**
12533      * Abort the current server request.
12534      */
12535     abort : function(){
12536         if(this.isLoading()){
12537             this.destroyTrans(this.trans);
12538         }
12539     },
12540
12541     // private
12542     destroyTrans : function(trans, isLoaded){
12543         this.head.removeChild(document.getElementById(trans.scriptId));
12544         clearTimeout(trans.timeoutId);
12545         if(isLoaded){
12546             window[trans.cb] = undefined;
12547             try{
12548                 delete window[trans.cb];
12549             }catch(e){}
12550         }else{
12551             // if hasn't been loaded, wait for load to remove it to prevent script error
12552             window[trans.cb] = function(){
12553                 window[trans.cb] = undefined;
12554                 try{
12555                     delete window[trans.cb];
12556                 }catch(e){}
12557             };
12558         }
12559     },
12560
12561     // private
12562     handleResponse : function(o, trans){
12563         this.trans = false;
12564         this.destroyTrans(trans, true);
12565         var result;
12566         try {
12567             result = trans.reader.readRecords(o);
12568         }catch(e){
12569             this.fireEvent("loadexception", this, o, trans.arg, e);
12570             trans.callback.call(trans.scope||window, null, trans.arg, false);
12571             return;
12572         }
12573         this.fireEvent("load", this, o, trans.arg);
12574         trans.callback.call(trans.scope||window, result, trans.arg, true);
12575     },
12576
12577     // private
12578     handleFailure : function(trans){
12579         this.trans = false;
12580         this.destroyTrans(trans, false);
12581         this.fireEvent("loadexception", this, null, trans.arg);
12582         trans.callback.call(trans.scope||window, null, trans.arg, false);
12583     }
12584 });/*
12585  * Based on:
12586  * Ext JS Library 1.1.1
12587  * Copyright(c) 2006-2007, Ext JS, LLC.
12588  *
12589  * Originally Released Under LGPL - original licence link has changed is not relivant.
12590  *
12591  * Fork - LGPL
12592  * <script type="text/javascript">
12593  */
12594
12595 /**
12596  * @class Roo.data.JsonReader
12597  * @extends Roo.data.DataReader
12598  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12599  * based on mappings in a provided Roo.data.Record constructor.
12600  * 
12601  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12602  * in the reply previously. 
12603  * 
12604  * <p>
12605  * Example code:
12606  * <pre><code>
12607 var RecordDef = Roo.data.Record.create([
12608     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12609     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12610 ]);
12611 var myReader = new Roo.data.JsonReader({
12612     totalProperty: "results",    // The property which contains the total dataset size (optional)
12613     root: "rows",                // The property which contains an Array of row objects
12614     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12615 }, RecordDef);
12616 </code></pre>
12617  * <p>
12618  * This would consume a JSON file like this:
12619  * <pre><code>
12620 { 'results': 2, 'rows': [
12621     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12622     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12623 }
12624 </code></pre>
12625  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12626  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12627  * paged from the remote server.
12628  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12629  * @cfg {String} root name of the property which contains the Array of row objects.
12630  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12631  * @cfg {Array} fields Array of field definition objects
12632  * @constructor
12633  * Create a new JsonReader
12634  * @param {Object} meta Metadata configuration options
12635  * @param {Object} recordType Either an Array of field definition objects,
12636  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12637  */
12638 Roo.data.JsonReader = function(meta, recordType){
12639     
12640     meta = meta || {};
12641     // set some defaults:
12642     Roo.applyIf(meta, {
12643         totalProperty: 'total',
12644         successProperty : 'success',
12645         root : 'data',
12646         id : 'id'
12647     });
12648     
12649     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12650 };
12651 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12652     
12653     /**
12654      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12655      * Used by Store query builder to append _requestMeta to params.
12656      * 
12657      */
12658     metaFromRemote : false,
12659     /**
12660      * This method is only used by a DataProxy which has retrieved data from a remote server.
12661      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12662      * @return {Object} data A data block which is used by an Roo.data.Store object as
12663      * a cache of Roo.data.Records.
12664      */
12665     read : function(response){
12666         var json = response.responseText;
12667        
12668         var o = /* eval:var:o */ eval("("+json+")");
12669         if(!o) {
12670             throw {message: "JsonReader.read: Json object not found"};
12671         }
12672         
12673         if(o.metaData){
12674             
12675             delete this.ef;
12676             this.metaFromRemote = true;
12677             this.meta = o.metaData;
12678             this.recordType = Roo.data.Record.create(o.metaData.fields);
12679             this.onMetaChange(this.meta, this.recordType, o);
12680         }
12681         return this.readRecords(o);
12682     },
12683
12684     // private function a store will implement
12685     onMetaChange : function(meta, recordType, o){
12686
12687     },
12688
12689     /**
12690          * @ignore
12691          */
12692     simpleAccess: function(obj, subsc) {
12693         return obj[subsc];
12694     },
12695
12696         /**
12697          * @ignore
12698          */
12699     getJsonAccessor: function(){
12700         var re = /[\[\.]/;
12701         return function(expr) {
12702             try {
12703                 return(re.test(expr))
12704                     ? new Function("obj", "return obj." + expr)
12705                     : function(obj){
12706                         return obj[expr];
12707                     };
12708             } catch(e){}
12709             return Roo.emptyFn;
12710         };
12711     }(),
12712
12713     /**
12714      * Create a data block containing Roo.data.Records from an XML document.
12715      * @param {Object} o An object which contains an Array of row objects in the property specified
12716      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12717      * which contains the total size of the dataset.
12718      * @return {Object} data A data block which is used by an Roo.data.Store object as
12719      * a cache of Roo.data.Records.
12720      */
12721     readRecords : function(o){
12722         /**
12723          * After any data loads, the raw JSON data is available for further custom processing.
12724          * @type Object
12725          */
12726         this.o = o;
12727         var s = this.meta, Record = this.recordType,
12728             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12729
12730 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12731         if (!this.ef) {
12732             if(s.totalProperty) {
12733                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12734                 }
12735                 if(s.successProperty) {
12736                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12737                 }
12738                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12739                 if (s.id) {
12740                         var g = this.getJsonAccessor(s.id);
12741                         this.getId = function(rec) {
12742                                 var r = g(rec);  
12743                                 return (r === undefined || r === "") ? null : r;
12744                         };
12745                 } else {
12746                         this.getId = function(){return null;};
12747                 }
12748             this.ef = [];
12749             for(var jj = 0; jj < fl; jj++){
12750                 f = fi[jj];
12751                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12752                 this.ef[jj] = this.getJsonAccessor(map);
12753             }
12754         }
12755
12756         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12757         if(s.totalProperty){
12758             var vt = parseInt(this.getTotal(o), 10);
12759             if(!isNaN(vt)){
12760                 totalRecords = vt;
12761             }
12762         }
12763         if(s.successProperty){
12764             var vs = this.getSuccess(o);
12765             if(vs === false || vs === 'false'){
12766                 success = false;
12767             }
12768         }
12769         var records = [];
12770         for(var i = 0; i < c; i++){
12771                 var n = root[i];
12772             var values = {};
12773             var id = this.getId(n);
12774             for(var j = 0; j < fl; j++){
12775                 f = fi[j];
12776             var v = this.ef[j](n);
12777             if (!f.convert) {
12778                 Roo.log('missing convert for ' + f.name);
12779                 Roo.log(f);
12780                 continue;
12781             }
12782             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12783             }
12784             var record = new Record(values, id);
12785             record.json = n;
12786             records[i] = record;
12787         }
12788         return {
12789             raw : o,
12790             success : success,
12791             records : records,
12792             totalRecords : totalRecords
12793         };
12794     }
12795 });/*
12796  * Based on:
12797  * Ext JS Library 1.1.1
12798  * Copyright(c) 2006-2007, Ext JS, LLC.
12799  *
12800  * Originally Released Under LGPL - original licence link has changed is not relivant.
12801  *
12802  * Fork - LGPL
12803  * <script type="text/javascript">
12804  */
12805
12806 /**
12807  * @class Roo.data.ArrayReader
12808  * @extends Roo.data.DataReader
12809  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12810  * Each element of that Array represents a row of data fields. The
12811  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12812  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12813  * <p>
12814  * Example code:.
12815  * <pre><code>
12816 var RecordDef = Roo.data.Record.create([
12817     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12818     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12819 ]);
12820 var myReader = new Roo.data.ArrayReader({
12821     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12822 }, RecordDef);
12823 </code></pre>
12824  * <p>
12825  * This would consume an Array like this:
12826  * <pre><code>
12827 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12828   </code></pre>
12829  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12830  * @constructor
12831  * Create a new JsonReader
12832  * @param {Object} meta Metadata configuration options.
12833  * @param {Object} recordType Either an Array of field definition objects
12834  * as specified to {@link Roo.data.Record#create},
12835  * or an {@link Roo.data.Record} object
12836  * created using {@link Roo.data.Record#create}.
12837  */
12838 Roo.data.ArrayReader = function(meta, recordType){
12839     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12840 };
12841
12842 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12843     /**
12844      * Create a data block containing Roo.data.Records from an XML document.
12845      * @param {Object} o An Array of row objects which represents the dataset.
12846      * @return {Object} data A data block which is used by an Roo.data.Store object as
12847      * a cache of Roo.data.Records.
12848      */
12849     readRecords : function(o){
12850         var sid = this.meta ? this.meta.id : null;
12851         var recordType = this.recordType, fields = recordType.prototype.fields;
12852         var records = [];
12853         var root = o;
12854             for(var i = 0; i < root.length; i++){
12855                     var n = root[i];
12856                 var values = {};
12857                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12858                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12859                 var f = fields.items[j];
12860                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12861                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12862                 v = f.convert(v);
12863                 values[f.name] = v;
12864             }
12865                 var record = new recordType(values, id);
12866                 record.json = n;
12867                 records[records.length] = record;
12868             }
12869             return {
12870                 records : records,
12871                 totalRecords : records.length
12872             };
12873     }
12874 });/*
12875  * - LGPL
12876  * * 
12877  */
12878
12879 /**
12880  * @class Roo.bootstrap.ComboBox
12881  * @extends Roo.bootstrap.TriggerField
12882  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12883  * @cfg {Boolean} append (true|false) default false
12884  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12885  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12886  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12887  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12888  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12889  * @cfg {Boolean} animate default true
12890  * @cfg {Boolean} emptyResultText only for touch device
12891  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12892  * @cfg {String} emptyTitle default ''
12893  * @constructor
12894  * Create a new ComboBox.
12895  * @param {Object} config Configuration options
12896  */
12897 Roo.bootstrap.ComboBox = function(config){
12898     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12899     this.addEvents({
12900         /**
12901          * @event expand
12902          * Fires when the dropdown list is expanded
12903         * @param {Roo.bootstrap.ComboBox} combo This combo box
12904         */
12905         'expand' : true,
12906         /**
12907          * @event collapse
12908          * Fires when the dropdown list is collapsed
12909         * @param {Roo.bootstrap.ComboBox} combo This combo box
12910         */
12911         'collapse' : true,
12912         /**
12913          * @event beforeselect
12914          * Fires before a list item is selected. Return false to cancel the selection.
12915         * @param {Roo.bootstrap.ComboBox} combo This combo box
12916         * @param {Roo.data.Record} record The data record returned from the underlying store
12917         * @param {Number} index The index of the selected item in the dropdown list
12918         */
12919         'beforeselect' : true,
12920         /**
12921          * @event select
12922          * Fires when a list item is selected
12923         * @param {Roo.bootstrap.ComboBox} combo This combo box
12924         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12925         * @param {Number} index The index of the selected item in the dropdown list
12926         */
12927         'select' : true,
12928         /**
12929          * @event beforequery
12930          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12931          * The event object passed has these properties:
12932         * @param {Roo.bootstrap.ComboBox} combo This combo box
12933         * @param {String} query The query
12934         * @param {Boolean} forceAll true to force "all" query
12935         * @param {Boolean} cancel true to cancel the query
12936         * @param {Object} e The query event object
12937         */
12938         'beforequery': true,
12939          /**
12940          * @event add
12941          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12942         * @param {Roo.bootstrap.ComboBox} combo This combo box
12943         */
12944         'add' : true,
12945         /**
12946          * @event edit
12947          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12948         * @param {Roo.bootstrap.ComboBox} combo This combo box
12949         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12950         */
12951         'edit' : true,
12952         /**
12953          * @event remove
12954          * Fires when the remove value from the combobox array
12955         * @param {Roo.bootstrap.ComboBox} combo This combo box
12956         */
12957         'remove' : true,
12958         /**
12959          * @event afterremove
12960          * Fires when the remove value from the combobox array
12961         * @param {Roo.bootstrap.ComboBox} combo This combo box
12962         */
12963         'afterremove' : true,
12964         /**
12965          * @event specialfilter
12966          * Fires when specialfilter
12967             * @param {Roo.bootstrap.ComboBox} combo This combo box
12968             */
12969         'specialfilter' : true,
12970         /**
12971          * @event tick
12972          * Fires when tick the element
12973             * @param {Roo.bootstrap.ComboBox} combo This combo box
12974             */
12975         'tick' : true,
12976         /**
12977          * @event touchviewdisplay
12978          * Fires when touch view require special display (default is using displayField)
12979             * @param {Roo.bootstrap.ComboBox} combo This combo box
12980             * @param {Object} cfg set html .
12981             */
12982         'touchviewdisplay' : true
12983         
12984     });
12985     
12986     this.item = [];
12987     this.tickItems = [];
12988     
12989     this.selectedIndex = -1;
12990     if(this.mode == 'local'){
12991         if(config.queryDelay === undefined){
12992             this.queryDelay = 10;
12993         }
12994         if(config.minChars === undefined){
12995             this.minChars = 0;
12996         }
12997     }
12998 };
12999
13000 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13001      
13002     /**
13003      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13004      * rendering into an Roo.Editor, defaults to false)
13005      */
13006     /**
13007      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13008      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13009      */
13010     /**
13011      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13012      */
13013     /**
13014      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13015      * the dropdown list (defaults to undefined, with no header element)
13016      */
13017
13018      /**
13019      * @cfg {String/Roo.Template} tpl The template to use to render the output
13020      */
13021      
13022      /**
13023      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13024      */
13025     listWidth: undefined,
13026     /**
13027      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13028      * mode = 'remote' or 'text' if mode = 'local')
13029      */
13030     displayField: undefined,
13031     
13032     /**
13033      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13034      * mode = 'remote' or 'value' if mode = 'local'). 
13035      * Note: use of a valueField requires the user make a selection
13036      * in order for a value to be mapped.
13037      */
13038     valueField: undefined,
13039     /**
13040      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13041      */
13042     modalTitle : '',
13043     
13044     /**
13045      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13046      * field's data value (defaults to the underlying DOM element's name)
13047      */
13048     hiddenName: undefined,
13049     /**
13050      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13051      */
13052     listClass: '',
13053     /**
13054      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13055      */
13056     selectedClass: 'active',
13057     
13058     /**
13059      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13060      */
13061     shadow:'sides',
13062     /**
13063      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13064      * anchor positions (defaults to 'tl-bl')
13065      */
13066     listAlign: 'tl-bl?',
13067     /**
13068      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13069      */
13070     maxHeight: 300,
13071     /**
13072      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13073      * query specified by the allQuery config option (defaults to 'query')
13074      */
13075     triggerAction: 'query',
13076     /**
13077      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13078      * (defaults to 4, does not apply if editable = false)
13079      */
13080     minChars : 4,
13081     /**
13082      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13083      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13084      */
13085     typeAhead: false,
13086     /**
13087      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13088      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13089      */
13090     queryDelay: 500,
13091     /**
13092      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13093      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13094      */
13095     pageSize: 0,
13096     /**
13097      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13098      * when editable = true (defaults to false)
13099      */
13100     selectOnFocus:false,
13101     /**
13102      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13103      */
13104     queryParam: 'query',
13105     /**
13106      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13107      * when mode = 'remote' (defaults to 'Loading...')
13108      */
13109     loadingText: 'Loading...',
13110     /**
13111      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13112      */
13113     resizable: false,
13114     /**
13115      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13116      */
13117     handleHeight : 8,
13118     /**
13119      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13120      * traditional select (defaults to true)
13121      */
13122     editable: true,
13123     /**
13124      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13125      */
13126     allQuery: '',
13127     /**
13128      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13129      */
13130     mode: 'remote',
13131     /**
13132      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13133      * listWidth has a higher value)
13134      */
13135     minListWidth : 70,
13136     /**
13137      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13138      * allow the user to set arbitrary text into the field (defaults to false)
13139      */
13140     forceSelection:false,
13141     /**
13142      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13143      * if typeAhead = true (defaults to 250)
13144      */
13145     typeAheadDelay : 250,
13146     /**
13147      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13148      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13149      */
13150     valueNotFoundText : undefined,
13151     /**
13152      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13153      */
13154     blockFocus : false,
13155     
13156     /**
13157      * @cfg {Boolean} disableClear Disable showing of clear button.
13158      */
13159     disableClear : false,
13160     /**
13161      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13162      */
13163     alwaysQuery : false,
13164     
13165     /**
13166      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13167      */
13168     multiple : false,
13169     
13170     /**
13171      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13172      */
13173     invalidClass : "has-warning",
13174     
13175     /**
13176      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13177      */
13178     validClass : "has-success",
13179     
13180     /**
13181      * @cfg {Boolean} specialFilter (true|false) special filter default false
13182      */
13183     specialFilter : false,
13184     
13185     /**
13186      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13187      */
13188     mobileTouchView : true,
13189     
13190     /**
13191      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13192      */
13193     useNativeIOS : false,
13194     
13195     /**
13196      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13197      */
13198     mobile_restrict_height : false,
13199     
13200     ios_options : false,
13201     
13202     //private
13203     addicon : false,
13204     editicon: false,
13205     
13206     page: 0,
13207     hasQuery: false,
13208     append: false,
13209     loadNext: false,
13210     autoFocus : true,
13211     tickable : false,
13212     btnPosition : 'right',
13213     triggerList : true,
13214     showToggleBtn : true,
13215     animate : true,
13216     emptyResultText: 'Empty',
13217     triggerText : 'Select',
13218     emptyTitle : '',
13219     
13220     // element that contains real text value.. (when hidden is used..)
13221     
13222     getAutoCreate : function()
13223     {   
13224         var cfg = false;
13225         //render
13226         /*
13227          * Render classic select for iso
13228          */
13229         
13230         if(Roo.isIOS && this.useNativeIOS){
13231             cfg = this.getAutoCreateNativeIOS();
13232             return cfg;
13233         }
13234         
13235         /*
13236          * Touch Devices
13237          */
13238         
13239         if(Roo.isTouch && this.mobileTouchView){
13240             cfg = this.getAutoCreateTouchView();
13241             return cfg;;
13242         }
13243         
13244         /*
13245          *  Normal ComboBox
13246          */
13247         if(!this.tickable){
13248             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13249             return cfg;
13250         }
13251         
13252         /*
13253          *  ComboBox with tickable selections
13254          */
13255              
13256         var align = this.labelAlign || this.parentLabelAlign();
13257         
13258         cfg = {
13259             cls : 'form-group roo-combobox-tickable' //input-group
13260         };
13261         
13262         var btn_text_select = '';
13263         var btn_text_done = '';
13264         var btn_text_cancel = '';
13265         
13266         if (this.btn_text_show) {
13267             btn_text_select = 'Select';
13268             btn_text_done = 'Done';
13269             btn_text_cancel = 'Cancel'; 
13270         }
13271         
13272         var buttons = {
13273             tag : 'div',
13274             cls : 'tickable-buttons',
13275             cn : [
13276                 {
13277                     tag : 'button',
13278                     type : 'button',
13279                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13280                     //html : this.triggerText
13281                     html: btn_text_select
13282                 },
13283                 {
13284                     tag : 'button',
13285                     type : 'button',
13286                     name : 'ok',
13287                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13288                     //html : 'Done'
13289                     html: btn_text_done
13290                 },
13291                 {
13292                     tag : 'button',
13293                     type : 'button',
13294                     name : 'cancel',
13295                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13296                     //html : 'Cancel'
13297                     html: btn_text_cancel
13298                 }
13299             ]
13300         };
13301         
13302         if(this.editable){
13303             buttons.cn.unshift({
13304                 tag: 'input',
13305                 cls: 'roo-select2-search-field-input'
13306             });
13307         }
13308         
13309         var _this = this;
13310         
13311         Roo.each(buttons.cn, function(c){
13312             if (_this.size) {
13313                 c.cls += ' btn-' + _this.size;
13314             }
13315
13316             if (_this.disabled) {
13317                 c.disabled = true;
13318             }
13319         });
13320         
13321         var box = {
13322             tag: 'div',
13323             cn: [
13324                 {
13325                     tag: 'input',
13326                     type : 'hidden',
13327                     cls: 'form-hidden-field'
13328                 },
13329                 {
13330                     tag: 'ul',
13331                     cls: 'roo-select2-choices',
13332                     cn:[
13333                         {
13334                             tag: 'li',
13335                             cls: 'roo-select2-search-field',
13336                             cn: [
13337                                 buttons
13338                             ]
13339                         }
13340                     ]
13341                 }
13342             ]
13343         };
13344         
13345         var combobox = {
13346             cls: 'roo-select2-container input-group roo-select2-container-multi',
13347             cn: [
13348                 
13349                 box
13350 //                {
13351 //                    tag: 'ul',
13352 //                    cls: 'typeahead typeahead-long dropdown-menu',
13353 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13354 //                }
13355             ]
13356         };
13357         
13358         if(this.hasFeedback && !this.allowBlank){
13359             
13360             var feedback = {
13361                 tag: 'span',
13362                 cls: 'glyphicon form-control-feedback'
13363             };
13364
13365             combobox.cn.push(feedback);
13366         }
13367         
13368         var indicator = {
13369             tag : 'i',
13370             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13371             tooltip : 'This field is required'
13372         };
13373         if (Roo.bootstrap.version == 4) {
13374             indicator = {
13375                 tag : 'i',
13376                 style : 'display:none'
13377             };
13378         }
13379         if (align ==='left' && this.fieldLabel.length) {
13380             
13381             cfg.cls += ' roo-form-group-label-left row';
13382             
13383             cfg.cn = [
13384                 indicator,
13385                 {
13386                     tag: 'label',
13387                     'for' :  id,
13388                     cls : 'control-label col-form-label',
13389                     html : this.fieldLabel
13390
13391                 },
13392                 {
13393                     cls : "", 
13394                     cn: [
13395                         combobox
13396                     ]
13397                 }
13398
13399             ];
13400             
13401             var labelCfg = cfg.cn[1];
13402             var contentCfg = cfg.cn[2];
13403             
13404
13405             if(this.indicatorpos == 'right'){
13406                 
13407                 cfg.cn = [
13408                     {
13409                         tag: 'label',
13410                         'for' :  id,
13411                         cls : 'control-label col-form-label',
13412                         cn : [
13413                             {
13414                                 tag : 'span',
13415                                 html : this.fieldLabel
13416                             },
13417                             indicator
13418                         ]
13419                     },
13420                     {
13421                         cls : "",
13422                         cn: [
13423                             combobox
13424                         ]
13425                     }
13426
13427                 ];
13428                 
13429                 
13430                 
13431                 labelCfg = cfg.cn[0];
13432                 contentCfg = cfg.cn[1];
13433             
13434             }
13435             
13436             if(this.labelWidth > 12){
13437                 labelCfg.style = "width: " + this.labelWidth + 'px';
13438             }
13439             
13440             if(this.labelWidth < 13 && this.labelmd == 0){
13441                 this.labelmd = this.labelWidth;
13442             }
13443             
13444             if(this.labellg > 0){
13445                 labelCfg.cls += ' col-lg-' + this.labellg;
13446                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13447             }
13448             
13449             if(this.labelmd > 0){
13450                 labelCfg.cls += ' col-md-' + this.labelmd;
13451                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13452             }
13453             
13454             if(this.labelsm > 0){
13455                 labelCfg.cls += ' col-sm-' + this.labelsm;
13456                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13457             }
13458             
13459             if(this.labelxs > 0){
13460                 labelCfg.cls += ' col-xs-' + this.labelxs;
13461                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13462             }
13463                 
13464                 
13465         } else if ( this.fieldLabel.length) {
13466 //                Roo.log(" label");
13467                  cfg.cn = [
13468                    indicator,
13469                     {
13470                         tag: 'label',
13471                         //cls : 'input-group-addon',
13472                         html : this.fieldLabel
13473                     },
13474                     combobox
13475                 ];
13476                 
13477                 if(this.indicatorpos == 'right'){
13478                     cfg.cn = [
13479                         {
13480                             tag: 'label',
13481                             //cls : 'input-group-addon',
13482                             html : this.fieldLabel
13483                         },
13484                         indicator,
13485                         combobox
13486                     ];
13487                     
13488                 }
13489
13490         } else {
13491             
13492 //                Roo.log(" no label && no align");
13493                 cfg = combobox
13494                      
13495                 
13496         }
13497          
13498         var settings=this;
13499         ['xs','sm','md','lg'].map(function(size){
13500             if (settings[size]) {
13501                 cfg.cls += ' col-' + size + '-' + settings[size];
13502             }
13503         });
13504         
13505         return cfg;
13506         
13507     },
13508     
13509     _initEventsCalled : false,
13510     
13511     // private
13512     initEvents: function()
13513     {   
13514         if (this._initEventsCalled) { // as we call render... prevent looping...
13515             return;
13516         }
13517         this._initEventsCalled = true;
13518         
13519         if (!this.store) {
13520             throw "can not find store for combo";
13521         }
13522         
13523         this.indicator = this.indicatorEl();
13524         
13525         this.store = Roo.factory(this.store, Roo.data);
13526         this.store.parent = this;
13527         
13528         // if we are building from html. then this element is so complex, that we can not really
13529         // use the rendered HTML.
13530         // so we have to trash and replace the previous code.
13531         if (Roo.XComponent.build_from_html) {
13532             // remove this element....
13533             var e = this.el.dom, k=0;
13534             while (e ) { e = e.previousSibling;  ++k;}
13535
13536             this.el.remove();
13537             
13538             this.el=false;
13539             this.rendered = false;
13540             
13541             this.render(this.parent().getChildContainer(true), k);
13542         }
13543         
13544         if(Roo.isIOS && this.useNativeIOS){
13545             this.initIOSView();
13546             return;
13547         }
13548         
13549         /*
13550          * Touch Devices
13551          */
13552         
13553         if(Roo.isTouch && this.mobileTouchView){
13554             this.initTouchView();
13555             return;
13556         }
13557         
13558         if(this.tickable){
13559             this.initTickableEvents();
13560             return;
13561         }
13562         
13563         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13564         
13565         if(this.hiddenName){
13566             
13567             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13568             
13569             this.hiddenField.dom.value =
13570                 this.hiddenValue !== undefined ? this.hiddenValue :
13571                 this.value !== undefined ? this.value : '';
13572
13573             // prevent input submission
13574             this.el.dom.removeAttribute('name');
13575             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13576              
13577              
13578         }
13579         //if(Roo.isGecko){
13580         //    this.el.dom.setAttribute('autocomplete', 'off');
13581         //}
13582         
13583         var cls = 'x-combo-list';
13584         
13585         //this.list = new Roo.Layer({
13586         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13587         //});
13588         
13589         var _this = this;
13590         
13591         (function(){
13592             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13593             _this.list.setWidth(lw);
13594         }).defer(100);
13595         
13596         this.list.on('mouseover', this.onViewOver, this);
13597         this.list.on('mousemove', this.onViewMove, this);
13598         this.list.on('scroll', this.onViewScroll, this);
13599         
13600         /*
13601         this.list.swallowEvent('mousewheel');
13602         this.assetHeight = 0;
13603
13604         if(this.title){
13605             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13606             this.assetHeight += this.header.getHeight();
13607         }
13608
13609         this.innerList = this.list.createChild({cls:cls+'-inner'});
13610         this.innerList.on('mouseover', this.onViewOver, this);
13611         this.innerList.on('mousemove', this.onViewMove, this);
13612         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13613         
13614         if(this.allowBlank && !this.pageSize && !this.disableClear){
13615             this.footer = this.list.createChild({cls:cls+'-ft'});
13616             this.pageTb = new Roo.Toolbar(this.footer);
13617            
13618         }
13619         if(this.pageSize){
13620             this.footer = this.list.createChild({cls:cls+'-ft'});
13621             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13622                     {pageSize: this.pageSize});
13623             
13624         }
13625         
13626         if (this.pageTb && this.allowBlank && !this.disableClear) {
13627             var _this = this;
13628             this.pageTb.add(new Roo.Toolbar.Fill(), {
13629                 cls: 'x-btn-icon x-btn-clear',
13630                 text: '&#160;',
13631                 handler: function()
13632                 {
13633                     _this.collapse();
13634                     _this.clearValue();
13635                     _this.onSelect(false, -1);
13636                 }
13637             });
13638         }
13639         if (this.footer) {
13640             this.assetHeight += this.footer.getHeight();
13641         }
13642         */
13643             
13644         if(!this.tpl){
13645             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13646         }
13647
13648         this.view = new Roo.View(this.list, this.tpl, {
13649             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13650         });
13651         //this.view.wrapEl.setDisplayed(false);
13652         this.view.on('click', this.onViewClick, this);
13653         
13654         
13655         this.store.on('beforeload', this.onBeforeLoad, this);
13656         this.store.on('load', this.onLoad, this);
13657         this.store.on('loadexception', this.onLoadException, this);
13658         /*
13659         if(this.resizable){
13660             this.resizer = new Roo.Resizable(this.list,  {
13661                pinned:true, handles:'se'
13662             });
13663             this.resizer.on('resize', function(r, w, h){
13664                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13665                 this.listWidth = w;
13666                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13667                 this.restrictHeight();
13668             }, this);
13669             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13670         }
13671         */
13672         if(!this.editable){
13673             this.editable = true;
13674             this.setEditable(false);
13675         }
13676         
13677         /*
13678         
13679         if (typeof(this.events.add.listeners) != 'undefined') {
13680             
13681             this.addicon = this.wrap.createChild(
13682                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13683        
13684             this.addicon.on('click', function(e) {
13685                 this.fireEvent('add', this);
13686             }, this);
13687         }
13688         if (typeof(this.events.edit.listeners) != 'undefined') {
13689             
13690             this.editicon = this.wrap.createChild(
13691                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13692             if (this.addicon) {
13693                 this.editicon.setStyle('margin-left', '40px');
13694             }
13695             this.editicon.on('click', function(e) {
13696                 
13697                 // we fire even  if inothing is selected..
13698                 this.fireEvent('edit', this, this.lastData );
13699                 
13700             }, this);
13701         }
13702         */
13703         
13704         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13705             "up" : function(e){
13706                 this.inKeyMode = true;
13707                 this.selectPrev();
13708             },
13709
13710             "down" : function(e){
13711                 if(!this.isExpanded()){
13712                     this.onTriggerClick();
13713                 }else{
13714                     this.inKeyMode = true;
13715                     this.selectNext();
13716                 }
13717             },
13718
13719             "enter" : function(e){
13720 //                this.onViewClick();
13721                 //return true;
13722                 this.collapse();
13723                 
13724                 if(this.fireEvent("specialkey", this, e)){
13725                     this.onViewClick(false);
13726                 }
13727                 
13728                 return true;
13729             },
13730
13731             "esc" : function(e){
13732                 this.collapse();
13733             },
13734
13735             "tab" : function(e){
13736                 this.collapse();
13737                 
13738                 if(this.fireEvent("specialkey", this, e)){
13739                     this.onViewClick(false);
13740                 }
13741                 
13742                 return true;
13743             },
13744
13745             scope : this,
13746
13747             doRelay : function(foo, bar, hname){
13748                 if(hname == 'down' || this.scope.isExpanded()){
13749                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13750                 }
13751                 return true;
13752             },
13753
13754             forceKeyDown: true
13755         });
13756         
13757         
13758         this.queryDelay = Math.max(this.queryDelay || 10,
13759                 this.mode == 'local' ? 10 : 250);
13760         
13761         
13762         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13763         
13764         if(this.typeAhead){
13765             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13766         }
13767         if(this.editable !== false){
13768             this.inputEl().on("keyup", this.onKeyUp, this);
13769         }
13770         if(this.forceSelection){
13771             this.inputEl().on('blur', this.doForce, this);
13772         }
13773         
13774         if(this.multiple){
13775             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13776             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13777         }
13778     },
13779     
13780     initTickableEvents: function()
13781     {   
13782         this.createList();
13783         
13784         if(this.hiddenName){
13785             
13786             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13787             
13788             this.hiddenField.dom.value =
13789                 this.hiddenValue !== undefined ? this.hiddenValue :
13790                 this.value !== undefined ? this.value : '';
13791
13792             // prevent input submission
13793             this.el.dom.removeAttribute('name');
13794             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13795              
13796              
13797         }
13798         
13799 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13800         
13801         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13802         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13803         if(this.triggerList){
13804             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13805         }
13806          
13807         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13808         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13809         
13810         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13811         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13812         
13813         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13814         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13815         
13816         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13817         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13818         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13819         
13820         this.okBtn.hide();
13821         this.cancelBtn.hide();
13822         
13823         var _this = this;
13824         
13825         (function(){
13826             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13827             _this.list.setWidth(lw);
13828         }).defer(100);
13829         
13830         this.list.on('mouseover', this.onViewOver, this);
13831         this.list.on('mousemove', this.onViewMove, this);
13832         
13833         this.list.on('scroll', this.onViewScroll, this);
13834         
13835         if(!this.tpl){
13836             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13837                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13838         }
13839
13840         this.view = new Roo.View(this.list, this.tpl, {
13841             singleSelect:true,
13842             tickable:true,
13843             parent:this,
13844             store: this.store,
13845             selectedClass: this.selectedClass
13846         });
13847         
13848         //this.view.wrapEl.setDisplayed(false);
13849         this.view.on('click', this.onViewClick, this);
13850         
13851         
13852         
13853         this.store.on('beforeload', this.onBeforeLoad, this);
13854         this.store.on('load', this.onLoad, this);
13855         this.store.on('loadexception', this.onLoadException, this);
13856         
13857         if(this.editable){
13858             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13859                 "up" : function(e){
13860                     this.inKeyMode = true;
13861                     this.selectPrev();
13862                 },
13863
13864                 "down" : function(e){
13865                     this.inKeyMode = true;
13866                     this.selectNext();
13867                 },
13868
13869                 "enter" : function(e){
13870                     if(this.fireEvent("specialkey", this, e)){
13871                         this.onViewClick(false);
13872                     }
13873                     
13874                     return true;
13875                 },
13876
13877                 "esc" : function(e){
13878                     this.onTickableFooterButtonClick(e, false, false);
13879                 },
13880
13881                 "tab" : function(e){
13882                     this.fireEvent("specialkey", this, e);
13883                     
13884                     this.onTickableFooterButtonClick(e, false, false);
13885                     
13886                     return true;
13887                 },
13888
13889                 scope : this,
13890
13891                 doRelay : function(e, fn, key){
13892                     if(this.scope.isExpanded()){
13893                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13894                     }
13895                     return true;
13896                 },
13897
13898                 forceKeyDown: true
13899             });
13900         }
13901         
13902         this.queryDelay = Math.max(this.queryDelay || 10,
13903                 this.mode == 'local' ? 10 : 250);
13904         
13905         
13906         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13907         
13908         if(this.typeAhead){
13909             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13910         }
13911         
13912         if(this.editable !== false){
13913             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13914         }
13915         
13916         this.indicator = this.indicatorEl();
13917         
13918         if(this.indicator){
13919             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13920             this.indicator.hide();
13921         }
13922         
13923     },
13924
13925     onDestroy : function(){
13926         if(this.view){
13927             this.view.setStore(null);
13928             this.view.el.removeAllListeners();
13929             this.view.el.remove();
13930             this.view.purgeListeners();
13931         }
13932         if(this.list){
13933             this.list.dom.innerHTML  = '';
13934         }
13935         
13936         if(this.store){
13937             this.store.un('beforeload', this.onBeforeLoad, this);
13938             this.store.un('load', this.onLoad, this);
13939             this.store.un('loadexception', this.onLoadException, this);
13940         }
13941         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13942     },
13943
13944     // private
13945     fireKey : function(e){
13946         if(e.isNavKeyPress() && !this.list.isVisible()){
13947             this.fireEvent("specialkey", this, e);
13948         }
13949     },
13950
13951     // private
13952     onResize: function(w, h){
13953 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13954 //        
13955 //        if(typeof w != 'number'){
13956 //            // we do not handle it!?!?
13957 //            return;
13958 //        }
13959 //        var tw = this.trigger.getWidth();
13960 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13961 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13962 //        var x = w - tw;
13963 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13964 //            
13965 //        //this.trigger.setStyle('left', x+'px');
13966 //        
13967 //        if(this.list && this.listWidth === undefined){
13968 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13969 //            this.list.setWidth(lw);
13970 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13971 //        }
13972         
13973     
13974         
13975     },
13976
13977     /**
13978      * Allow or prevent the user from directly editing the field text.  If false is passed,
13979      * the user will only be able to select from the items defined in the dropdown list.  This method
13980      * is the runtime equivalent of setting the 'editable' config option at config time.
13981      * @param {Boolean} value True to allow the user to directly edit the field text
13982      */
13983     setEditable : function(value){
13984         if(value == this.editable){
13985             return;
13986         }
13987         this.editable = value;
13988         if(!value){
13989             this.inputEl().dom.setAttribute('readOnly', true);
13990             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13991             this.inputEl().addClass('x-combo-noedit');
13992         }else{
13993             this.inputEl().dom.setAttribute('readOnly', false);
13994             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13995             this.inputEl().removeClass('x-combo-noedit');
13996         }
13997     },
13998
13999     // private
14000     
14001     onBeforeLoad : function(combo,opts){
14002         if(!this.hasFocus){
14003             return;
14004         }
14005          if (!opts.add) {
14006             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14007          }
14008         this.restrictHeight();
14009         this.selectedIndex = -1;
14010     },
14011
14012     // private
14013     onLoad : function(){
14014         
14015         this.hasQuery = false;
14016         
14017         if(!this.hasFocus){
14018             return;
14019         }
14020         
14021         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14022             this.loading.hide();
14023         }
14024         
14025         if(this.store.getCount() > 0){
14026             
14027             this.expand();
14028             this.restrictHeight();
14029             if(this.lastQuery == this.allQuery){
14030                 if(this.editable && !this.tickable){
14031                     this.inputEl().dom.select();
14032                 }
14033                 
14034                 if(
14035                     !this.selectByValue(this.value, true) &&
14036                     this.autoFocus && 
14037                     (
14038                         !this.store.lastOptions ||
14039                         typeof(this.store.lastOptions.add) == 'undefined' || 
14040                         this.store.lastOptions.add != true
14041                     )
14042                 ){
14043                     this.select(0, true);
14044                 }
14045             }else{
14046                 if(this.autoFocus){
14047                     this.selectNext();
14048                 }
14049                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14050                     this.taTask.delay(this.typeAheadDelay);
14051                 }
14052             }
14053         }else{
14054             this.onEmptyResults();
14055         }
14056         
14057         //this.el.focus();
14058     },
14059     // private
14060     onLoadException : function()
14061     {
14062         this.hasQuery = false;
14063         
14064         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14065             this.loading.hide();
14066         }
14067         
14068         if(this.tickable && this.editable){
14069             return;
14070         }
14071         
14072         this.collapse();
14073         // only causes errors at present
14074         //Roo.log(this.store.reader.jsonData);
14075         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14076             // fixme
14077             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14078         //}
14079         
14080         
14081     },
14082     // private
14083     onTypeAhead : function(){
14084         if(this.store.getCount() > 0){
14085             var r = this.store.getAt(0);
14086             var newValue = r.data[this.displayField];
14087             var len = newValue.length;
14088             var selStart = this.getRawValue().length;
14089             
14090             if(selStart != len){
14091                 this.setRawValue(newValue);
14092                 this.selectText(selStart, newValue.length);
14093             }
14094         }
14095     },
14096
14097     // private
14098     onSelect : function(record, index){
14099         
14100         if(this.fireEvent('beforeselect', this, record, index) !== false){
14101         
14102             this.setFromData(index > -1 ? record.data : false);
14103             
14104             this.collapse();
14105             this.fireEvent('select', this, record, index);
14106         }
14107     },
14108
14109     /**
14110      * Returns the currently selected field value or empty string if no value is set.
14111      * @return {String} value The selected value
14112      */
14113     getValue : function()
14114     {
14115         if(Roo.isIOS && this.useNativeIOS){
14116             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14117         }
14118         
14119         if(this.multiple){
14120             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14121         }
14122         
14123         if(this.valueField){
14124             return typeof this.value != 'undefined' ? this.value : '';
14125         }else{
14126             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14127         }
14128     },
14129     
14130     getRawValue : function()
14131     {
14132         if(Roo.isIOS && this.useNativeIOS){
14133             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14134         }
14135         
14136         var v = this.inputEl().getValue();
14137         
14138         return v;
14139     },
14140
14141     /**
14142      * Clears any text/value currently set in the field
14143      */
14144     clearValue : function(){
14145         
14146         if(this.hiddenField){
14147             this.hiddenField.dom.value = '';
14148         }
14149         this.value = '';
14150         this.setRawValue('');
14151         this.lastSelectionText = '';
14152         this.lastData = false;
14153         
14154         var close = this.closeTriggerEl();
14155         
14156         if(close){
14157             close.hide();
14158         }
14159         
14160         this.validate();
14161         
14162     },
14163
14164     /**
14165      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14166      * will be displayed in the field.  If the value does not match the data value of an existing item,
14167      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14168      * Otherwise the field will be blank (although the value will still be set).
14169      * @param {String} value The value to match
14170      */
14171     setValue : function(v)
14172     {
14173         if(Roo.isIOS && this.useNativeIOS){
14174             this.setIOSValue(v);
14175             return;
14176         }
14177         
14178         if(this.multiple){
14179             this.syncValue();
14180             return;
14181         }
14182         
14183         var text = v;
14184         if(this.valueField){
14185             var r = this.findRecord(this.valueField, v);
14186             if(r){
14187                 text = r.data[this.displayField];
14188             }else if(this.valueNotFoundText !== undefined){
14189                 text = this.valueNotFoundText;
14190             }
14191         }
14192         this.lastSelectionText = text;
14193         if(this.hiddenField){
14194             this.hiddenField.dom.value = v;
14195         }
14196         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14197         this.value = v;
14198         
14199         var close = this.closeTriggerEl();
14200         
14201         if(close){
14202             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14203         }
14204         
14205         this.validate();
14206     },
14207     /**
14208      * @property {Object} the last set data for the element
14209      */
14210     
14211     lastData : false,
14212     /**
14213      * Sets the value of the field based on a object which is related to the record format for the store.
14214      * @param {Object} value the value to set as. or false on reset?
14215      */
14216     setFromData : function(o){
14217         
14218         if(this.multiple){
14219             this.addItem(o);
14220             return;
14221         }
14222             
14223         var dv = ''; // display value
14224         var vv = ''; // value value..
14225         this.lastData = o;
14226         if (this.displayField) {
14227             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14228         } else {
14229             // this is an error condition!!!
14230             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14231         }
14232         
14233         if(this.valueField){
14234             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14235         }
14236         
14237         var close = this.closeTriggerEl();
14238         
14239         if(close){
14240             if(dv.length || vv * 1 > 0){
14241                 close.show() ;
14242                 this.blockFocus=true;
14243             } else {
14244                 close.hide();
14245             }             
14246         }
14247         
14248         if(this.hiddenField){
14249             this.hiddenField.dom.value = vv;
14250             
14251             this.lastSelectionText = dv;
14252             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14253             this.value = vv;
14254             return;
14255         }
14256         // no hidden field.. - we store the value in 'value', but still display
14257         // display field!!!!
14258         this.lastSelectionText = dv;
14259         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14260         this.value = vv;
14261         
14262         
14263         
14264     },
14265     // private
14266     reset : function(){
14267         // overridden so that last data is reset..
14268         
14269         if(this.multiple){
14270             this.clearItem();
14271             return;
14272         }
14273         
14274         this.setValue(this.originalValue);
14275         //this.clearInvalid();
14276         this.lastData = false;
14277         if (this.view) {
14278             this.view.clearSelections();
14279         }
14280         
14281         this.validate();
14282     },
14283     // private
14284     findRecord : function(prop, value){
14285         var record;
14286         if(this.store.getCount() > 0){
14287             this.store.each(function(r){
14288                 if(r.data[prop] == value){
14289                     record = r;
14290                     return false;
14291                 }
14292                 return true;
14293             });
14294         }
14295         return record;
14296     },
14297     
14298     getName: function()
14299     {
14300         // returns hidden if it's set..
14301         if (!this.rendered) {return ''};
14302         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14303         
14304     },
14305     // private
14306     onViewMove : function(e, t){
14307         this.inKeyMode = false;
14308     },
14309
14310     // private
14311     onViewOver : function(e, t){
14312         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14313             return;
14314         }
14315         var item = this.view.findItemFromChild(t);
14316         
14317         if(item){
14318             var index = this.view.indexOf(item);
14319             this.select(index, false);
14320         }
14321     },
14322
14323     // private
14324     onViewClick : function(view, doFocus, el, e)
14325     {
14326         var index = this.view.getSelectedIndexes()[0];
14327         
14328         var r = this.store.getAt(index);
14329         
14330         if(this.tickable){
14331             
14332             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14333                 return;
14334             }
14335             
14336             var rm = false;
14337             var _this = this;
14338             
14339             Roo.each(this.tickItems, function(v,k){
14340                 
14341                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14342                     Roo.log(v);
14343                     _this.tickItems.splice(k, 1);
14344                     
14345                     if(typeof(e) == 'undefined' && view == false){
14346                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14347                     }
14348                     
14349                     rm = true;
14350                     return;
14351                 }
14352             });
14353             
14354             if(rm){
14355                 return;
14356             }
14357             
14358             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14359                 this.tickItems.push(r.data);
14360             }
14361             
14362             if(typeof(e) == 'undefined' && view == false){
14363                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14364             }
14365                     
14366             return;
14367         }
14368         
14369         if(r){
14370             this.onSelect(r, index);
14371         }
14372         if(doFocus !== false && !this.blockFocus){
14373             this.inputEl().focus();
14374         }
14375     },
14376
14377     // private
14378     restrictHeight : function(){
14379         //this.innerList.dom.style.height = '';
14380         //var inner = this.innerList.dom;
14381         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14382         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14383         //this.list.beginUpdate();
14384         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14385         this.list.alignTo(this.inputEl(), this.listAlign);
14386         this.list.alignTo(this.inputEl(), this.listAlign);
14387         //this.list.endUpdate();
14388     },
14389
14390     // private
14391     onEmptyResults : function(){
14392         
14393         if(this.tickable && this.editable){
14394             this.hasFocus = false;
14395             this.restrictHeight();
14396             return;
14397         }
14398         
14399         this.collapse();
14400     },
14401
14402     /**
14403      * Returns true if the dropdown list is expanded, else false.
14404      */
14405     isExpanded : function(){
14406         return this.list.isVisible();
14407     },
14408
14409     /**
14410      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14411      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14412      * @param {String} value The data value of the item to select
14413      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14414      * selected item if it is not currently in view (defaults to true)
14415      * @return {Boolean} True if the value matched an item in the list, else false
14416      */
14417     selectByValue : function(v, scrollIntoView){
14418         if(v !== undefined && v !== null){
14419             var r = this.findRecord(this.valueField || this.displayField, v);
14420             if(r){
14421                 this.select(this.store.indexOf(r), scrollIntoView);
14422                 return true;
14423             }
14424         }
14425         return false;
14426     },
14427
14428     /**
14429      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14430      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14431      * @param {Number} index The zero-based index of the list item to select
14432      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14433      * selected item if it is not currently in view (defaults to true)
14434      */
14435     select : function(index, scrollIntoView){
14436         this.selectedIndex = index;
14437         this.view.select(index);
14438         if(scrollIntoView !== false){
14439             var el = this.view.getNode(index);
14440             /*
14441              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14442              */
14443             if(el){
14444                 this.list.scrollChildIntoView(el, false);
14445             }
14446         }
14447     },
14448
14449     // private
14450     selectNext : function(){
14451         var ct = this.store.getCount();
14452         if(ct > 0){
14453             if(this.selectedIndex == -1){
14454                 this.select(0);
14455             }else if(this.selectedIndex < ct-1){
14456                 this.select(this.selectedIndex+1);
14457             }
14458         }
14459     },
14460
14461     // private
14462     selectPrev : function(){
14463         var ct = this.store.getCount();
14464         if(ct > 0){
14465             if(this.selectedIndex == -1){
14466                 this.select(0);
14467             }else if(this.selectedIndex != 0){
14468                 this.select(this.selectedIndex-1);
14469             }
14470         }
14471     },
14472
14473     // private
14474     onKeyUp : function(e){
14475         if(this.editable !== false && !e.isSpecialKey()){
14476             this.lastKey = e.getKey();
14477             this.dqTask.delay(this.queryDelay);
14478         }
14479     },
14480
14481     // private
14482     validateBlur : function(){
14483         return !this.list || !this.list.isVisible();   
14484     },
14485
14486     // private
14487     initQuery : function(){
14488         
14489         var v = this.getRawValue();
14490         
14491         if(this.tickable && this.editable){
14492             v = this.tickableInputEl().getValue();
14493         }
14494         
14495         this.doQuery(v);
14496     },
14497
14498     // private
14499     doForce : function(){
14500         if(this.inputEl().dom.value.length > 0){
14501             this.inputEl().dom.value =
14502                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14503              
14504         }
14505     },
14506
14507     /**
14508      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14509      * query allowing the query action to be canceled if needed.
14510      * @param {String} query The SQL query to execute
14511      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14512      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14513      * saved in the current store (defaults to false)
14514      */
14515     doQuery : function(q, forceAll){
14516         
14517         if(q === undefined || q === null){
14518             q = '';
14519         }
14520         var qe = {
14521             query: q,
14522             forceAll: forceAll,
14523             combo: this,
14524             cancel:false
14525         };
14526         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14527             return false;
14528         }
14529         q = qe.query;
14530         
14531         forceAll = qe.forceAll;
14532         if(forceAll === true || (q.length >= this.minChars)){
14533             
14534             this.hasQuery = true;
14535             
14536             if(this.lastQuery != q || this.alwaysQuery){
14537                 this.lastQuery = q;
14538                 if(this.mode == 'local'){
14539                     this.selectedIndex = -1;
14540                     if(forceAll){
14541                         this.store.clearFilter();
14542                     }else{
14543                         
14544                         if(this.specialFilter){
14545                             this.fireEvent('specialfilter', this);
14546                             this.onLoad();
14547                             return;
14548                         }
14549                         
14550                         this.store.filter(this.displayField, q);
14551                     }
14552                     
14553                     this.store.fireEvent("datachanged", this.store);
14554                     
14555                     this.onLoad();
14556                     
14557                     
14558                 }else{
14559                     
14560                     this.store.baseParams[this.queryParam] = q;
14561                     
14562                     var options = {params : this.getParams(q)};
14563                     
14564                     if(this.loadNext){
14565                         options.add = true;
14566                         options.params.start = this.page * this.pageSize;
14567                     }
14568                     
14569                     this.store.load(options);
14570                     
14571                     /*
14572                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14573                      *  we should expand the list on onLoad
14574                      *  so command out it
14575                      */
14576 //                    this.expand();
14577                 }
14578             }else{
14579                 this.selectedIndex = -1;
14580                 this.onLoad();   
14581             }
14582         }
14583         
14584         this.loadNext = false;
14585     },
14586     
14587     // private
14588     getParams : function(q){
14589         var p = {};
14590         //p[this.queryParam] = q;
14591         
14592         if(this.pageSize){
14593             p.start = 0;
14594             p.limit = this.pageSize;
14595         }
14596         return p;
14597     },
14598
14599     /**
14600      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14601      */
14602     collapse : function(){
14603         if(!this.isExpanded()){
14604             return;
14605         }
14606         
14607         this.list.hide();
14608         
14609         this.hasFocus = false;
14610         
14611         if(this.tickable){
14612             this.okBtn.hide();
14613             this.cancelBtn.hide();
14614             this.trigger.show();
14615             
14616             if(this.editable){
14617                 this.tickableInputEl().dom.value = '';
14618                 this.tickableInputEl().blur();
14619             }
14620             
14621         }
14622         
14623         Roo.get(document).un('mousedown', this.collapseIf, this);
14624         Roo.get(document).un('mousewheel', this.collapseIf, this);
14625         if (!this.editable) {
14626             Roo.get(document).un('keydown', this.listKeyPress, this);
14627         }
14628         this.fireEvent('collapse', this);
14629         
14630         this.validate();
14631     },
14632
14633     // private
14634     collapseIf : function(e){
14635         var in_combo  = e.within(this.el);
14636         var in_list =  e.within(this.list);
14637         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14638         
14639         if (in_combo || in_list || is_list) {
14640             //e.stopPropagation();
14641             return;
14642         }
14643         
14644         if(this.tickable){
14645             this.onTickableFooterButtonClick(e, false, false);
14646         }
14647
14648         this.collapse();
14649         
14650     },
14651
14652     /**
14653      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14654      */
14655     expand : function(){
14656        
14657         if(this.isExpanded() || !this.hasFocus){
14658             return;
14659         }
14660         
14661         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14662         this.list.setWidth(lw);
14663         
14664         Roo.log('expand');
14665         
14666         this.list.show();
14667         
14668         this.restrictHeight();
14669         
14670         if(this.tickable){
14671             
14672             this.tickItems = Roo.apply([], this.item);
14673             
14674             this.okBtn.show();
14675             this.cancelBtn.show();
14676             this.trigger.hide();
14677             
14678             if(this.editable){
14679                 this.tickableInputEl().focus();
14680             }
14681             
14682         }
14683         
14684         Roo.get(document).on('mousedown', this.collapseIf, this);
14685         Roo.get(document).on('mousewheel', this.collapseIf, this);
14686         if (!this.editable) {
14687             Roo.get(document).on('keydown', this.listKeyPress, this);
14688         }
14689         
14690         this.fireEvent('expand', this);
14691     },
14692
14693     // private
14694     // Implements the default empty TriggerField.onTriggerClick function
14695     onTriggerClick : function(e)
14696     {
14697         Roo.log('trigger click');
14698         
14699         if(this.disabled || !this.triggerList){
14700             return;
14701         }
14702         
14703         this.page = 0;
14704         this.loadNext = false;
14705         
14706         if(this.isExpanded()){
14707             this.collapse();
14708             if (!this.blockFocus) {
14709                 this.inputEl().focus();
14710             }
14711             
14712         }else {
14713             this.hasFocus = true;
14714             if(this.triggerAction == 'all') {
14715                 this.doQuery(this.allQuery, true);
14716             } else {
14717                 this.doQuery(this.getRawValue());
14718             }
14719             if (!this.blockFocus) {
14720                 this.inputEl().focus();
14721             }
14722         }
14723     },
14724     
14725     onTickableTriggerClick : function(e)
14726     {
14727         if(this.disabled){
14728             return;
14729         }
14730         
14731         this.page = 0;
14732         this.loadNext = false;
14733         this.hasFocus = true;
14734         
14735         if(this.triggerAction == 'all') {
14736             this.doQuery(this.allQuery, true);
14737         } else {
14738             this.doQuery(this.getRawValue());
14739         }
14740     },
14741     
14742     onSearchFieldClick : function(e)
14743     {
14744         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14745             this.onTickableFooterButtonClick(e, false, false);
14746             return;
14747         }
14748         
14749         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14750             return;
14751         }
14752         
14753         this.page = 0;
14754         this.loadNext = false;
14755         this.hasFocus = true;
14756         
14757         if(this.triggerAction == 'all') {
14758             this.doQuery(this.allQuery, true);
14759         } else {
14760             this.doQuery(this.getRawValue());
14761         }
14762     },
14763     
14764     listKeyPress : function(e)
14765     {
14766         //Roo.log('listkeypress');
14767         // scroll to first matching element based on key pres..
14768         if (e.isSpecialKey()) {
14769             return false;
14770         }
14771         var k = String.fromCharCode(e.getKey()).toUpperCase();
14772         //Roo.log(k);
14773         var match  = false;
14774         var csel = this.view.getSelectedNodes();
14775         var cselitem = false;
14776         if (csel.length) {
14777             var ix = this.view.indexOf(csel[0]);
14778             cselitem  = this.store.getAt(ix);
14779             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14780                 cselitem = false;
14781             }
14782             
14783         }
14784         
14785         this.store.each(function(v) { 
14786             if (cselitem) {
14787                 // start at existing selection.
14788                 if (cselitem.id == v.id) {
14789                     cselitem = false;
14790                 }
14791                 return true;
14792             }
14793                 
14794             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14795                 match = this.store.indexOf(v);
14796                 return false;
14797             }
14798             return true;
14799         }, this);
14800         
14801         if (match === false) {
14802             return true; // no more action?
14803         }
14804         // scroll to?
14805         this.view.select(match);
14806         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14807         sn.scrollIntoView(sn.dom.parentNode, false);
14808     },
14809     
14810     onViewScroll : function(e, t){
14811         
14812         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){
14813             return;
14814         }
14815         
14816         this.hasQuery = true;
14817         
14818         this.loading = this.list.select('.loading', true).first();
14819         
14820         if(this.loading === null){
14821             this.list.createChild({
14822                 tag: 'div',
14823                 cls: 'loading roo-select2-more-results roo-select2-active',
14824                 html: 'Loading more results...'
14825             });
14826             
14827             this.loading = this.list.select('.loading', true).first();
14828             
14829             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14830             
14831             this.loading.hide();
14832         }
14833         
14834         this.loading.show();
14835         
14836         var _combo = this;
14837         
14838         this.page++;
14839         this.loadNext = true;
14840         
14841         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14842         
14843         return;
14844     },
14845     
14846     addItem : function(o)
14847     {   
14848         var dv = ''; // display value
14849         
14850         if (this.displayField) {
14851             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14852         } else {
14853             // this is an error condition!!!
14854             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14855         }
14856         
14857         if(!dv.length){
14858             return;
14859         }
14860         
14861         var choice = this.choices.createChild({
14862             tag: 'li',
14863             cls: 'roo-select2-search-choice',
14864             cn: [
14865                 {
14866                     tag: 'div',
14867                     html: dv
14868                 },
14869                 {
14870                     tag: 'a',
14871                     href: '#',
14872                     cls: 'roo-select2-search-choice-close fa fa-times',
14873                     tabindex: '-1'
14874                 }
14875             ]
14876             
14877         }, this.searchField);
14878         
14879         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14880         
14881         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14882         
14883         this.item.push(o);
14884         
14885         this.lastData = o;
14886         
14887         this.syncValue();
14888         
14889         this.inputEl().dom.value = '';
14890         
14891         this.validate();
14892     },
14893     
14894     onRemoveItem : function(e, _self, o)
14895     {
14896         e.preventDefault();
14897         
14898         this.lastItem = Roo.apply([], this.item);
14899         
14900         var index = this.item.indexOf(o.data) * 1;
14901         
14902         if( index < 0){
14903             Roo.log('not this item?!');
14904             return;
14905         }
14906         
14907         this.item.splice(index, 1);
14908         o.item.remove();
14909         
14910         this.syncValue();
14911         
14912         this.fireEvent('remove', this, e);
14913         
14914         this.validate();
14915         
14916     },
14917     
14918     syncValue : function()
14919     {
14920         if(!this.item.length){
14921             this.clearValue();
14922             return;
14923         }
14924             
14925         var value = [];
14926         var _this = this;
14927         Roo.each(this.item, function(i){
14928             if(_this.valueField){
14929                 value.push(i[_this.valueField]);
14930                 return;
14931             }
14932
14933             value.push(i);
14934         });
14935
14936         this.value = value.join(',');
14937
14938         if(this.hiddenField){
14939             this.hiddenField.dom.value = this.value;
14940         }
14941         
14942         this.store.fireEvent("datachanged", this.store);
14943         
14944         this.validate();
14945     },
14946     
14947     clearItem : function()
14948     {
14949         if(!this.multiple){
14950             return;
14951         }
14952         
14953         this.item = [];
14954         
14955         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14956            c.remove();
14957         });
14958         
14959         this.syncValue();
14960         
14961         this.validate();
14962         
14963         if(this.tickable && !Roo.isTouch){
14964             this.view.refresh();
14965         }
14966     },
14967     
14968     inputEl: function ()
14969     {
14970         if(Roo.isIOS && this.useNativeIOS){
14971             return this.el.select('select.roo-ios-select', true).first();
14972         }
14973         
14974         if(Roo.isTouch && this.mobileTouchView){
14975             return this.el.select('input.form-control',true).first();
14976         }
14977         
14978         if(this.tickable){
14979             return this.searchField;
14980         }
14981         
14982         return this.el.select('input.form-control',true).first();
14983     },
14984     
14985     onTickableFooterButtonClick : function(e, btn, el)
14986     {
14987         e.preventDefault();
14988         
14989         this.lastItem = Roo.apply([], this.item);
14990         
14991         if(btn && btn.name == 'cancel'){
14992             this.tickItems = Roo.apply([], this.item);
14993             this.collapse();
14994             return;
14995         }
14996         
14997         this.clearItem();
14998         
14999         var _this = this;
15000         
15001         Roo.each(this.tickItems, function(o){
15002             _this.addItem(o);
15003         });
15004         
15005         this.collapse();
15006         
15007     },
15008     
15009     validate : function()
15010     {
15011         if(this.getVisibilityEl().hasClass('hidden')){
15012             return true;
15013         }
15014         
15015         var v = this.getRawValue();
15016         
15017         if(this.multiple){
15018             v = this.getValue();
15019         }
15020         
15021         if(this.disabled || this.allowBlank || v.length){
15022             this.markValid();
15023             return true;
15024         }
15025         
15026         this.markInvalid();
15027         return false;
15028     },
15029     
15030     tickableInputEl : function()
15031     {
15032         if(!this.tickable || !this.editable){
15033             return this.inputEl();
15034         }
15035         
15036         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15037     },
15038     
15039     
15040     getAutoCreateTouchView : function()
15041     {
15042         var id = Roo.id();
15043         
15044         var cfg = {
15045             cls: 'form-group' //input-group
15046         };
15047         
15048         var input =  {
15049             tag: 'input',
15050             id : id,
15051             type : this.inputType,
15052             cls : 'form-control x-combo-noedit',
15053             autocomplete: 'new-password',
15054             placeholder : this.placeholder || '',
15055             readonly : true
15056         };
15057         
15058         if (this.name) {
15059             input.name = this.name;
15060         }
15061         
15062         if (this.size) {
15063             input.cls += ' input-' + this.size;
15064         }
15065         
15066         if (this.disabled) {
15067             input.disabled = true;
15068         }
15069         
15070         var inputblock = {
15071             cls : '',
15072             cn : [
15073                 input
15074             ]
15075         };
15076         
15077         if(this.before){
15078             inputblock.cls += ' input-group';
15079             
15080             inputblock.cn.unshift({
15081                 tag :'span',
15082                 cls : 'input-group-addon input-group-prepend input-group-text',
15083                 html : this.before
15084             });
15085         }
15086         
15087         if(this.removable && !this.multiple){
15088             inputblock.cls += ' roo-removable';
15089             
15090             inputblock.cn.push({
15091                 tag: 'button',
15092                 html : 'x',
15093                 cls : 'roo-combo-removable-btn close'
15094             });
15095         }
15096
15097         if(this.hasFeedback && !this.allowBlank){
15098             
15099             inputblock.cls += ' has-feedback';
15100             
15101             inputblock.cn.push({
15102                 tag: 'span',
15103                 cls: 'glyphicon form-control-feedback'
15104             });
15105             
15106         }
15107         
15108         if (this.after) {
15109             
15110             inputblock.cls += (this.before) ? '' : ' input-group';
15111             
15112             inputblock.cn.push({
15113                 tag :'span',
15114                 cls : 'input-group-addon input-group-append input-group-text',
15115                 html : this.after
15116             });
15117         }
15118
15119         
15120         var ibwrap = inputblock;
15121         
15122         if(this.multiple){
15123             ibwrap = {
15124                 tag: 'ul',
15125                 cls: 'roo-select2-choices',
15126                 cn:[
15127                     {
15128                         tag: 'li',
15129                         cls: 'roo-select2-search-field',
15130                         cn: [
15131
15132                             inputblock
15133                         ]
15134                     }
15135                 ]
15136             };
15137         
15138             
15139         }
15140         
15141         var combobox = {
15142             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15143            cn: [
15144                 {
15145                     tag: 'input',
15146                     type : 'hidden',
15147                     cls: 'form-hidden-field'
15148                 },
15149                 ibwrap
15150             ]
15151         };
15152         
15153         if(!this.multiple && this.showToggleBtn){
15154             
15155             var caret = {
15156                         tag: 'span',
15157                         cls: 'caret'
15158             };
15159             
15160             if (this.caret != false) {
15161                 caret = {
15162                      tag: 'i',
15163                      cls: 'fa fa-' + this.caret
15164                 };
15165                 
15166             }
15167             
15168             combobox.cn.push({
15169                 tag :'span',
15170                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15171                 cn : [
15172                     caret,
15173                     {
15174                         tag: 'span',
15175                         cls: 'combobox-clear',
15176                         cn  : [
15177                             {
15178                                 tag : 'i',
15179                                 cls: 'icon-remove'
15180                             }
15181                         ]
15182                     }
15183                 ]
15184
15185             })
15186         }
15187         
15188         if(this.multiple){
15189             combobox.cls += ' roo-select2-container-multi';
15190         }
15191         
15192         var align = this.labelAlign || this.parentLabelAlign();
15193         
15194         if (align ==='left' && this.fieldLabel.length) {
15195
15196             cfg.cn = [
15197                 {
15198                    tag : 'i',
15199                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15200                    tooltip : 'This field is required'
15201                 },
15202                 {
15203                     tag: 'label',
15204                     cls : 'control-label col-form-label',
15205                     html : this.fieldLabel
15206
15207                 },
15208                 {
15209                     cls : '', 
15210                     cn: [
15211                         combobox
15212                     ]
15213                 }
15214             ];
15215             
15216             var labelCfg = cfg.cn[1];
15217             var contentCfg = cfg.cn[2];
15218             
15219
15220             if(this.indicatorpos == 'right'){
15221                 cfg.cn = [
15222                     {
15223                         tag: 'label',
15224                         'for' :  id,
15225                         cls : 'control-label col-form-label',
15226                         cn : [
15227                             {
15228                                 tag : 'span',
15229                                 html : this.fieldLabel
15230                             },
15231                             {
15232                                 tag : 'i',
15233                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15234                                 tooltip : 'This field is required'
15235                             }
15236                         ]
15237                     },
15238                     {
15239                         cls : "",
15240                         cn: [
15241                             combobox
15242                         ]
15243                     }
15244
15245                 ];
15246                 
15247                 labelCfg = cfg.cn[0];
15248                 contentCfg = cfg.cn[1];
15249             }
15250             
15251            
15252             
15253             if(this.labelWidth > 12){
15254                 labelCfg.style = "width: " + this.labelWidth + 'px';
15255             }
15256             
15257             if(this.labelWidth < 13 && this.labelmd == 0){
15258                 this.labelmd = this.labelWidth;
15259             }
15260             
15261             if(this.labellg > 0){
15262                 labelCfg.cls += ' col-lg-' + this.labellg;
15263                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15264             }
15265             
15266             if(this.labelmd > 0){
15267                 labelCfg.cls += ' col-md-' + this.labelmd;
15268                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15269             }
15270             
15271             if(this.labelsm > 0){
15272                 labelCfg.cls += ' col-sm-' + this.labelsm;
15273                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15274             }
15275             
15276             if(this.labelxs > 0){
15277                 labelCfg.cls += ' col-xs-' + this.labelxs;
15278                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15279             }
15280                 
15281                 
15282         } else if ( this.fieldLabel.length) {
15283             cfg.cn = [
15284                 {
15285                    tag : 'i',
15286                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15287                    tooltip : 'This field is required'
15288                 },
15289                 {
15290                     tag: 'label',
15291                     cls : 'control-label',
15292                     html : this.fieldLabel
15293
15294                 },
15295                 {
15296                     cls : '', 
15297                     cn: [
15298                         combobox
15299                     ]
15300                 }
15301             ];
15302             
15303             if(this.indicatorpos == 'right'){
15304                 cfg.cn = [
15305                     {
15306                         tag: 'label',
15307                         cls : 'control-label',
15308                         html : this.fieldLabel,
15309                         cn : [
15310                             {
15311                                tag : 'i',
15312                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15313                                tooltip : 'This field is required'
15314                             }
15315                         ]
15316                     },
15317                     {
15318                         cls : '', 
15319                         cn: [
15320                             combobox
15321                         ]
15322                     }
15323                 ];
15324             }
15325         } else {
15326             cfg.cn = combobox;    
15327         }
15328         
15329         
15330         var settings = this;
15331         
15332         ['xs','sm','md','lg'].map(function(size){
15333             if (settings[size]) {
15334                 cfg.cls += ' col-' + size + '-' + settings[size];
15335             }
15336         });
15337         
15338         return cfg;
15339     },
15340     
15341     initTouchView : function()
15342     {
15343         this.renderTouchView();
15344         
15345         this.touchViewEl.on('scroll', function(){
15346             this.el.dom.scrollTop = 0;
15347         }, this);
15348         
15349         this.originalValue = this.getValue();
15350         
15351         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15352         
15353         this.inputEl().on("click", this.showTouchView, this);
15354         if (this.triggerEl) {
15355             this.triggerEl.on("click", this.showTouchView, this);
15356         }
15357         
15358         
15359         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15360         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15361         
15362         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15363         
15364         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15365         this.store.on('load', this.onTouchViewLoad, this);
15366         this.store.on('loadexception', this.onTouchViewLoadException, this);
15367         
15368         if(this.hiddenName){
15369             
15370             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15371             
15372             this.hiddenField.dom.value =
15373                 this.hiddenValue !== undefined ? this.hiddenValue :
15374                 this.value !== undefined ? this.value : '';
15375         
15376             this.el.dom.removeAttribute('name');
15377             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15378         }
15379         
15380         if(this.multiple){
15381             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15382             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15383         }
15384         
15385         if(this.removable && !this.multiple){
15386             var close = this.closeTriggerEl();
15387             if(close){
15388                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15389                 close.on('click', this.removeBtnClick, this, close);
15390             }
15391         }
15392         /*
15393          * fix the bug in Safari iOS8
15394          */
15395         this.inputEl().on("focus", function(e){
15396             document.activeElement.blur();
15397         }, this);
15398         
15399         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15400         
15401         return;
15402         
15403         
15404     },
15405     
15406     renderTouchView : function()
15407     {
15408         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15409         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15410         
15411         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15412         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15413         
15414         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15415         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15416         this.touchViewBodyEl.setStyle('overflow', 'auto');
15417         
15418         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15419         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15420         
15421         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15422         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15423         
15424     },
15425     
15426     showTouchView : function()
15427     {
15428         if(this.disabled){
15429             return;
15430         }
15431         
15432         this.touchViewHeaderEl.hide();
15433
15434         if(this.modalTitle.length){
15435             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15436             this.touchViewHeaderEl.show();
15437         }
15438
15439         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15440         this.touchViewEl.show();
15441
15442         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15443         
15444         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15445         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15446
15447         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15448
15449         if(this.modalTitle.length){
15450             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15451         }
15452         
15453         this.touchViewBodyEl.setHeight(bodyHeight);
15454
15455         if(this.animate){
15456             var _this = this;
15457             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15458         }else{
15459             this.touchViewEl.addClass('in');
15460         }
15461         
15462         if(this._touchViewMask){
15463             Roo.get(document.body).addClass("x-body-masked");
15464             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15465             this._touchViewMask.setStyle('z-index', 10000);
15466             this._touchViewMask.addClass('show');
15467         }
15468         
15469         this.doTouchViewQuery();
15470         
15471     },
15472     
15473     hideTouchView : function()
15474     {
15475         this.touchViewEl.removeClass('in');
15476
15477         if(this.animate){
15478             var _this = this;
15479             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15480         }else{
15481             this.touchViewEl.setStyle('display', 'none');
15482         }
15483         
15484         if(this._touchViewMask){
15485             this._touchViewMask.removeClass('show');
15486             Roo.get(document.body).removeClass("x-body-masked");
15487         }
15488     },
15489     
15490     setTouchViewValue : function()
15491     {
15492         if(this.multiple){
15493             this.clearItem();
15494         
15495             var _this = this;
15496
15497             Roo.each(this.tickItems, function(o){
15498                 this.addItem(o);
15499             }, this);
15500         }
15501         
15502         this.hideTouchView();
15503     },
15504     
15505     doTouchViewQuery : function()
15506     {
15507         var qe = {
15508             query: '',
15509             forceAll: true,
15510             combo: this,
15511             cancel:false
15512         };
15513         
15514         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15515             return false;
15516         }
15517         
15518         if(!this.alwaysQuery || this.mode == 'local'){
15519             this.onTouchViewLoad();
15520             return;
15521         }
15522         
15523         this.store.load();
15524     },
15525     
15526     onTouchViewBeforeLoad : function(combo,opts)
15527     {
15528         return;
15529     },
15530
15531     // private
15532     onTouchViewLoad : function()
15533     {
15534         if(this.store.getCount() < 1){
15535             this.onTouchViewEmptyResults();
15536             return;
15537         }
15538         
15539         this.clearTouchView();
15540         
15541         var rawValue = this.getRawValue();
15542         
15543         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15544         
15545         this.tickItems = [];
15546         
15547         this.store.data.each(function(d, rowIndex){
15548             var row = this.touchViewListGroup.createChild(template);
15549             
15550             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15551                 row.addClass(d.data.cls);
15552             }
15553             
15554             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15555                 var cfg = {
15556                     data : d.data,
15557                     html : d.data[this.displayField]
15558                 };
15559                 
15560                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15561                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15562                 }
15563             }
15564             row.removeClass('selected');
15565             if(!this.multiple && this.valueField &&
15566                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15567             {
15568                 // radio buttons..
15569                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15570                 row.addClass('selected');
15571             }
15572             
15573             if(this.multiple && this.valueField &&
15574                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15575             {
15576                 
15577                 // checkboxes...
15578                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15579                 this.tickItems.push(d.data);
15580             }
15581             
15582             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15583             
15584         }, this);
15585         
15586         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15587         
15588         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15589
15590         if(this.modalTitle.length){
15591             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15592         }
15593
15594         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15595         
15596         if(this.mobile_restrict_height && listHeight < bodyHeight){
15597             this.touchViewBodyEl.setHeight(listHeight);
15598         }
15599         
15600         var _this = this;
15601         
15602         if(firstChecked && listHeight > bodyHeight){
15603             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15604         }
15605         
15606     },
15607     
15608     onTouchViewLoadException : function()
15609     {
15610         this.hideTouchView();
15611     },
15612     
15613     onTouchViewEmptyResults : function()
15614     {
15615         this.clearTouchView();
15616         
15617         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15618         
15619         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15620         
15621     },
15622     
15623     clearTouchView : function()
15624     {
15625         this.touchViewListGroup.dom.innerHTML = '';
15626     },
15627     
15628     onTouchViewClick : function(e, el, o)
15629     {
15630         e.preventDefault();
15631         
15632         var row = o.row;
15633         var rowIndex = o.rowIndex;
15634         
15635         var r = this.store.getAt(rowIndex);
15636         
15637         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15638             
15639             if(!this.multiple){
15640                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15641                     c.dom.removeAttribute('checked');
15642                 }, this);
15643
15644                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15645
15646                 this.setFromData(r.data);
15647
15648                 var close = this.closeTriggerEl();
15649
15650                 if(close){
15651                     close.show();
15652                 }
15653
15654                 this.hideTouchView();
15655
15656                 this.fireEvent('select', this, r, rowIndex);
15657
15658                 return;
15659             }
15660
15661             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15662                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15663                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15664                 return;
15665             }
15666
15667             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15668             this.addItem(r.data);
15669             this.tickItems.push(r.data);
15670         }
15671     },
15672     
15673     getAutoCreateNativeIOS : function()
15674     {
15675         var cfg = {
15676             cls: 'form-group' //input-group,
15677         };
15678         
15679         var combobox =  {
15680             tag: 'select',
15681             cls : 'roo-ios-select'
15682         };
15683         
15684         if (this.name) {
15685             combobox.name = this.name;
15686         }
15687         
15688         if (this.disabled) {
15689             combobox.disabled = true;
15690         }
15691         
15692         var settings = this;
15693         
15694         ['xs','sm','md','lg'].map(function(size){
15695             if (settings[size]) {
15696                 cfg.cls += ' col-' + size + '-' + settings[size];
15697             }
15698         });
15699         
15700         cfg.cn = combobox;
15701         
15702         return cfg;
15703         
15704     },
15705     
15706     initIOSView : function()
15707     {
15708         this.store.on('load', this.onIOSViewLoad, this);
15709         
15710         return;
15711     },
15712     
15713     onIOSViewLoad : function()
15714     {
15715         if(this.store.getCount() < 1){
15716             return;
15717         }
15718         
15719         this.clearIOSView();
15720         
15721         if(this.allowBlank) {
15722             
15723             var default_text = '-- SELECT --';
15724             
15725             if(this.placeholder.length){
15726                 default_text = this.placeholder;
15727             }
15728             
15729             if(this.emptyTitle.length){
15730                 default_text += ' - ' + this.emptyTitle + ' -';
15731             }
15732             
15733             var opt = this.inputEl().createChild({
15734                 tag: 'option',
15735                 value : 0,
15736                 html : default_text
15737             });
15738             
15739             var o = {};
15740             o[this.valueField] = 0;
15741             o[this.displayField] = default_text;
15742             
15743             this.ios_options.push({
15744                 data : o,
15745                 el : opt
15746             });
15747             
15748         }
15749         
15750         this.store.data.each(function(d, rowIndex){
15751             
15752             var html = '';
15753             
15754             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15755                 html = d.data[this.displayField];
15756             }
15757             
15758             var value = '';
15759             
15760             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15761                 value = d.data[this.valueField];
15762             }
15763             
15764             var option = {
15765                 tag: 'option',
15766                 value : value,
15767                 html : html
15768             };
15769             
15770             if(this.value == d.data[this.valueField]){
15771                 option['selected'] = true;
15772             }
15773             
15774             var opt = this.inputEl().createChild(option);
15775             
15776             this.ios_options.push({
15777                 data : d.data,
15778                 el : opt
15779             });
15780             
15781         }, this);
15782         
15783         this.inputEl().on('change', function(){
15784            this.fireEvent('select', this);
15785         }, this);
15786         
15787     },
15788     
15789     clearIOSView: function()
15790     {
15791         this.inputEl().dom.innerHTML = '';
15792         
15793         this.ios_options = [];
15794     },
15795     
15796     setIOSValue: function(v)
15797     {
15798         this.value = v;
15799         
15800         if(!this.ios_options){
15801             return;
15802         }
15803         
15804         Roo.each(this.ios_options, function(opts){
15805            
15806            opts.el.dom.removeAttribute('selected');
15807            
15808            if(opts.data[this.valueField] != v){
15809                return;
15810            }
15811            
15812            opts.el.dom.setAttribute('selected', true);
15813            
15814         }, this);
15815     }
15816
15817     /** 
15818     * @cfg {Boolean} grow 
15819     * @hide 
15820     */
15821     /** 
15822     * @cfg {Number} growMin 
15823     * @hide 
15824     */
15825     /** 
15826     * @cfg {Number} growMax 
15827     * @hide 
15828     */
15829     /**
15830      * @hide
15831      * @method autoSize
15832      */
15833 });
15834
15835 Roo.apply(Roo.bootstrap.ComboBox,  {
15836     
15837     header : {
15838         tag: 'div',
15839         cls: 'modal-header',
15840         cn: [
15841             {
15842                 tag: 'h4',
15843                 cls: 'modal-title'
15844             }
15845         ]
15846     },
15847     
15848     body : {
15849         tag: 'div',
15850         cls: 'modal-body',
15851         cn: [
15852             {
15853                 tag: 'ul',
15854                 cls: 'list-group'
15855             }
15856         ]
15857     },
15858     
15859     listItemRadio : {
15860         tag: 'li',
15861         cls: 'list-group-item',
15862         cn: [
15863             {
15864                 tag: 'span',
15865                 cls: 'roo-combobox-list-group-item-value'
15866             },
15867             {
15868                 tag: 'div',
15869                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15870                 cn: [
15871                     {
15872                         tag: 'input',
15873                         type: 'radio'
15874                     },
15875                     {
15876                         tag: 'label'
15877                     }
15878                 ]
15879             }
15880         ]
15881     },
15882     
15883     listItemCheckbox : {
15884         tag: 'li',
15885         cls: 'list-group-item',
15886         cn: [
15887             {
15888                 tag: 'span',
15889                 cls: 'roo-combobox-list-group-item-value'
15890             },
15891             {
15892                 tag: 'div',
15893                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15894                 cn: [
15895                     {
15896                         tag: 'input',
15897                         type: 'checkbox'
15898                     },
15899                     {
15900                         tag: 'label'
15901                     }
15902                 ]
15903             }
15904         ]
15905     },
15906     
15907     emptyResult : {
15908         tag: 'div',
15909         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15910     },
15911     
15912     footer : {
15913         tag: 'div',
15914         cls: 'modal-footer',
15915         cn: [
15916             {
15917                 tag: 'div',
15918                 cls: 'row',
15919                 cn: [
15920                     {
15921                         tag: 'div',
15922                         cls: 'col-xs-6 text-left',
15923                         cn: {
15924                             tag: 'button',
15925                             cls: 'btn btn-danger roo-touch-view-cancel',
15926                             html: 'Cancel'
15927                         }
15928                     },
15929                     {
15930                         tag: 'div',
15931                         cls: 'col-xs-6 text-right',
15932                         cn: {
15933                             tag: 'button',
15934                             cls: 'btn btn-success roo-touch-view-ok',
15935                             html: 'OK'
15936                         }
15937                     }
15938                 ]
15939             }
15940         ]
15941         
15942     }
15943 });
15944
15945 Roo.apply(Roo.bootstrap.ComboBox,  {
15946     
15947     touchViewTemplate : {
15948         tag: 'div',
15949         cls: 'modal fade roo-combobox-touch-view',
15950         cn: [
15951             {
15952                 tag: 'div',
15953                 cls: 'modal-dialog',
15954                 style : 'position:fixed', // we have to fix position....
15955                 cn: [
15956                     {
15957                         tag: 'div',
15958                         cls: 'modal-content',
15959                         cn: [
15960                             Roo.bootstrap.ComboBox.header,
15961                             Roo.bootstrap.ComboBox.body,
15962                             Roo.bootstrap.ComboBox.footer
15963                         ]
15964                     }
15965                 ]
15966             }
15967         ]
15968     }
15969 });/*
15970  * Based on:
15971  * Ext JS Library 1.1.1
15972  * Copyright(c) 2006-2007, Ext JS, LLC.
15973  *
15974  * Originally Released Under LGPL - original licence link has changed is not relivant.
15975  *
15976  * Fork - LGPL
15977  * <script type="text/javascript">
15978  */
15979
15980 /**
15981  * @class Roo.View
15982  * @extends Roo.util.Observable
15983  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15984  * This class also supports single and multi selection modes. <br>
15985  * Create a data model bound view:
15986  <pre><code>
15987  var store = new Roo.data.Store(...);
15988
15989  var view = new Roo.View({
15990     el : "my-element",
15991     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15992  
15993     singleSelect: true,
15994     selectedClass: "ydataview-selected",
15995     store: store
15996  });
15997
15998  // listen for node click?
15999  view.on("click", function(vw, index, node, e){
16000  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16001  });
16002
16003  // load XML data
16004  dataModel.load("foobar.xml");
16005  </code></pre>
16006  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16007  * <br><br>
16008  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16009  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16010  * 
16011  * Note: old style constructor is still suported (container, template, config)
16012  * 
16013  * @constructor
16014  * Create a new View
16015  * @param {Object} config The config object
16016  * 
16017  */
16018 Roo.View = function(config, depreciated_tpl, depreciated_config){
16019     
16020     this.parent = false;
16021     
16022     if (typeof(depreciated_tpl) == 'undefined') {
16023         // new way.. - universal constructor.
16024         Roo.apply(this, config);
16025         this.el  = Roo.get(this.el);
16026     } else {
16027         // old format..
16028         this.el  = Roo.get(config);
16029         this.tpl = depreciated_tpl;
16030         Roo.apply(this, depreciated_config);
16031     }
16032     this.wrapEl  = this.el.wrap().wrap();
16033     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16034     
16035     
16036     if(typeof(this.tpl) == "string"){
16037         this.tpl = new Roo.Template(this.tpl);
16038     } else {
16039         // support xtype ctors..
16040         this.tpl = new Roo.factory(this.tpl, Roo);
16041     }
16042     
16043     
16044     this.tpl.compile();
16045     
16046     /** @private */
16047     this.addEvents({
16048         /**
16049          * @event beforeclick
16050          * Fires before a click is processed. Returns false to cancel the default action.
16051          * @param {Roo.View} this
16052          * @param {Number} index The index of the target node
16053          * @param {HTMLElement} node The target node
16054          * @param {Roo.EventObject} e The raw event object
16055          */
16056             "beforeclick" : true,
16057         /**
16058          * @event click
16059          * Fires when a template node is clicked.
16060          * @param {Roo.View} this
16061          * @param {Number} index The index of the target node
16062          * @param {HTMLElement} node The target node
16063          * @param {Roo.EventObject} e The raw event object
16064          */
16065             "click" : true,
16066         /**
16067          * @event dblclick
16068          * Fires when a template node is double clicked.
16069          * @param {Roo.View} this
16070          * @param {Number} index The index of the target node
16071          * @param {HTMLElement} node The target node
16072          * @param {Roo.EventObject} e The raw event object
16073          */
16074             "dblclick" : true,
16075         /**
16076          * @event contextmenu
16077          * Fires when a template node is right clicked.
16078          * @param {Roo.View} this
16079          * @param {Number} index The index of the target node
16080          * @param {HTMLElement} node The target node
16081          * @param {Roo.EventObject} e The raw event object
16082          */
16083             "contextmenu" : true,
16084         /**
16085          * @event selectionchange
16086          * Fires when the selected nodes change.
16087          * @param {Roo.View} this
16088          * @param {Array} selections Array of the selected nodes
16089          */
16090             "selectionchange" : true,
16091     
16092         /**
16093          * @event beforeselect
16094          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16095          * @param {Roo.View} this
16096          * @param {HTMLElement} node The node to be selected
16097          * @param {Array} selections Array of currently selected nodes
16098          */
16099             "beforeselect" : true,
16100         /**
16101          * @event preparedata
16102          * Fires on every row to render, to allow you to change the data.
16103          * @param {Roo.View} this
16104          * @param {Object} data to be rendered (change this)
16105          */
16106           "preparedata" : true
16107           
16108           
16109         });
16110
16111
16112
16113     this.el.on({
16114         "click": this.onClick,
16115         "dblclick": this.onDblClick,
16116         "contextmenu": this.onContextMenu,
16117         scope:this
16118     });
16119
16120     this.selections = [];
16121     this.nodes = [];
16122     this.cmp = new Roo.CompositeElementLite([]);
16123     if(this.store){
16124         this.store = Roo.factory(this.store, Roo.data);
16125         this.setStore(this.store, true);
16126     }
16127     
16128     if ( this.footer && this.footer.xtype) {
16129            
16130          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16131         
16132         this.footer.dataSource = this.store;
16133         this.footer.container = fctr;
16134         this.footer = Roo.factory(this.footer, Roo);
16135         fctr.insertFirst(this.el);
16136         
16137         // this is a bit insane - as the paging toolbar seems to detach the el..
16138 //        dom.parentNode.parentNode.parentNode
16139          // they get detached?
16140     }
16141     
16142     
16143     Roo.View.superclass.constructor.call(this);
16144     
16145     
16146 };
16147
16148 Roo.extend(Roo.View, Roo.util.Observable, {
16149     
16150      /**
16151      * @cfg {Roo.data.Store} store Data store to load data from.
16152      */
16153     store : false,
16154     
16155     /**
16156      * @cfg {String|Roo.Element} el The container element.
16157      */
16158     el : '',
16159     
16160     /**
16161      * @cfg {String|Roo.Template} tpl The template used by this View 
16162      */
16163     tpl : false,
16164     /**
16165      * @cfg {String} dataName the named area of the template to use as the data area
16166      *                          Works with domtemplates roo-name="name"
16167      */
16168     dataName: false,
16169     /**
16170      * @cfg {String} selectedClass The css class to add to selected nodes
16171      */
16172     selectedClass : "x-view-selected",
16173      /**
16174      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16175      */
16176     emptyText : "",
16177     
16178     /**
16179      * @cfg {String} text to display on mask (default Loading)
16180      */
16181     mask : false,
16182     /**
16183      * @cfg {Boolean} multiSelect Allow multiple selection
16184      */
16185     multiSelect : false,
16186     /**
16187      * @cfg {Boolean} singleSelect Allow single selection
16188      */
16189     singleSelect:  false,
16190     
16191     /**
16192      * @cfg {Boolean} toggleSelect - selecting 
16193      */
16194     toggleSelect : false,
16195     
16196     /**
16197      * @cfg {Boolean} tickable - selecting 
16198      */
16199     tickable : false,
16200     
16201     /**
16202      * Returns the element this view is bound to.
16203      * @return {Roo.Element}
16204      */
16205     getEl : function(){
16206         return this.wrapEl;
16207     },
16208     
16209     
16210
16211     /**
16212      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16213      */
16214     refresh : function(){
16215         //Roo.log('refresh');
16216         var t = this.tpl;
16217         
16218         // if we are using something like 'domtemplate', then
16219         // the what gets used is:
16220         // t.applySubtemplate(NAME, data, wrapping data..)
16221         // the outer template then get' applied with
16222         //     the store 'extra data'
16223         // and the body get's added to the
16224         //      roo-name="data" node?
16225         //      <span class='roo-tpl-{name}'></span> ?????
16226         
16227         
16228         
16229         this.clearSelections();
16230         this.el.update("");
16231         var html = [];
16232         var records = this.store.getRange();
16233         if(records.length < 1) {
16234             
16235             // is this valid??  = should it render a template??
16236             
16237             this.el.update(this.emptyText);
16238             return;
16239         }
16240         var el = this.el;
16241         if (this.dataName) {
16242             this.el.update(t.apply(this.store.meta)); //????
16243             el = this.el.child('.roo-tpl-' + this.dataName);
16244         }
16245         
16246         for(var i = 0, len = records.length; i < len; i++){
16247             var data = this.prepareData(records[i].data, i, records[i]);
16248             this.fireEvent("preparedata", this, data, i, records[i]);
16249             
16250             var d = Roo.apply({}, data);
16251             
16252             if(this.tickable){
16253                 Roo.apply(d, {'roo-id' : Roo.id()});
16254                 
16255                 var _this = this;
16256             
16257                 Roo.each(this.parent.item, function(item){
16258                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16259                         return;
16260                     }
16261                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16262                 });
16263             }
16264             
16265             html[html.length] = Roo.util.Format.trim(
16266                 this.dataName ?
16267                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16268                     t.apply(d)
16269             );
16270         }
16271         
16272         
16273         
16274         el.update(html.join(""));
16275         this.nodes = el.dom.childNodes;
16276         this.updateIndexes(0);
16277     },
16278     
16279
16280     /**
16281      * Function to override to reformat the data that is sent to
16282      * the template for each node.
16283      * DEPRICATED - use the preparedata event handler.
16284      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16285      * a JSON object for an UpdateManager bound view).
16286      */
16287     prepareData : function(data, index, record)
16288     {
16289         this.fireEvent("preparedata", this, data, index, record);
16290         return data;
16291     },
16292
16293     onUpdate : function(ds, record){
16294         // Roo.log('on update');   
16295         this.clearSelections();
16296         var index = this.store.indexOf(record);
16297         var n = this.nodes[index];
16298         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16299         n.parentNode.removeChild(n);
16300         this.updateIndexes(index, index);
16301     },
16302
16303     
16304     
16305 // --------- FIXME     
16306     onAdd : function(ds, records, index)
16307     {
16308         //Roo.log(['on Add', ds, records, index] );        
16309         this.clearSelections();
16310         if(this.nodes.length == 0){
16311             this.refresh();
16312             return;
16313         }
16314         var n = this.nodes[index];
16315         for(var i = 0, len = records.length; i < len; i++){
16316             var d = this.prepareData(records[i].data, i, records[i]);
16317             if(n){
16318                 this.tpl.insertBefore(n, d);
16319             }else{
16320                 
16321                 this.tpl.append(this.el, d);
16322             }
16323         }
16324         this.updateIndexes(index);
16325     },
16326
16327     onRemove : function(ds, record, index){
16328        // Roo.log('onRemove');
16329         this.clearSelections();
16330         var el = this.dataName  ?
16331             this.el.child('.roo-tpl-' + this.dataName) :
16332             this.el; 
16333         
16334         el.dom.removeChild(this.nodes[index]);
16335         this.updateIndexes(index);
16336     },
16337
16338     /**
16339      * Refresh an individual node.
16340      * @param {Number} index
16341      */
16342     refreshNode : function(index){
16343         this.onUpdate(this.store, this.store.getAt(index));
16344     },
16345
16346     updateIndexes : function(startIndex, endIndex){
16347         var ns = this.nodes;
16348         startIndex = startIndex || 0;
16349         endIndex = endIndex || ns.length - 1;
16350         for(var i = startIndex; i <= endIndex; i++){
16351             ns[i].nodeIndex = i;
16352         }
16353     },
16354
16355     /**
16356      * Changes the data store this view uses and refresh the view.
16357      * @param {Store} store
16358      */
16359     setStore : function(store, initial){
16360         if(!initial && this.store){
16361             this.store.un("datachanged", this.refresh);
16362             this.store.un("add", this.onAdd);
16363             this.store.un("remove", this.onRemove);
16364             this.store.un("update", this.onUpdate);
16365             this.store.un("clear", this.refresh);
16366             this.store.un("beforeload", this.onBeforeLoad);
16367             this.store.un("load", this.onLoad);
16368             this.store.un("loadexception", this.onLoad);
16369         }
16370         if(store){
16371           
16372             store.on("datachanged", this.refresh, this);
16373             store.on("add", this.onAdd, this);
16374             store.on("remove", this.onRemove, this);
16375             store.on("update", this.onUpdate, this);
16376             store.on("clear", this.refresh, this);
16377             store.on("beforeload", this.onBeforeLoad, this);
16378             store.on("load", this.onLoad, this);
16379             store.on("loadexception", this.onLoad, this);
16380         }
16381         
16382         if(store){
16383             this.refresh();
16384         }
16385     },
16386     /**
16387      * onbeforeLoad - masks the loading area.
16388      *
16389      */
16390     onBeforeLoad : function(store,opts)
16391     {
16392          //Roo.log('onBeforeLoad');   
16393         if (!opts.add) {
16394             this.el.update("");
16395         }
16396         this.el.mask(this.mask ? this.mask : "Loading" ); 
16397     },
16398     onLoad : function ()
16399     {
16400         this.el.unmask();
16401     },
16402     
16403
16404     /**
16405      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16406      * @param {HTMLElement} node
16407      * @return {HTMLElement} The template node
16408      */
16409     findItemFromChild : function(node){
16410         var el = this.dataName  ?
16411             this.el.child('.roo-tpl-' + this.dataName,true) :
16412             this.el.dom; 
16413         
16414         if(!node || node.parentNode == el){
16415                     return node;
16416             }
16417             var p = node.parentNode;
16418             while(p && p != el){
16419             if(p.parentNode == el){
16420                 return p;
16421             }
16422             p = p.parentNode;
16423         }
16424             return null;
16425     },
16426
16427     /** @ignore */
16428     onClick : function(e){
16429         var item = this.findItemFromChild(e.getTarget());
16430         if(item){
16431             var index = this.indexOf(item);
16432             if(this.onItemClick(item, index, e) !== false){
16433                 this.fireEvent("click", this, index, item, e);
16434             }
16435         }else{
16436             this.clearSelections();
16437         }
16438     },
16439
16440     /** @ignore */
16441     onContextMenu : function(e){
16442         var item = this.findItemFromChild(e.getTarget());
16443         if(item){
16444             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16445         }
16446     },
16447
16448     /** @ignore */
16449     onDblClick : function(e){
16450         var item = this.findItemFromChild(e.getTarget());
16451         if(item){
16452             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16453         }
16454     },
16455
16456     onItemClick : function(item, index, e)
16457     {
16458         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16459             return false;
16460         }
16461         if (this.toggleSelect) {
16462             var m = this.isSelected(item) ? 'unselect' : 'select';
16463             //Roo.log(m);
16464             var _t = this;
16465             _t[m](item, true, false);
16466             return true;
16467         }
16468         if(this.multiSelect || this.singleSelect){
16469             if(this.multiSelect && e.shiftKey && this.lastSelection){
16470                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16471             }else{
16472                 this.select(item, this.multiSelect && e.ctrlKey);
16473                 this.lastSelection = item;
16474             }
16475             
16476             if(!this.tickable){
16477                 e.preventDefault();
16478             }
16479             
16480         }
16481         return true;
16482     },
16483
16484     /**
16485      * Get the number of selected nodes.
16486      * @return {Number}
16487      */
16488     getSelectionCount : function(){
16489         return this.selections.length;
16490     },
16491
16492     /**
16493      * Get the currently selected nodes.
16494      * @return {Array} An array of HTMLElements
16495      */
16496     getSelectedNodes : function(){
16497         return this.selections;
16498     },
16499
16500     /**
16501      * Get the indexes of the selected nodes.
16502      * @return {Array}
16503      */
16504     getSelectedIndexes : function(){
16505         var indexes = [], s = this.selections;
16506         for(var i = 0, len = s.length; i < len; i++){
16507             indexes.push(s[i].nodeIndex);
16508         }
16509         return indexes;
16510     },
16511
16512     /**
16513      * Clear all selections
16514      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16515      */
16516     clearSelections : function(suppressEvent){
16517         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16518             this.cmp.elements = this.selections;
16519             this.cmp.removeClass(this.selectedClass);
16520             this.selections = [];
16521             if(!suppressEvent){
16522                 this.fireEvent("selectionchange", this, this.selections);
16523             }
16524         }
16525     },
16526
16527     /**
16528      * Returns true if the passed node is selected
16529      * @param {HTMLElement/Number} node The node or node index
16530      * @return {Boolean}
16531      */
16532     isSelected : function(node){
16533         var s = this.selections;
16534         if(s.length < 1){
16535             return false;
16536         }
16537         node = this.getNode(node);
16538         return s.indexOf(node) !== -1;
16539     },
16540
16541     /**
16542      * Selects nodes.
16543      * @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
16544      * @param {Boolean} keepExisting (optional) true to keep existing selections
16545      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16546      */
16547     select : function(nodeInfo, keepExisting, suppressEvent){
16548         if(nodeInfo instanceof Array){
16549             if(!keepExisting){
16550                 this.clearSelections(true);
16551             }
16552             for(var i = 0, len = nodeInfo.length; i < len; i++){
16553                 this.select(nodeInfo[i], true, true);
16554             }
16555             return;
16556         } 
16557         var node = this.getNode(nodeInfo);
16558         if(!node || this.isSelected(node)){
16559             return; // already selected.
16560         }
16561         if(!keepExisting){
16562             this.clearSelections(true);
16563         }
16564         
16565         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16566             Roo.fly(node).addClass(this.selectedClass);
16567             this.selections.push(node);
16568             if(!suppressEvent){
16569                 this.fireEvent("selectionchange", this, this.selections);
16570             }
16571         }
16572         
16573         
16574     },
16575       /**
16576      * Unselects nodes.
16577      * @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
16578      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16579      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16580      */
16581     unselect : function(nodeInfo, keepExisting, suppressEvent)
16582     {
16583         if(nodeInfo instanceof Array){
16584             Roo.each(this.selections, function(s) {
16585                 this.unselect(s, nodeInfo);
16586             }, this);
16587             return;
16588         }
16589         var node = this.getNode(nodeInfo);
16590         if(!node || !this.isSelected(node)){
16591             //Roo.log("not selected");
16592             return; // not selected.
16593         }
16594         // fireevent???
16595         var ns = [];
16596         Roo.each(this.selections, function(s) {
16597             if (s == node ) {
16598                 Roo.fly(node).removeClass(this.selectedClass);
16599
16600                 return;
16601             }
16602             ns.push(s);
16603         },this);
16604         
16605         this.selections= ns;
16606         this.fireEvent("selectionchange", this, this.selections);
16607     },
16608
16609     /**
16610      * Gets a template node.
16611      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16612      * @return {HTMLElement} The node or null if it wasn't found
16613      */
16614     getNode : function(nodeInfo){
16615         if(typeof nodeInfo == "string"){
16616             return document.getElementById(nodeInfo);
16617         }else if(typeof nodeInfo == "number"){
16618             return this.nodes[nodeInfo];
16619         }
16620         return nodeInfo;
16621     },
16622
16623     /**
16624      * Gets a range template nodes.
16625      * @param {Number} startIndex
16626      * @param {Number} endIndex
16627      * @return {Array} An array of nodes
16628      */
16629     getNodes : function(start, end){
16630         var ns = this.nodes;
16631         start = start || 0;
16632         end = typeof end == "undefined" ? ns.length - 1 : end;
16633         var nodes = [];
16634         if(start <= end){
16635             for(var i = start; i <= end; i++){
16636                 nodes.push(ns[i]);
16637             }
16638         } else{
16639             for(var i = start; i >= end; i--){
16640                 nodes.push(ns[i]);
16641             }
16642         }
16643         return nodes;
16644     },
16645
16646     /**
16647      * Finds the index of the passed node
16648      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16649      * @return {Number} The index of the node or -1
16650      */
16651     indexOf : function(node){
16652         node = this.getNode(node);
16653         if(typeof node.nodeIndex == "number"){
16654             return node.nodeIndex;
16655         }
16656         var ns = this.nodes;
16657         for(var i = 0, len = ns.length; i < len; i++){
16658             if(ns[i] == node){
16659                 return i;
16660             }
16661         }
16662         return -1;
16663     }
16664 });
16665 /*
16666  * - LGPL
16667  *
16668  * based on jquery fullcalendar
16669  * 
16670  */
16671
16672 Roo.bootstrap = Roo.bootstrap || {};
16673 /**
16674  * @class Roo.bootstrap.Calendar
16675  * @extends Roo.bootstrap.Component
16676  * Bootstrap Calendar class
16677  * @cfg {Boolean} loadMask (true|false) default false
16678  * @cfg {Object} header generate the user specific header of the calendar, default false
16679
16680  * @constructor
16681  * Create a new Container
16682  * @param {Object} config The config object
16683  */
16684
16685
16686
16687 Roo.bootstrap.Calendar = function(config){
16688     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16689      this.addEvents({
16690         /**
16691              * @event select
16692              * Fires when a date is selected
16693              * @param {DatePicker} this
16694              * @param {Date} date The selected date
16695              */
16696         'select': true,
16697         /**
16698              * @event monthchange
16699              * Fires when the displayed month changes 
16700              * @param {DatePicker} this
16701              * @param {Date} date The selected month
16702              */
16703         'monthchange': true,
16704         /**
16705              * @event evententer
16706              * Fires when mouse over an event
16707              * @param {Calendar} this
16708              * @param {event} Event
16709              */
16710         'evententer': true,
16711         /**
16712              * @event eventleave
16713              * Fires when the mouse leaves an
16714              * @param {Calendar} this
16715              * @param {event}
16716              */
16717         'eventleave': true,
16718         /**
16719              * @event eventclick
16720              * Fires when the mouse click an
16721              * @param {Calendar} this
16722              * @param {event}
16723              */
16724         'eventclick': true
16725         
16726     });
16727
16728 };
16729
16730 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16731     
16732      /**
16733      * @cfg {Number} startDay
16734      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16735      */
16736     startDay : 0,
16737     
16738     loadMask : false,
16739     
16740     header : false,
16741       
16742     getAutoCreate : function(){
16743         
16744         
16745         var fc_button = function(name, corner, style, content ) {
16746             return Roo.apply({},{
16747                 tag : 'span',
16748                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16749                          (corner.length ?
16750                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16751                             ''
16752                         ),
16753                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16754                 unselectable: 'on'
16755             });
16756         };
16757         
16758         var header = {};
16759         
16760         if(!this.header){
16761             header = {
16762                 tag : 'table',
16763                 cls : 'fc-header',
16764                 style : 'width:100%',
16765                 cn : [
16766                     {
16767                         tag: 'tr',
16768                         cn : [
16769                             {
16770                                 tag : 'td',
16771                                 cls : 'fc-header-left',
16772                                 cn : [
16773                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16774                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16775                                     { tag: 'span', cls: 'fc-header-space' },
16776                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16777
16778
16779                                 ]
16780                             },
16781
16782                             {
16783                                 tag : 'td',
16784                                 cls : 'fc-header-center',
16785                                 cn : [
16786                                     {
16787                                         tag: 'span',
16788                                         cls: 'fc-header-title',
16789                                         cn : {
16790                                             tag: 'H2',
16791                                             html : 'month / year'
16792                                         }
16793                                     }
16794
16795                                 ]
16796                             },
16797                             {
16798                                 tag : 'td',
16799                                 cls : 'fc-header-right',
16800                                 cn : [
16801                               /*      fc_button('month', 'left', '', 'month' ),
16802                                     fc_button('week', '', '', 'week' ),
16803                                     fc_button('day', 'right', '', 'day' )
16804                                 */    
16805
16806                                 ]
16807                             }
16808
16809                         ]
16810                     }
16811                 ]
16812             };
16813         }
16814         
16815         header = this.header;
16816         
16817        
16818         var cal_heads = function() {
16819             var ret = [];
16820             // fixme - handle this.
16821             
16822             for (var i =0; i < Date.dayNames.length; i++) {
16823                 var d = Date.dayNames[i];
16824                 ret.push({
16825                     tag: 'th',
16826                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16827                     html : d.substring(0,3)
16828                 });
16829                 
16830             }
16831             ret[0].cls += ' fc-first';
16832             ret[6].cls += ' fc-last';
16833             return ret;
16834         };
16835         var cal_cell = function(n) {
16836             return  {
16837                 tag: 'td',
16838                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16839                 cn : [
16840                     {
16841                         cn : [
16842                             {
16843                                 cls: 'fc-day-number',
16844                                 html: 'D'
16845                             },
16846                             {
16847                                 cls: 'fc-day-content',
16848                              
16849                                 cn : [
16850                                      {
16851                                         style: 'position: relative;' // height: 17px;
16852                                     }
16853                                 ]
16854                             }
16855                             
16856                             
16857                         ]
16858                     }
16859                 ]
16860                 
16861             }
16862         };
16863         var cal_rows = function() {
16864             
16865             var ret = [];
16866             for (var r = 0; r < 6; r++) {
16867                 var row= {
16868                     tag : 'tr',
16869                     cls : 'fc-week',
16870                     cn : []
16871                 };
16872                 
16873                 for (var i =0; i < Date.dayNames.length; i++) {
16874                     var d = Date.dayNames[i];
16875                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16876
16877                 }
16878                 row.cn[0].cls+=' fc-first';
16879                 row.cn[0].cn[0].style = 'min-height:90px';
16880                 row.cn[6].cls+=' fc-last';
16881                 ret.push(row);
16882                 
16883             }
16884             ret[0].cls += ' fc-first';
16885             ret[4].cls += ' fc-prev-last';
16886             ret[5].cls += ' fc-last';
16887             return ret;
16888             
16889         };
16890         
16891         var cal_table = {
16892             tag: 'table',
16893             cls: 'fc-border-separate',
16894             style : 'width:100%',
16895             cellspacing  : 0,
16896             cn : [
16897                 { 
16898                     tag: 'thead',
16899                     cn : [
16900                         { 
16901                             tag: 'tr',
16902                             cls : 'fc-first fc-last',
16903                             cn : cal_heads()
16904                         }
16905                     ]
16906                 },
16907                 { 
16908                     tag: 'tbody',
16909                     cn : cal_rows()
16910                 }
16911                   
16912             ]
16913         };
16914          
16915          var cfg = {
16916             cls : 'fc fc-ltr',
16917             cn : [
16918                 header,
16919                 {
16920                     cls : 'fc-content',
16921                     style : "position: relative;",
16922                     cn : [
16923                         {
16924                             cls : 'fc-view fc-view-month fc-grid',
16925                             style : 'position: relative',
16926                             unselectable : 'on',
16927                             cn : [
16928                                 {
16929                                     cls : 'fc-event-container',
16930                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16931                                 },
16932                                 cal_table
16933                             ]
16934                         }
16935                     ]
16936     
16937                 }
16938            ] 
16939             
16940         };
16941         
16942          
16943         
16944         return cfg;
16945     },
16946     
16947     
16948     initEvents : function()
16949     {
16950         if(!this.store){
16951             throw "can not find store for calendar";
16952         }
16953         
16954         var mark = {
16955             tag: "div",
16956             cls:"x-dlg-mask",
16957             style: "text-align:center",
16958             cn: [
16959                 {
16960                     tag: "div",
16961                     style: "background-color:white;width:50%;margin:250 auto",
16962                     cn: [
16963                         {
16964                             tag: "img",
16965                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16966                         },
16967                         {
16968                             tag: "span",
16969                             html: "Loading"
16970                         }
16971                         
16972                     ]
16973                 }
16974             ]
16975         };
16976         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16977         
16978         var size = this.el.select('.fc-content', true).first().getSize();
16979         this.maskEl.setSize(size.width, size.height);
16980         this.maskEl.enableDisplayMode("block");
16981         if(!this.loadMask){
16982             this.maskEl.hide();
16983         }
16984         
16985         this.store = Roo.factory(this.store, Roo.data);
16986         this.store.on('load', this.onLoad, this);
16987         this.store.on('beforeload', this.onBeforeLoad, this);
16988         
16989         this.resize();
16990         
16991         this.cells = this.el.select('.fc-day',true);
16992         //Roo.log(this.cells);
16993         this.textNodes = this.el.query('.fc-day-number');
16994         this.cells.addClassOnOver('fc-state-hover');
16995         
16996         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16997         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16998         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16999         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17000         
17001         this.on('monthchange', this.onMonthChange, this);
17002         
17003         this.update(new Date().clearTime());
17004     },
17005     
17006     resize : function() {
17007         var sz  = this.el.getSize();
17008         
17009         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17010         this.el.select('.fc-day-content div',true).setHeight(34);
17011     },
17012     
17013     
17014     // private
17015     showPrevMonth : function(e){
17016         this.update(this.activeDate.add("mo", -1));
17017     },
17018     showToday : function(e){
17019         this.update(new Date().clearTime());
17020     },
17021     // private
17022     showNextMonth : function(e){
17023         this.update(this.activeDate.add("mo", 1));
17024     },
17025
17026     // private
17027     showPrevYear : function(){
17028         this.update(this.activeDate.add("y", -1));
17029     },
17030
17031     // private
17032     showNextYear : function(){
17033         this.update(this.activeDate.add("y", 1));
17034     },
17035
17036     
17037    // private
17038     update : function(date)
17039     {
17040         var vd = this.activeDate;
17041         this.activeDate = date;
17042 //        if(vd && this.el){
17043 //            var t = date.getTime();
17044 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17045 //                Roo.log('using add remove');
17046 //                
17047 //                this.fireEvent('monthchange', this, date);
17048 //                
17049 //                this.cells.removeClass("fc-state-highlight");
17050 //                this.cells.each(function(c){
17051 //                   if(c.dateValue == t){
17052 //                       c.addClass("fc-state-highlight");
17053 //                       setTimeout(function(){
17054 //                            try{c.dom.firstChild.focus();}catch(e){}
17055 //                       }, 50);
17056 //                       return false;
17057 //                   }
17058 //                   return true;
17059 //                });
17060 //                return;
17061 //            }
17062 //        }
17063         
17064         var days = date.getDaysInMonth();
17065         
17066         var firstOfMonth = date.getFirstDateOfMonth();
17067         var startingPos = firstOfMonth.getDay()-this.startDay;
17068         
17069         if(startingPos < this.startDay){
17070             startingPos += 7;
17071         }
17072         
17073         var pm = date.add(Date.MONTH, -1);
17074         var prevStart = pm.getDaysInMonth()-startingPos;
17075 //        
17076         this.cells = this.el.select('.fc-day',true);
17077         this.textNodes = this.el.query('.fc-day-number');
17078         this.cells.addClassOnOver('fc-state-hover');
17079         
17080         var cells = this.cells.elements;
17081         var textEls = this.textNodes;
17082         
17083         Roo.each(cells, function(cell){
17084             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17085         });
17086         
17087         days += startingPos;
17088
17089         // convert everything to numbers so it's fast
17090         var day = 86400000;
17091         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17092         //Roo.log(d);
17093         //Roo.log(pm);
17094         //Roo.log(prevStart);
17095         
17096         var today = new Date().clearTime().getTime();
17097         var sel = date.clearTime().getTime();
17098         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17099         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17100         var ddMatch = this.disabledDatesRE;
17101         var ddText = this.disabledDatesText;
17102         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17103         var ddaysText = this.disabledDaysText;
17104         var format = this.format;
17105         
17106         var setCellClass = function(cal, cell){
17107             cell.row = 0;
17108             cell.events = [];
17109             cell.more = [];
17110             //Roo.log('set Cell Class');
17111             cell.title = "";
17112             var t = d.getTime();
17113             
17114             //Roo.log(d);
17115             
17116             cell.dateValue = t;
17117             if(t == today){
17118                 cell.className += " fc-today";
17119                 cell.className += " fc-state-highlight";
17120                 cell.title = cal.todayText;
17121             }
17122             if(t == sel){
17123                 // disable highlight in other month..
17124                 //cell.className += " fc-state-highlight";
17125                 
17126             }
17127             // disabling
17128             if(t < min) {
17129                 cell.className = " fc-state-disabled";
17130                 cell.title = cal.minText;
17131                 return;
17132             }
17133             if(t > max) {
17134                 cell.className = " fc-state-disabled";
17135                 cell.title = cal.maxText;
17136                 return;
17137             }
17138             if(ddays){
17139                 if(ddays.indexOf(d.getDay()) != -1){
17140                     cell.title = ddaysText;
17141                     cell.className = " fc-state-disabled";
17142                 }
17143             }
17144             if(ddMatch && format){
17145                 var fvalue = d.dateFormat(format);
17146                 if(ddMatch.test(fvalue)){
17147                     cell.title = ddText.replace("%0", fvalue);
17148                     cell.className = " fc-state-disabled";
17149                 }
17150             }
17151             
17152             if (!cell.initialClassName) {
17153                 cell.initialClassName = cell.dom.className;
17154             }
17155             
17156             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17157         };
17158
17159         var i = 0;
17160         
17161         for(; i < startingPos; i++) {
17162             textEls[i].innerHTML = (++prevStart);
17163             d.setDate(d.getDate()+1);
17164             
17165             cells[i].className = "fc-past fc-other-month";
17166             setCellClass(this, cells[i]);
17167         }
17168         
17169         var intDay = 0;
17170         
17171         for(; i < days; i++){
17172             intDay = i - startingPos + 1;
17173             textEls[i].innerHTML = (intDay);
17174             d.setDate(d.getDate()+1);
17175             
17176             cells[i].className = ''; // "x-date-active";
17177             setCellClass(this, cells[i]);
17178         }
17179         var extraDays = 0;
17180         
17181         for(; i < 42; i++) {
17182             textEls[i].innerHTML = (++extraDays);
17183             d.setDate(d.getDate()+1);
17184             
17185             cells[i].className = "fc-future fc-other-month";
17186             setCellClass(this, cells[i]);
17187         }
17188         
17189         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17190         
17191         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17192         
17193         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17194         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17195         
17196         if(totalRows != 6){
17197             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17198             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17199         }
17200         
17201         this.fireEvent('monthchange', this, date);
17202         
17203         
17204         /*
17205         if(!this.internalRender){
17206             var main = this.el.dom.firstChild;
17207             var w = main.offsetWidth;
17208             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17209             Roo.fly(main).setWidth(w);
17210             this.internalRender = true;
17211             // opera does not respect the auto grow header center column
17212             // then, after it gets a width opera refuses to recalculate
17213             // without a second pass
17214             if(Roo.isOpera && !this.secondPass){
17215                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17216                 this.secondPass = true;
17217                 this.update.defer(10, this, [date]);
17218             }
17219         }
17220         */
17221         
17222     },
17223     
17224     findCell : function(dt) {
17225         dt = dt.clearTime().getTime();
17226         var ret = false;
17227         this.cells.each(function(c){
17228             //Roo.log("check " +c.dateValue + '?=' + dt);
17229             if(c.dateValue == dt){
17230                 ret = c;
17231                 return false;
17232             }
17233             return true;
17234         });
17235         
17236         return ret;
17237     },
17238     
17239     findCells : function(ev) {
17240         var s = ev.start.clone().clearTime().getTime();
17241        // Roo.log(s);
17242         var e= ev.end.clone().clearTime().getTime();
17243        // Roo.log(e);
17244         var ret = [];
17245         this.cells.each(function(c){
17246              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17247             
17248             if(c.dateValue > e){
17249                 return ;
17250             }
17251             if(c.dateValue < s){
17252                 return ;
17253             }
17254             ret.push(c);
17255         });
17256         
17257         return ret;    
17258     },
17259     
17260 //    findBestRow: function(cells)
17261 //    {
17262 //        var ret = 0;
17263 //        
17264 //        for (var i =0 ; i < cells.length;i++) {
17265 //            ret  = Math.max(cells[i].rows || 0,ret);
17266 //        }
17267 //        return ret;
17268 //        
17269 //    },
17270     
17271     
17272     addItem : function(ev)
17273     {
17274         // look for vertical location slot in
17275         var cells = this.findCells(ev);
17276         
17277 //        ev.row = this.findBestRow(cells);
17278         
17279         // work out the location.
17280         
17281         var crow = false;
17282         var rows = [];
17283         for(var i =0; i < cells.length; i++) {
17284             
17285             cells[i].row = cells[0].row;
17286             
17287             if(i == 0){
17288                 cells[i].row = cells[i].row + 1;
17289             }
17290             
17291             if (!crow) {
17292                 crow = {
17293                     start : cells[i],
17294                     end :  cells[i]
17295                 };
17296                 continue;
17297             }
17298             if (crow.start.getY() == cells[i].getY()) {
17299                 // on same row.
17300                 crow.end = cells[i];
17301                 continue;
17302             }
17303             // different row.
17304             rows.push(crow);
17305             crow = {
17306                 start: cells[i],
17307                 end : cells[i]
17308             };
17309             
17310         }
17311         
17312         rows.push(crow);
17313         ev.els = [];
17314         ev.rows = rows;
17315         ev.cells = cells;
17316         
17317         cells[0].events.push(ev);
17318         
17319         this.calevents.push(ev);
17320     },
17321     
17322     clearEvents: function() {
17323         
17324         if(!this.calevents){
17325             return;
17326         }
17327         
17328         Roo.each(this.cells.elements, function(c){
17329             c.row = 0;
17330             c.events = [];
17331             c.more = [];
17332         });
17333         
17334         Roo.each(this.calevents, function(e) {
17335             Roo.each(e.els, function(el) {
17336                 el.un('mouseenter' ,this.onEventEnter, this);
17337                 el.un('mouseleave' ,this.onEventLeave, this);
17338                 el.remove();
17339             },this);
17340         },this);
17341         
17342         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17343             e.remove();
17344         });
17345         
17346     },
17347     
17348     renderEvents: function()
17349     {   
17350         var _this = this;
17351         
17352         this.cells.each(function(c) {
17353             
17354             if(c.row < 5){
17355                 return;
17356             }
17357             
17358             var ev = c.events;
17359             
17360             var r = 4;
17361             if(c.row != c.events.length){
17362                 r = 4 - (4 - (c.row - c.events.length));
17363             }
17364             
17365             c.events = ev.slice(0, r);
17366             c.more = ev.slice(r);
17367             
17368             if(c.more.length && c.more.length == 1){
17369                 c.events.push(c.more.pop());
17370             }
17371             
17372             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17373             
17374         });
17375             
17376         this.cells.each(function(c) {
17377             
17378             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17379             
17380             
17381             for (var e = 0; e < c.events.length; e++){
17382                 var ev = c.events[e];
17383                 var rows = ev.rows;
17384                 
17385                 for(var i = 0; i < rows.length; i++) {
17386                 
17387                     // how many rows should it span..
17388
17389                     var  cfg = {
17390                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17391                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17392
17393                         unselectable : "on",
17394                         cn : [
17395                             {
17396                                 cls: 'fc-event-inner',
17397                                 cn : [
17398     //                                {
17399     //                                  tag:'span',
17400     //                                  cls: 'fc-event-time',
17401     //                                  html : cells.length > 1 ? '' : ev.time
17402     //                                },
17403                                     {
17404                                       tag:'span',
17405                                       cls: 'fc-event-title',
17406                                       html : String.format('{0}', ev.title)
17407                                     }
17408
17409
17410                                 ]
17411                             },
17412                             {
17413                                 cls: 'ui-resizable-handle ui-resizable-e',
17414                                 html : '&nbsp;&nbsp;&nbsp'
17415                             }
17416
17417                         ]
17418                     };
17419
17420                     if (i == 0) {
17421                         cfg.cls += ' fc-event-start';
17422                     }
17423                     if ((i+1) == rows.length) {
17424                         cfg.cls += ' fc-event-end';
17425                     }
17426
17427                     var ctr = _this.el.select('.fc-event-container',true).first();
17428                     var cg = ctr.createChild(cfg);
17429
17430                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17431                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17432
17433                     var r = (c.more.length) ? 1 : 0;
17434                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17435                     cg.setWidth(ebox.right - sbox.x -2);
17436
17437                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17438                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17439                     cg.on('click', _this.onEventClick, _this, ev);
17440
17441                     ev.els.push(cg);
17442                     
17443                 }
17444                 
17445             }
17446             
17447             
17448             if(c.more.length){
17449                 var  cfg = {
17450                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17451                     style : 'position: absolute',
17452                     unselectable : "on",
17453                     cn : [
17454                         {
17455                             cls: 'fc-event-inner',
17456                             cn : [
17457                                 {
17458                                   tag:'span',
17459                                   cls: 'fc-event-title',
17460                                   html : 'More'
17461                                 }
17462
17463
17464                             ]
17465                         },
17466                         {
17467                             cls: 'ui-resizable-handle ui-resizable-e',
17468                             html : '&nbsp;&nbsp;&nbsp'
17469                         }
17470
17471                     ]
17472                 };
17473
17474                 var ctr = _this.el.select('.fc-event-container',true).first();
17475                 var cg = ctr.createChild(cfg);
17476
17477                 var sbox = c.select('.fc-day-content',true).first().getBox();
17478                 var ebox = c.select('.fc-day-content',true).first().getBox();
17479                 //Roo.log(cg);
17480                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17481                 cg.setWidth(ebox.right - sbox.x -2);
17482
17483                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17484                 
17485             }
17486             
17487         });
17488         
17489         
17490         
17491     },
17492     
17493     onEventEnter: function (e, el,event,d) {
17494         this.fireEvent('evententer', this, el, event);
17495     },
17496     
17497     onEventLeave: function (e, el,event,d) {
17498         this.fireEvent('eventleave', this, el, event);
17499     },
17500     
17501     onEventClick: function (e, el,event,d) {
17502         this.fireEvent('eventclick', this, el, event);
17503     },
17504     
17505     onMonthChange: function () {
17506         this.store.load();
17507     },
17508     
17509     onMoreEventClick: function(e, el, more)
17510     {
17511         var _this = this;
17512         
17513         this.calpopover.placement = 'right';
17514         this.calpopover.setTitle('More');
17515         
17516         this.calpopover.setContent('');
17517         
17518         var ctr = this.calpopover.el.select('.popover-content', true).first();
17519         
17520         Roo.each(more, function(m){
17521             var cfg = {
17522                 cls : 'fc-event-hori fc-event-draggable',
17523                 html : m.title
17524             };
17525             var cg = ctr.createChild(cfg);
17526             
17527             cg.on('click', _this.onEventClick, _this, m);
17528         });
17529         
17530         this.calpopover.show(el);
17531         
17532         
17533     },
17534     
17535     onLoad: function () 
17536     {   
17537         this.calevents = [];
17538         var cal = this;
17539         
17540         if(this.store.getCount() > 0){
17541             this.store.data.each(function(d){
17542                cal.addItem({
17543                     id : d.data.id,
17544                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17545                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17546                     time : d.data.start_time,
17547                     title : d.data.title,
17548                     description : d.data.description,
17549                     venue : d.data.venue
17550                 });
17551             });
17552         }
17553         
17554         this.renderEvents();
17555         
17556         if(this.calevents.length && this.loadMask){
17557             this.maskEl.hide();
17558         }
17559     },
17560     
17561     onBeforeLoad: function()
17562     {
17563         this.clearEvents();
17564         if(this.loadMask){
17565             this.maskEl.show();
17566         }
17567     }
17568 });
17569
17570  
17571  /*
17572  * - LGPL
17573  *
17574  * element
17575  * 
17576  */
17577
17578 /**
17579  * @class Roo.bootstrap.Popover
17580  * @extends Roo.bootstrap.Component
17581  * Bootstrap Popover class
17582  * @cfg {String} html contents of the popover   (or false to use children..)
17583  * @cfg {String} title of popover (or false to hide)
17584  * @cfg {String} placement how it is placed
17585  * @cfg {String} trigger click || hover (or false to trigger manually)
17586  * @cfg {String} over what (parent or false to trigger manually.)
17587  * @cfg {Number} delay - delay before showing
17588  
17589  * @constructor
17590  * Create a new Popover
17591  * @param {Object} config The config object
17592  */
17593
17594 Roo.bootstrap.Popover = function(config){
17595     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17596     
17597     this.addEvents({
17598         // raw events
17599          /**
17600          * @event show
17601          * After the popover show
17602          * 
17603          * @param {Roo.bootstrap.Popover} this
17604          */
17605         "show" : true,
17606         /**
17607          * @event hide
17608          * After the popover hide
17609          * 
17610          * @param {Roo.bootstrap.Popover} this
17611          */
17612         "hide" : true
17613     });
17614 };
17615
17616 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17617     
17618     title: 'Fill in a title',
17619     html: false,
17620     
17621     placement : 'right',
17622     trigger : 'hover', // hover
17623     
17624     delay : 0,
17625     
17626     over: 'parent',
17627     
17628     can_build_overlaid : false,
17629     
17630     getChildContainer : function()
17631     {
17632         return this.el.select('.popover-content',true).first();
17633     },
17634     
17635     getAutoCreate : function(){
17636          
17637         var cfg = {
17638            cls : 'popover roo-dynamic',
17639            style: 'display:block',
17640            cn : [
17641                 {
17642                     cls : 'arrow'
17643                 },
17644                 {
17645                     cls : 'popover-inner',
17646                     cn : [
17647                         {
17648                             tag: 'h3',
17649                             cls: 'popover-title popover-header',
17650                             html : this.title
17651                         },
17652                         {
17653                             cls : 'popover-content popover-body',
17654                             html : this.html
17655                         }
17656                     ]
17657                     
17658                 }
17659            ]
17660         };
17661         
17662         return cfg;
17663     },
17664     setTitle: function(str)
17665     {
17666         this.title = str;
17667         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17668     },
17669     setContent: function(str)
17670     {
17671         this.html = str;
17672         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17673     },
17674     // as it get's added to the bottom of the page.
17675     onRender : function(ct, position)
17676     {
17677         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17678         if(!this.el){
17679             var cfg = Roo.apply({},  this.getAutoCreate());
17680             cfg.id = Roo.id();
17681             
17682             if (this.cls) {
17683                 cfg.cls += ' ' + this.cls;
17684             }
17685             if (this.style) {
17686                 cfg.style = this.style;
17687             }
17688             //Roo.log("adding to ");
17689             this.el = Roo.get(document.body).createChild(cfg, position);
17690 //            Roo.log(this.el);
17691         }
17692         this.initEvents();
17693     },
17694     
17695     initEvents : function()
17696     {
17697         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17698         this.el.enableDisplayMode('block');
17699         this.el.hide();
17700         if (this.over === false) {
17701             return; 
17702         }
17703         if (this.triggers === false) {
17704             return;
17705         }
17706         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17707         var triggers = this.trigger ? this.trigger.split(' ') : [];
17708         Roo.each(triggers, function(trigger) {
17709         
17710             if (trigger == 'click') {
17711                 on_el.on('click', this.toggle, this);
17712             } else if (trigger != 'manual') {
17713                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17714                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17715       
17716                 on_el.on(eventIn  ,this.enter, this);
17717                 on_el.on(eventOut, this.leave, this);
17718             }
17719         }, this);
17720         
17721     },
17722     
17723     
17724     // private
17725     timeout : null,
17726     hoverState : null,
17727     
17728     toggle : function () {
17729         this.hoverState == 'in' ? this.leave() : this.enter();
17730     },
17731     
17732     enter : function () {
17733         
17734         clearTimeout(this.timeout);
17735     
17736         this.hoverState = 'in';
17737     
17738         if (!this.delay || !this.delay.show) {
17739             this.show();
17740             return;
17741         }
17742         var _t = this;
17743         this.timeout = setTimeout(function () {
17744             if (_t.hoverState == 'in') {
17745                 _t.show();
17746             }
17747         }, this.delay.show)
17748     },
17749     
17750     leave : function() {
17751         clearTimeout(this.timeout);
17752     
17753         this.hoverState = 'out';
17754     
17755         if (!this.delay || !this.delay.hide) {
17756             this.hide();
17757             return;
17758         }
17759         var _t = this;
17760         this.timeout = setTimeout(function () {
17761             if (_t.hoverState == 'out') {
17762                 _t.hide();
17763             }
17764         }, this.delay.hide)
17765     },
17766     
17767     show : function (on_el)
17768     {
17769         if (!on_el) {
17770             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17771         }
17772         
17773         // set content.
17774         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17775         if (this.html !== false) {
17776             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17777         }
17778         this.el.removeClass([
17779             'fade','top','bottom', 'left', 'right','in',
17780             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17781         ]);
17782         if (!this.title.length) {
17783             this.el.select('.popover-title',true).hide();
17784         }
17785         
17786         var placement = typeof this.placement == 'function' ?
17787             this.placement.call(this, this.el, on_el) :
17788             this.placement;
17789             
17790         var autoToken = /\s?auto?\s?/i;
17791         var autoPlace = autoToken.test(placement);
17792         if (autoPlace) {
17793             placement = placement.replace(autoToken, '') || 'top';
17794         }
17795         
17796         //this.el.detach()
17797         //this.el.setXY([0,0]);
17798         this.el.show();
17799         this.el.dom.style.display='block';
17800         this.el.addClass(placement);
17801         
17802         //this.el.appendTo(on_el);
17803         
17804         var p = this.getPosition();
17805         var box = this.el.getBox();
17806         
17807         if (autoPlace) {
17808             // fixme..
17809         }
17810         var align = Roo.bootstrap.Popover.alignment[placement];
17811         
17812 //        Roo.log(align);
17813         this.el.alignTo(on_el, align[0],align[1]);
17814         //var arrow = this.el.select('.arrow',true).first();
17815         //arrow.set(align[2], 
17816         
17817         this.el.addClass('in');
17818         
17819         
17820         if (this.el.hasClass('fade')) {
17821             // fade it?
17822         }
17823         
17824         this.hoverState = 'in';
17825         
17826         this.fireEvent('show', this);
17827         
17828     },
17829     hide : function()
17830     {
17831         this.el.setXY([0,0]);
17832         this.el.removeClass('in');
17833         this.el.hide();
17834         this.hoverState = null;
17835         
17836         this.fireEvent('hide', this);
17837     }
17838     
17839 });
17840
17841 Roo.bootstrap.Popover.alignment = {
17842     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17843     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17844     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17845     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17846 };
17847
17848  /*
17849  * - LGPL
17850  *
17851  * Progress
17852  * 
17853  */
17854
17855 /**
17856  * @class Roo.bootstrap.Progress
17857  * @extends Roo.bootstrap.Component
17858  * Bootstrap Progress class
17859  * @cfg {Boolean} striped striped of the progress bar
17860  * @cfg {Boolean} active animated of the progress bar
17861  * 
17862  * 
17863  * @constructor
17864  * Create a new Progress
17865  * @param {Object} config The config object
17866  */
17867
17868 Roo.bootstrap.Progress = function(config){
17869     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17870 };
17871
17872 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17873     
17874     striped : false,
17875     active: false,
17876     
17877     getAutoCreate : function(){
17878         var cfg = {
17879             tag: 'div',
17880             cls: 'progress'
17881         };
17882         
17883         
17884         if(this.striped){
17885             cfg.cls += ' progress-striped';
17886         }
17887       
17888         if(this.active){
17889             cfg.cls += ' active';
17890         }
17891         
17892         
17893         return cfg;
17894     }
17895    
17896 });
17897
17898  
17899
17900  /*
17901  * - LGPL
17902  *
17903  * ProgressBar
17904  * 
17905  */
17906
17907 /**
17908  * @class Roo.bootstrap.ProgressBar
17909  * @extends Roo.bootstrap.Component
17910  * Bootstrap ProgressBar class
17911  * @cfg {Number} aria_valuenow aria-value now
17912  * @cfg {Number} aria_valuemin aria-value min
17913  * @cfg {Number} aria_valuemax aria-value max
17914  * @cfg {String} label label for the progress bar
17915  * @cfg {String} panel (success | info | warning | danger )
17916  * @cfg {String} role role of the progress bar
17917  * @cfg {String} sr_only text
17918  * 
17919  * 
17920  * @constructor
17921  * Create a new ProgressBar
17922  * @param {Object} config The config object
17923  */
17924
17925 Roo.bootstrap.ProgressBar = function(config){
17926     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17927 };
17928
17929 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17930     
17931     aria_valuenow : 0,
17932     aria_valuemin : 0,
17933     aria_valuemax : 100,
17934     label : false,
17935     panel : false,
17936     role : false,
17937     sr_only: false,
17938     
17939     getAutoCreate : function()
17940     {
17941         
17942         var cfg = {
17943             tag: 'div',
17944             cls: 'progress-bar',
17945             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17946         };
17947         
17948         if(this.sr_only){
17949             cfg.cn = {
17950                 tag: 'span',
17951                 cls: 'sr-only',
17952                 html: this.sr_only
17953             }
17954         }
17955         
17956         if(this.role){
17957             cfg.role = this.role;
17958         }
17959         
17960         if(this.aria_valuenow){
17961             cfg['aria-valuenow'] = this.aria_valuenow;
17962         }
17963         
17964         if(this.aria_valuemin){
17965             cfg['aria-valuemin'] = this.aria_valuemin;
17966         }
17967         
17968         if(this.aria_valuemax){
17969             cfg['aria-valuemax'] = this.aria_valuemax;
17970         }
17971         
17972         if(this.label && !this.sr_only){
17973             cfg.html = this.label;
17974         }
17975         
17976         if(this.panel){
17977             cfg.cls += ' progress-bar-' + this.panel;
17978         }
17979         
17980         return cfg;
17981     },
17982     
17983     update : function(aria_valuenow)
17984     {
17985         this.aria_valuenow = aria_valuenow;
17986         
17987         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17988     }
17989    
17990 });
17991
17992  
17993
17994  /*
17995  * - LGPL
17996  *
17997  * column
17998  * 
17999  */
18000
18001 /**
18002  * @class Roo.bootstrap.TabGroup
18003  * @extends Roo.bootstrap.Column
18004  * Bootstrap Column class
18005  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18006  * @cfg {Boolean} carousel true to make the group behave like a carousel
18007  * @cfg {Boolean} bullets show bullets for the panels
18008  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18009  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18010  * @cfg {Boolean} showarrow (true|false) show arrow default true
18011  * 
18012  * @constructor
18013  * Create a new TabGroup
18014  * @param {Object} config The config object
18015  */
18016
18017 Roo.bootstrap.TabGroup = function(config){
18018     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18019     if (!this.navId) {
18020         this.navId = Roo.id();
18021     }
18022     this.tabs = [];
18023     Roo.bootstrap.TabGroup.register(this);
18024     
18025 };
18026
18027 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18028     
18029     carousel : false,
18030     transition : false,
18031     bullets : 0,
18032     timer : 0,
18033     autoslide : false,
18034     slideFn : false,
18035     slideOnTouch : false,
18036     showarrow : true,
18037     
18038     getAutoCreate : function()
18039     {
18040         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18041         
18042         cfg.cls += ' tab-content';
18043         
18044         if (this.carousel) {
18045             cfg.cls += ' carousel slide';
18046             
18047             cfg.cn = [{
18048                cls : 'carousel-inner',
18049                cn : []
18050             }];
18051         
18052             if(this.bullets  && !Roo.isTouch){
18053                 
18054                 var bullets = {
18055                     cls : 'carousel-bullets',
18056                     cn : []
18057                 };
18058                
18059                 if(this.bullets_cls){
18060                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18061                 }
18062                 
18063                 bullets.cn.push({
18064                     cls : 'clear'
18065                 });
18066                 
18067                 cfg.cn[0].cn.push(bullets);
18068             }
18069             
18070             if(this.showarrow){
18071                 cfg.cn[0].cn.push({
18072                     tag : 'div',
18073                     class : 'carousel-arrow',
18074                     cn : [
18075                         {
18076                             tag : 'div',
18077                             class : 'carousel-prev',
18078                             cn : [
18079                                 {
18080                                     tag : 'i',
18081                                     class : 'fa fa-chevron-left'
18082                                 }
18083                             ]
18084                         },
18085                         {
18086                             tag : 'div',
18087                             class : 'carousel-next',
18088                             cn : [
18089                                 {
18090                                     tag : 'i',
18091                                     class : 'fa fa-chevron-right'
18092                                 }
18093                             ]
18094                         }
18095                     ]
18096                 });
18097             }
18098             
18099         }
18100         
18101         return cfg;
18102     },
18103     
18104     initEvents:  function()
18105     {
18106 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18107 //            this.el.on("touchstart", this.onTouchStart, this);
18108 //        }
18109         
18110         if(this.autoslide){
18111             var _this = this;
18112             
18113             this.slideFn = window.setInterval(function() {
18114                 _this.showPanelNext();
18115             }, this.timer);
18116         }
18117         
18118         if(this.showarrow){
18119             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18120             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18121         }
18122         
18123         
18124     },
18125     
18126 //    onTouchStart : function(e, el, o)
18127 //    {
18128 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18129 //            return;
18130 //        }
18131 //        
18132 //        this.showPanelNext();
18133 //    },
18134     
18135     
18136     getChildContainer : function()
18137     {
18138         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18139     },
18140     
18141     /**
18142     * register a Navigation item
18143     * @param {Roo.bootstrap.NavItem} the navitem to add
18144     */
18145     register : function(item)
18146     {
18147         this.tabs.push( item);
18148         item.navId = this.navId; // not really needed..
18149         this.addBullet();
18150     
18151     },
18152     
18153     getActivePanel : function()
18154     {
18155         var r = false;
18156         Roo.each(this.tabs, function(t) {
18157             if (t.active) {
18158                 r = t;
18159                 return false;
18160             }
18161             return null;
18162         });
18163         return r;
18164         
18165     },
18166     getPanelByName : function(n)
18167     {
18168         var r = false;
18169         Roo.each(this.tabs, function(t) {
18170             if (t.tabId == n) {
18171                 r = t;
18172                 return false;
18173             }
18174             return null;
18175         });
18176         return r;
18177     },
18178     indexOfPanel : function(p)
18179     {
18180         var r = false;
18181         Roo.each(this.tabs, function(t,i) {
18182             if (t.tabId == p.tabId) {
18183                 r = i;
18184                 return false;
18185             }
18186             return null;
18187         });
18188         return r;
18189     },
18190     /**
18191      * show a specific panel
18192      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18193      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18194      */
18195     showPanel : function (pan)
18196     {
18197         if(this.transition || typeof(pan) == 'undefined'){
18198             Roo.log("waiting for the transitionend");
18199             return;
18200         }
18201         
18202         if (typeof(pan) == 'number') {
18203             pan = this.tabs[pan];
18204         }
18205         
18206         if (typeof(pan) == 'string') {
18207             pan = this.getPanelByName(pan);
18208         }
18209         
18210         var cur = this.getActivePanel();
18211         
18212         if(!pan || !cur){
18213             Roo.log('pan or acitve pan is undefined');
18214             return false;
18215         }
18216         
18217         if (pan.tabId == this.getActivePanel().tabId) {
18218             return true;
18219         }
18220         
18221         if (false === cur.fireEvent('beforedeactivate')) {
18222             return false;
18223         }
18224         
18225         if(this.bullets > 0 && !Roo.isTouch){
18226             this.setActiveBullet(this.indexOfPanel(pan));
18227         }
18228         
18229         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18230             
18231             this.transition = true;
18232             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18233             var lr = dir == 'next' ? 'left' : 'right';
18234             pan.el.addClass(dir); // or prev
18235             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18236             cur.el.addClass(lr); // or right
18237             pan.el.addClass(lr);
18238             
18239             var _this = this;
18240             cur.el.on('transitionend', function() {
18241                 Roo.log("trans end?");
18242                 
18243                 pan.el.removeClass([lr,dir]);
18244                 pan.setActive(true);
18245                 
18246                 cur.el.removeClass([lr]);
18247                 cur.setActive(false);
18248                 
18249                 _this.transition = false;
18250                 
18251             }, this, { single:  true } );
18252             
18253             return true;
18254         }
18255         
18256         cur.setActive(false);
18257         pan.setActive(true);
18258         
18259         return true;
18260         
18261     },
18262     showPanelNext : function()
18263     {
18264         var i = this.indexOfPanel(this.getActivePanel());
18265         
18266         if (i >= this.tabs.length - 1 && !this.autoslide) {
18267             return;
18268         }
18269         
18270         if (i >= this.tabs.length - 1 && this.autoslide) {
18271             i = -1;
18272         }
18273         
18274         this.showPanel(this.tabs[i+1]);
18275     },
18276     
18277     showPanelPrev : function()
18278     {
18279         var i = this.indexOfPanel(this.getActivePanel());
18280         
18281         if (i  < 1 && !this.autoslide) {
18282             return;
18283         }
18284         
18285         if (i < 1 && this.autoslide) {
18286             i = this.tabs.length;
18287         }
18288         
18289         this.showPanel(this.tabs[i-1]);
18290     },
18291     
18292     
18293     addBullet: function()
18294     {
18295         if(!this.bullets || Roo.isTouch){
18296             return;
18297         }
18298         var ctr = this.el.select('.carousel-bullets',true).first();
18299         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18300         var bullet = ctr.createChild({
18301             cls : 'bullet bullet-' + i
18302         },ctr.dom.lastChild);
18303         
18304         
18305         var _this = this;
18306         
18307         bullet.on('click', (function(e, el, o, ii, t){
18308
18309             e.preventDefault();
18310
18311             this.showPanel(ii);
18312
18313             if(this.autoslide && this.slideFn){
18314                 clearInterval(this.slideFn);
18315                 this.slideFn = window.setInterval(function() {
18316                     _this.showPanelNext();
18317                 }, this.timer);
18318             }
18319
18320         }).createDelegate(this, [i, bullet], true));
18321                 
18322         
18323     },
18324      
18325     setActiveBullet : function(i)
18326     {
18327         if(Roo.isTouch){
18328             return;
18329         }
18330         
18331         Roo.each(this.el.select('.bullet', true).elements, function(el){
18332             el.removeClass('selected');
18333         });
18334
18335         var bullet = this.el.select('.bullet-' + i, true).first();
18336         
18337         if(!bullet){
18338             return;
18339         }
18340         
18341         bullet.addClass('selected');
18342     }
18343     
18344     
18345   
18346 });
18347
18348  
18349
18350  
18351  
18352 Roo.apply(Roo.bootstrap.TabGroup, {
18353     
18354     groups: {},
18355      /**
18356     * register a Navigation Group
18357     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18358     */
18359     register : function(navgrp)
18360     {
18361         this.groups[navgrp.navId] = navgrp;
18362         
18363     },
18364     /**
18365     * fetch a Navigation Group based on the navigation ID
18366     * if one does not exist , it will get created.
18367     * @param {string} the navgroup to add
18368     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18369     */
18370     get: function(navId) {
18371         if (typeof(this.groups[navId]) == 'undefined') {
18372             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18373         }
18374         return this.groups[navId] ;
18375     }
18376     
18377     
18378     
18379 });
18380
18381  /*
18382  * - LGPL
18383  *
18384  * TabPanel
18385  * 
18386  */
18387
18388 /**
18389  * @class Roo.bootstrap.TabPanel
18390  * @extends Roo.bootstrap.Component
18391  * Bootstrap TabPanel class
18392  * @cfg {Boolean} active panel active
18393  * @cfg {String} html panel content
18394  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18395  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18396  * @cfg {String} href click to link..
18397  * 
18398  * 
18399  * @constructor
18400  * Create a new TabPanel
18401  * @param {Object} config The config object
18402  */
18403
18404 Roo.bootstrap.TabPanel = function(config){
18405     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18406     this.addEvents({
18407         /**
18408              * @event changed
18409              * Fires when the active status changes
18410              * @param {Roo.bootstrap.TabPanel} this
18411              * @param {Boolean} state the new state
18412             
18413          */
18414         'changed': true,
18415         /**
18416              * @event beforedeactivate
18417              * Fires before a tab is de-activated - can be used to do validation on a form.
18418              * @param {Roo.bootstrap.TabPanel} this
18419              * @return {Boolean} false if there is an error
18420             
18421          */
18422         'beforedeactivate': true
18423      });
18424     
18425     this.tabId = this.tabId || Roo.id();
18426   
18427 };
18428
18429 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18430     
18431     active: false,
18432     html: false,
18433     tabId: false,
18434     navId : false,
18435     href : '',
18436     
18437     getAutoCreate : function(){
18438         var cfg = {
18439             tag: 'div',
18440             // item is needed for carousel - not sure if it has any effect otherwise
18441             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18442             html: this.html || ''
18443         };
18444         
18445         if(this.active){
18446             cfg.cls += ' active';
18447         }
18448         
18449         if(this.tabId){
18450             cfg.tabId = this.tabId;
18451         }
18452         
18453         
18454         return cfg;
18455     },
18456     
18457     initEvents:  function()
18458     {
18459         var p = this.parent();
18460         
18461         this.navId = this.navId || p.navId;
18462         
18463         if (typeof(this.navId) != 'undefined') {
18464             // not really needed.. but just in case.. parent should be a NavGroup.
18465             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18466             
18467             tg.register(this);
18468             
18469             var i = tg.tabs.length - 1;
18470             
18471             if(this.active && tg.bullets > 0 && i < tg.bullets){
18472                 tg.setActiveBullet(i);
18473             }
18474         }
18475         
18476         this.el.on('click', this.onClick, this);
18477         
18478         if(Roo.isTouch){
18479             this.el.on("touchstart", this.onTouchStart, this);
18480             this.el.on("touchmove", this.onTouchMove, this);
18481             this.el.on("touchend", this.onTouchEnd, this);
18482         }
18483         
18484     },
18485     
18486     onRender : function(ct, position)
18487     {
18488         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18489     },
18490     
18491     setActive : function(state)
18492     {
18493         Roo.log("panel - set active " + this.tabId + "=" + state);
18494         
18495         this.active = state;
18496         if (!state) {
18497             this.el.removeClass('active');
18498             
18499         } else  if (!this.el.hasClass('active')) {
18500             this.el.addClass('active');
18501         }
18502         
18503         this.fireEvent('changed', this, state);
18504     },
18505     
18506     onClick : function(e)
18507     {
18508         e.preventDefault();
18509         
18510         if(!this.href.length){
18511             return;
18512         }
18513         
18514         window.location.href = this.href;
18515     },
18516     
18517     startX : 0,
18518     startY : 0,
18519     endX : 0,
18520     endY : 0,
18521     swiping : false,
18522     
18523     onTouchStart : function(e)
18524     {
18525         this.swiping = false;
18526         
18527         this.startX = e.browserEvent.touches[0].clientX;
18528         this.startY = e.browserEvent.touches[0].clientY;
18529     },
18530     
18531     onTouchMove : function(e)
18532     {
18533         this.swiping = true;
18534         
18535         this.endX = e.browserEvent.touches[0].clientX;
18536         this.endY = e.browserEvent.touches[0].clientY;
18537     },
18538     
18539     onTouchEnd : function(e)
18540     {
18541         if(!this.swiping){
18542             this.onClick(e);
18543             return;
18544         }
18545         
18546         var tabGroup = this.parent();
18547         
18548         if(this.endX > this.startX){ // swiping right
18549             tabGroup.showPanelPrev();
18550             return;
18551         }
18552         
18553         if(this.startX > this.endX){ // swiping left
18554             tabGroup.showPanelNext();
18555             return;
18556         }
18557     }
18558     
18559     
18560 });
18561  
18562
18563  
18564
18565  /*
18566  * - LGPL
18567  *
18568  * DateField
18569  * 
18570  */
18571
18572 /**
18573  * @class Roo.bootstrap.DateField
18574  * @extends Roo.bootstrap.Input
18575  * Bootstrap DateField class
18576  * @cfg {Number} weekStart default 0
18577  * @cfg {String} viewMode default empty, (months|years)
18578  * @cfg {String} minViewMode default empty, (months|years)
18579  * @cfg {Number} startDate default -Infinity
18580  * @cfg {Number} endDate default Infinity
18581  * @cfg {Boolean} todayHighlight default false
18582  * @cfg {Boolean} todayBtn default false
18583  * @cfg {Boolean} calendarWeeks default false
18584  * @cfg {Object} daysOfWeekDisabled default empty
18585  * @cfg {Boolean} singleMode default false (true | false)
18586  * 
18587  * @cfg {Boolean} keyboardNavigation default true
18588  * @cfg {String} language default en
18589  * 
18590  * @constructor
18591  * Create a new DateField
18592  * @param {Object} config The config object
18593  */
18594
18595 Roo.bootstrap.DateField = function(config){
18596     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18597      this.addEvents({
18598             /**
18599              * @event show
18600              * Fires when this field show.
18601              * @param {Roo.bootstrap.DateField} this
18602              * @param {Mixed} date The date value
18603              */
18604             show : true,
18605             /**
18606              * @event show
18607              * Fires when this field hide.
18608              * @param {Roo.bootstrap.DateField} this
18609              * @param {Mixed} date The date value
18610              */
18611             hide : true,
18612             /**
18613              * @event select
18614              * Fires when select a date.
18615              * @param {Roo.bootstrap.DateField} this
18616              * @param {Mixed} date The date value
18617              */
18618             select : true,
18619             /**
18620              * @event beforeselect
18621              * Fires when before select a date.
18622              * @param {Roo.bootstrap.DateField} this
18623              * @param {Mixed} date The date value
18624              */
18625             beforeselect : true
18626         });
18627 };
18628
18629 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18630     
18631     /**
18632      * @cfg {String} format
18633      * The default date format string which can be overriden for localization support.  The format must be
18634      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18635      */
18636     format : "m/d/y",
18637     /**
18638      * @cfg {String} altFormats
18639      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18640      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18641      */
18642     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18643     
18644     weekStart : 0,
18645     
18646     viewMode : '',
18647     
18648     minViewMode : '',
18649     
18650     todayHighlight : false,
18651     
18652     todayBtn: false,
18653     
18654     language: 'en',
18655     
18656     keyboardNavigation: true,
18657     
18658     calendarWeeks: false,
18659     
18660     startDate: -Infinity,
18661     
18662     endDate: Infinity,
18663     
18664     daysOfWeekDisabled: [],
18665     
18666     _events: [],
18667     
18668     singleMode : false,
18669     
18670     UTCDate: function()
18671     {
18672         return new Date(Date.UTC.apply(Date, arguments));
18673     },
18674     
18675     UTCToday: function()
18676     {
18677         var today = new Date();
18678         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18679     },
18680     
18681     getDate: function() {
18682             var d = this.getUTCDate();
18683             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18684     },
18685     
18686     getUTCDate: function() {
18687             return this.date;
18688     },
18689     
18690     setDate: function(d) {
18691             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18692     },
18693     
18694     setUTCDate: function(d) {
18695             this.date = d;
18696             this.setValue(this.formatDate(this.date));
18697     },
18698         
18699     onRender: function(ct, position)
18700     {
18701         
18702         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18703         
18704         this.language = this.language || 'en';
18705         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18706         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18707         
18708         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18709         this.format = this.format || 'm/d/y';
18710         this.isInline = false;
18711         this.isInput = true;
18712         this.component = this.el.select('.add-on', true).first() || false;
18713         this.component = (this.component && this.component.length === 0) ? false : this.component;
18714         this.hasInput = this.component && this.inputEl().length;
18715         
18716         if (typeof(this.minViewMode === 'string')) {
18717             switch (this.minViewMode) {
18718                 case 'months':
18719                     this.minViewMode = 1;
18720                     break;
18721                 case 'years':
18722                     this.minViewMode = 2;
18723                     break;
18724                 default:
18725                     this.minViewMode = 0;
18726                     break;
18727             }
18728         }
18729         
18730         if (typeof(this.viewMode === 'string')) {
18731             switch (this.viewMode) {
18732                 case 'months':
18733                     this.viewMode = 1;
18734                     break;
18735                 case 'years':
18736                     this.viewMode = 2;
18737                     break;
18738                 default:
18739                     this.viewMode = 0;
18740                     break;
18741             }
18742         }
18743                 
18744         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18745         
18746 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18747         
18748         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18749         
18750         this.picker().on('mousedown', this.onMousedown, this);
18751         this.picker().on('click', this.onClick, this);
18752         
18753         this.picker().addClass('datepicker-dropdown');
18754         
18755         this.startViewMode = this.viewMode;
18756         
18757         if(this.singleMode){
18758             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18759                 v.setVisibilityMode(Roo.Element.DISPLAY);
18760                 v.hide();
18761             });
18762             
18763             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18764                 v.setStyle('width', '189px');
18765             });
18766         }
18767         
18768         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18769             if(!this.calendarWeeks){
18770                 v.remove();
18771                 return;
18772             }
18773             
18774             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18775             v.attr('colspan', function(i, val){
18776                 return parseInt(val) + 1;
18777             });
18778         });
18779                         
18780         
18781         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18782         
18783         this.setStartDate(this.startDate);
18784         this.setEndDate(this.endDate);
18785         
18786         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18787         
18788         this.fillDow();
18789         this.fillMonths();
18790         this.update();
18791         this.showMode();
18792         
18793         if(this.isInline) {
18794             this.showPopup();
18795         }
18796     },
18797     
18798     picker : function()
18799     {
18800         return this.pickerEl;
18801 //        return this.el.select('.datepicker', true).first();
18802     },
18803     
18804     fillDow: function()
18805     {
18806         var dowCnt = this.weekStart;
18807         
18808         var dow = {
18809             tag: 'tr',
18810             cn: [
18811                 
18812             ]
18813         };
18814         
18815         if(this.calendarWeeks){
18816             dow.cn.push({
18817                 tag: 'th',
18818                 cls: 'cw',
18819                 html: '&nbsp;'
18820             })
18821         }
18822         
18823         while (dowCnt < this.weekStart + 7) {
18824             dow.cn.push({
18825                 tag: 'th',
18826                 cls: 'dow',
18827                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18828             });
18829         }
18830         
18831         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18832     },
18833     
18834     fillMonths: function()
18835     {    
18836         var i = 0;
18837         var months = this.picker().select('>.datepicker-months td', true).first();
18838         
18839         months.dom.innerHTML = '';
18840         
18841         while (i < 12) {
18842             var month = {
18843                 tag: 'span',
18844                 cls: 'month',
18845                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18846             };
18847             
18848             months.createChild(month);
18849         }
18850         
18851     },
18852     
18853     update: function()
18854     {
18855         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;
18856         
18857         if (this.date < this.startDate) {
18858             this.viewDate = new Date(this.startDate);
18859         } else if (this.date > this.endDate) {
18860             this.viewDate = new Date(this.endDate);
18861         } else {
18862             this.viewDate = new Date(this.date);
18863         }
18864         
18865         this.fill();
18866     },
18867     
18868     fill: function() 
18869     {
18870         var d = new Date(this.viewDate),
18871                 year = d.getUTCFullYear(),
18872                 month = d.getUTCMonth(),
18873                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18874                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18875                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18876                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18877                 currentDate = this.date && this.date.valueOf(),
18878                 today = this.UTCToday();
18879         
18880         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18881         
18882 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18883         
18884 //        this.picker.select('>tfoot th.today').
18885 //                                              .text(dates[this.language].today)
18886 //                                              .toggle(this.todayBtn !== false);
18887     
18888         this.updateNavArrows();
18889         this.fillMonths();
18890                                                 
18891         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18892         
18893         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18894          
18895         prevMonth.setUTCDate(day);
18896         
18897         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18898         
18899         var nextMonth = new Date(prevMonth);
18900         
18901         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18902         
18903         nextMonth = nextMonth.valueOf();
18904         
18905         var fillMonths = false;
18906         
18907         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18908         
18909         while(prevMonth.valueOf() <= nextMonth) {
18910             var clsName = '';
18911             
18912             if (prevMonth.getUTCDay() === this.weekStart) {
18913                 if(fillMonths){
18914                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18915                 }
18916                     
18917                 fillMonths = {
18918                     tag: 'tr',
18919                     cn: []
18920                 };
18921                 
18922                 if(this.calendarWeeks){
18923                     // ISO 8601: First week contains first thursday.
18924                     // ISO also states week starts on Monday, but we can be more abstract here.
18925                     var
18926                     // Start of current week: based on weekstart/current date
18927                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18928                     // Thursday of this week
18929                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18930                     // First Thursday of year, year from thursday
18931                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18932                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18933                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18934                     
18935                     fillMonths.cn.push({
18936                         tag: 'td',
18937                         cls: 'cw',
18938                         html: calWeek
18939                     });
18940                 }
18941             }
18942             
18943             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18944                 clsName += ' old';
18945             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18946                 clsName += ' new';
18947             }
18948             if (this.todayHighlight &&
18949                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18950                 prevMonth.getUTCMonth() == today.getMonth() &&
18951                 prevMonth.getUTCDate() == today.getDate()) {
18952                 clsName += ' today';
18953             }
18954             
18955             if (currentDate && prevMonth.valueOf() === currentDate) {
18956                 clsName += ' active';
18957             }
18958             
18959             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18960                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18961                     clsName += ' disabled';
18962             }
18963             
18964             fillMonths.cn.push({
18965                 tag: 'td',
18966                 cls: 'day ' + clsName,
18967                 html: prevMonth.getDate()
18968             });
18969             
18970             prevMonth.setDate(prevMonth.getDate()+1);
18971         }
18972           
18973         var currentYear = this.date && this.date.getUTCFullYear();
18974         var currentMonth = this.date && this.date.getUTCMonth();
18975         
18976         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18977         
18978         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18979             v.removeClass('active');
18980             
18981             if(currentYear === year && k === currentMonth){
18982                 v.addClass('active');
18983             }
18984             
18985             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18986                 v.addClass('disabled');
18987             }
18988             
18989         });
18990         
18991         
18992         year = parseInt(year/10, 10) * 10;
18993         
18994         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18995         
18996         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18997         
18998         year -= 1;
18999         for (var i = -1; i < 11; i++) {
19000             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19001                 tag: 'span',
19002                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19003                 html: year
19004             });
19005             
19006             year += 1;
19007         }
19008     },
19009     
19010     showMode: function(dir) 
19011     {
19012         if (dir) {
19013             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19014         }
19015         
19016         Roo.each(this.picker().select('>div',true).elements, function(v){
19017             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19018             v.hide();
19019         });
19020         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19021     },
19022     
19023     place: function()
19024     {
19025         if(this.isInline) {
19026             return;
19027         }
19028         
19029         this.picker().removeClass(['bottom', 'top']);
19030         
19031         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19032             /*
19033              * place to the top of element!
19034              *
19035              */
19036             
19037             this.picker().addClass('top');
19038             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19039             
19040             return;
19041         }
19042         
19043         this.picker().addClass('bottom');
19044         
19045         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19046     },
19047     
19048     parseDate : function(value)
19049     {
19050         if(!value || value instanceof Date){
19051             return value;
19052         }
19053         var v = Date.parseDate(value, this.format);
19054         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19055             v = Date.parseDate(value, 'Y-m-d');
19056         }
19057         if(!v && this.altFormats){
19058             if(!this.altFormatsArray){
19059                 this.altFormatsArray = this.altFormats.split("|");
19060             }
19061             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19062                 v = Date.parseDate(value, this.altFormatsArray[i]);
19063             }
19064         }
19065         return v;
19066     },
19067     
19068     formatDate : function(date, fmt)
19069     {   
19070         return (!date || !(date instanceof Date)) ?
19071         date : date.dateFormat(fmt || this.format);
19072     },
19073     
19074     onFocus : function()
19075     {
19076         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19077         this.showPopup();
19078     },
19079     
19080     onBlur : function()
19081     {
19082         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19083         
19084         var d = this.inputEl().getValue();
19085         
19086         this.setValue(d);
19087                 
19088         this.hidePopup();
19089     },
19090     
19091     showPopup : function()
19092     {
19093         this.picker().show();
19094         this.update();
19095         this.place();
19096         
19097         this.fireEvent('showpopup', this, this.date);
19098     },
19099     
19100     hidePopup : function()
19101     {
19102         if(this.isInline) {
19103             return;
19104         }
19105         this.picker().hide();
19106         this.viewMode = this.startViewMode;
19107         this.showMode();
19108         
19109         this.fireEvent('hidepopup', this, this.date);
19110         
19111     },
19112     
19113     onMousedown: function(e)
19114     {
19115         e.stopPropagation();
19116         e.preventDefault();
19117     },
19118     
19119     keyup: function(e)
19120     {
19121         Roo.bootstrap.DateField.superclass.keyup.call(this);
19122         this.update();
19123     },
19124
19125     setValue: function(v)
19126     {
19127         if(this.fireEvent('beforeselect', this, v) !== false){
19128             var d = new Date(this.parseDate(v) ).clearTime();
19129         
19130             if(isNaN(d.getTime())){
19131                 this.date = this.viewDate = '';
19132                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19133                 return;
19134             }
19135
19136             v = this.formatDate(d);
19137
19138             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19139
19140             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19141
19142             this.update();
19143
19144             this.fireEvent('select', this, this.date);
19145         }
19146     },
19147     
19148     getValue: function()
19149     {
19150         return this.formatDate(this.date);
19151     },
19152     
19153     fireKey: function(e)
19154     {
19155         if (!this.picker().isVisible()){
19156             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19157                 this.showPopup();
19158             }
19159             return;
19160         }
19161         
19162         var dateChanged = false,
19163         dir, day, month,
19164         newDate, newViewDate;
19165         
19166         switch(e.keyCode){
19167             case 27: // escape
19168                 this.hidePopup();
19169                 e.preventDefault();
19170                 break;
19171             case 37: // left
19172             case 39: // right
19173                 if (!this.keyboardNavigation) {
19174                     break;
19175                 }
19176                 dir = e.keyCode == 37 ? -1 : 1;
19177                 
19178                 if (e.ctrlKey){
19179                     newDate = this.moveYear(this.date, dir);
19180                     newViewDate = this.moveYear(this.viewDate, dir);
19181                 } else if (e.shiftKey){
19182                     newDate = this.moveMonth(this.date, dir);
19183                     newViewDate = this.moveMonth(this.viewDate, dir);
19184                 } else {
19185                     newDate = new Date(this.date);
19186                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19187                     newViewDate = new Date(this.viewDate);
19188                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19189                 }
19190                 if (this.dateWithinRange(newDate)){
19191                     this.date = newDate;
19192                     this.viewDate = newViewDate;
19193                     this.setValue(this.formatDate(this.date));
19194 //                    this.update();
19195                     e.preventDefault();
19196                     dateChanged = true;
19197                 }
19198                 break;
19199             case 38: // up
19200             case 40: // down
19201                 if (!this.keyboardNavigation) {
19202                     break;
19203                 }
19204                 dir = e.keyCode == 38 ? -1 : 1;
19205                 if (e.ctrlKey){
19206                     newDate = this.moveYear(this.date, dir);
19207                     newViewDate = this.moveYear(this.viewDate, dir);
19208                 } else if (e.shiftKey){
19209                     newDate = this.moveMonth(this.date, dir);
19210                     newViewDate = this.moveMonth(this.viewDate, dir);
19211                 } else {
19212                     newDate = new Date(this.date);
19213                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19214                     newViewDate = new Date(this.viewDate);
19215                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19216                 }
19217                 if (this.dateWithinRange(newDate)){
19218                     this.date = newDate;
19219                     this.viewDate = newViewDate;
19220                     this.setValue(this.formatDate(this.date));
19221 //                    this.update();
19222                     e.preventDefault();
19223                     dateChanged = true;
19224                 }
19225                 break;
19226             case 13: // enter
19227                 this.setValue(this.formatDate(this.date));
19228                 this.hidePopup();
19229                 e.preventDefault();
19230                 break;
19231             case 9: // tab
19232                 this.setValue(this.formatDate(this.date));
19233                 this.hidePopup();
19234                 break;
19235             case 16: // shift
19236             case 17: // ctrl
19237             case 18: // alt
19238                 break;
19239             default :
19240                 this.hidePopup();
19241                 
19242         }
19243     },
19244     
19245     
19246     onClick: function(e) 
19247     {
19248         e.stopPropagation();
19249         e.preventDefault();
19250         
19251         var target = e.getTarget();
19252         
19253         if(target.nodeName.toLowerCase() === 'i'){
19254             target = Roo.get(target).dom.parentNode;
19255         }
19256         
19257         var nodeName = target.nodeName;
19258         var className = target.className;
19259         var html = target.innerHTML;
19260         //Roo.log(nodeName);
19261         
19262         switch(nodeName.toLowerCase()) {
19263             case 'th':
19264                 switch(className) {
19265                     case 'switch':
19266                         this.showMode(1);
19267                         break;
19268                     case 'prev':
19269                     case 'next':
19270                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19271                         switch(this.viewMode){
19272                                 case 0:
19273                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19274                                         break;
19275                                 case 1:
19276                                 case 2:
19277                                         this.viewDate = this.moveYear(this.viewDate, dir);
19278                                         break;
19279                         }
19280                         this.fill();
19281                         break;
19282                     case 'today':
19283                         var date = new Date();
19284                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19285 //                        this.fill()
19286                         this.setValue(this.formatDate(this.date));
19287                         
19288                         this.hidePopup();
19289                         break;
19290                 }
19291                 break;
19292             case 'span':
19293                 if (className.indexOf('disabled') < 0) {
19294                     this.viewDate.setUTCDate(1);
19295                     if (className.indexOf('month') > -1) {
19296                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19297                     } else {
19298                         var year = parseInt(html, 10) || 0;
19299                         this.viewDate.setUTCFullYear(year);
19300                         
19301                     }
19302                     
19303                     if(this.singleMode){
19304                         this.setValue(this.formatDate(this.viewDate));
19305                         this.hidePopup();
19306                         return;
19307                     }
19308                     
19309                     this.showMode(-1);
19310                     this.fill();
19311                 }
19312                 break;
19313                 
19314             case 'td':
19315                 //Roo.log(className);
19316                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19317                     var day = parseInt(html, 10) || 1;
19318                     var year = this.viewDate.getUTCFullYear(),
19319                         month = this.viewDate.getUTCMonth();
19320
19321                     if (className.indexOf('old') > -1) {
19322                         if(month === 0 ){
19323                             month = 11;
19324                             year -= 1;
19325                         }else{
19326                             month -= 1;
19327                         }
19328                     } else if (className.indexOf('new') > -1) {
19329                         if (month == 11) {
19330                             month = 0;
19331                             year += 1;
19332                         } else {
19333                             month += 1;
19334                         }
19335                     }
19336                     //Roo.log([year,month,day]);
19337                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19338                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19339 //                    this.fill();
19340                     //Roo.log(this.formatDate(this.date));
19341                     this.setValue(this.formatDate(this.date));
19342                     this.hidePopup();
19343                 }
19344                 break;
19345         }
19346     },
19347     
19348     setStartDate: function(startDate)
19349     {
19350         this.startDate = startDate || -Infinity;
19351         if (this.startDate !== -Infinity) {
19352             this.startDate = this.parseDate(this.startDate);
19353         }
19354         this.update();
19355         this.updateNavArrows();
19356     },
19357
19358     setEndDate: function(endDate)
19359     {
19360         this.endDate = endDate || Infinity;
19361         if (this.endDate !== Infinity) {
19362             this.endDate = this.parseDate(this.endDate);
19363         }
19364         this.update();
19365         this.updateNavArrows();
19366     },
19367     
19368     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19369     {
19370         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19371         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19372             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19373         }
19374         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19375             return parseInt(d, 10);
19376         });
19377         this.update();
19378         this.updateNavArrows();
19379     },
19380     
19381     updateNavArrows: function() 
19382     {
19383         if(this.singleMode){
19384             return;
19385         }
19386         
19387         var d = new Date(this.viewDate),
19388         year = d.getUTCFullYear(),
19389         month = d.getUTCMonth();
19390         
19391         Roo.each(this.picker().select('.prev', true).elements, function(v){
19392             v.show();
19393             switch (this.viewMode) {
19394                 case 0:
19395
19396                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19397                         v.hide();
19398                     }
19399                     break;
19400                 case 1:
19401                 case 2:
19402                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19403                         v.hide();
19404                     }
19405                     break;
19406             }
19407         });
19408         
19409         Roo.each(this.picker().select('.next', true).elements, function(v){
19410             v.show();
19411             switch (this.viewMode) {
19412                 case 0:
19413
19414                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19415                         v.hide();
19416                     }
19417                     break;
19418                 case 1:
19419                 case 2:
19420                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19421                         v.hide();
19422                     }
19423                     break;
19424             }
19425         })
19426     },
19427     
19428     moveMonth: function(date, dir)
19429     {
19430         if (!dir) {
19431             return date;
19432         }
19433         var new_date = new Date(date.valueOf()),
19434         day = new_date.getUTCDate(),
19435         month = new_date.getUTCMonth(),
19436         mag = Math.abs(dir),
19437         new_month, test;
19438         dir = dir > 0 ? 1 : -1;
19439         if (mag == 1){
19440             test = dir == -1
19441             // If going back one month, make sure month is not current month
19442             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19443             ? function(){
19444                 return new_date.getUTCMonth() == month;
19445             }
19446             // If going forward one month, make sure month is as expected
19447             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19448             : function(){
19449                 return new_date.getUTCMonth() != new_month;
19450             };
19451             new_month = month + dir;
19452             new_date.setUTCMonth(new_month);
19453             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19454             if (new_month < 0 || new_month > 11) {
19455                 new_month = (new_month + 12) % 12;
19456             }
19457         } else {
19458             // For magnitudes >1, move one month at a time...
19459             for (var i=0; i<mag; i++) {
19460                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19461                 new_date = this.moveMonth(new_date, dir);
19462             }
19463             // ...then reset the day, keeping it in the new month
19464             new_month = new_date.getUTCMonth();
19465             new_date.setUTCDate(day);
19466             test = function(){
19467                 return new_month != new_date.getUTCMonth();
19468             };
19469         }
19470         // Common date-resetting loop -- if date is beyond end of month, make it
19471         // end of month
19472         while (test()){
19473             new_date.setUTCDate(--day);
19474             new_date.setUTCMonth(new_month);
19475         }
19476         return new_date;
19477     },
19478
19479     moveYear: function(date, dir)
19480     {
19481         return this.moveMonth(date, dir*12);
19482     },
19483
19484     dateWithinRange: function(date)
19485     {
19486         return date >= this.startDate && date <= this.endDate;
19487     },
19488
19489     
19490     remove: function() 
19491     {
19492         this.picker().remove();
19493     },
19494     
19495     validateValue : function(value)
19496     {
19497         if(this.getVisibilityEl().hasClass('hidden')){
19498             return true;
19499         }
19500         
19501         if(value.length < 1)  {
19502             if(this.allowBlank){
19503                 return true;
19504             }
19505             return false;
19506         }
19507         
19508         if(value.length < this.minLength){
19509             return false;
19510         }
19511         if(value.length > this.maxLength){
19512             return false;
19513         }
19514         if(this.vtype){
19515             var vt = Roo.form.VTypes;
19516             if(!vt[this.vtype](value, this)){
19517                 return false;
19518             }
19519         }
19520         if(typeof this.validator == "function"){
19521             var msg = this.validator(value);
19522             if(msg !== true){
19523                 return false;
19524             }
19525         }
19526         
19527         if(this.regex && !this.regex.test(value)){
19528             return false;
19529         }
19530         
19531         if(typeof(this.parseDate(value)) == 'undefined'){
19532             return false;
19533         }
19534         
19535         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19536             return false;
19537         }      
19538         
19539         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19540             return false;
19541         } 
19542         
19543         
19544         return true;
19545     },
19546     
19547     reset : function()
19548     {
19549         this.date = this.viewDate = '';
19550         
19551         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19552     }
19553    
19554 });
19555
19556 Roo.apply(Roo.bootstrap.DateField,  {
19557     
19558     head : {
19559         tag: 'thead',
19560         cn: [
19561         {
19562             tag: 'tr',
19563             cn: [
19564             {
19565                 tag: 'th',
19566                 cls: 'prev',
19567                 html: '<i class="fa fa-arrow-left"/>'
19568             },
19569             {
19570                 tag: 'th',
19571                 cls: 'switch',
19572                 colspan: '5'
19573             },
19574             {
19575                 tag: 'th',
19576                 cls: 'next',
19577                 html: '<i class="fa fa-arrow-right"/>'
19578             }
19579
19580             ]
19581         }
19582         ]
19583     },
19584     
19585     content : {
19586         tag: 'tbody',
19587         cn: [
19588         {
19589             tag: 'tr',
19590             cn: [
19591             {
19592                 tag: 'td',
19593                 colspan: '7'
19594             }
19595             ]
19596         }
19597         ]
19598     },
19599     
19600     footer : {
19601         tag: 'tfoot',
19602         cn: [
19603         {
19604             tag: 'tr',
19605             cn: [
19606             {
19607                 tag: 'th',
19608                 colspan: '7',
19609                 cls: 'today'
19610             }
19611                     
19612             ]
19613         }
19614         ]
19615     },
19616     
19617     dates:{
19618         en: {
19619             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19620             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19621             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19622             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19623             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19624             today: "Today"
19625         }
19626     },
19627     
19628     modes: [
19629     {
19630         clsName: 'days',
19631         navFnc: 'Month',
19632         navStep: 1
19633     },
19634     {
19635         clsName: 'months',
19636         navFnc: 'FullYear',
19637         navStep: 1
19638     },
19639     {
19640         clsName: 'years',
19641         navFnc: 'FullYear',
19642         navStep: 10
19643     }]
19644 });
19645
19646 Roo.apply(Roo.bootstrap.DateField,  {
19647   
19648     template : {
19649         tag: 'div',
19650         cls: 'datepicker dropdown-menu roo-dynamic',
19651         cn: [
19652         {
19653             tag: 'div',
19654             cls: 'datepicker-days',
19655             cn: [
19656             {
19657                 tag: 'table',
19658                 cls: 'table-condensed',
19659                 cn:[
19660                 Roo.bootstrap.DateField.head,
19661                 {
19662                     tag: 'tbody'
19663                 },
19664                 Roo.bootstrap.DateField.footer
19665                 ]
19666             }
19667             ]
19668         },
19669         {
19670             tag: 'div',
19671             cls: 'datepicker-months',
19672             cn: [
19673             {
19674                 tag: 'table',
19675                 cls: 'table-condensed',
19676                 cn:[
19677                 Roo.bootstrap.DateField.head,
19678                 Roo.bootstrap.DateField.content,
19679                 Roo.bootstrap.DateField.footer
19680                 ]
19681             }
19682             ]
19683         },
19684         {
19685             tag: 'div',
19686             cls: 'datepicker-years',
19687             cn: [
19688             {
19689                 tag: 'table',
19690                 cls: 'table-condensed',
19691                 cn:[
19692                 Roo.bootstrap.DateField.head,
19693                 Roo.bootstrap.DateField.content,
19694                 Roo.bootstrap.DateField.footer
19695                 ]
19696             }
19697             ]
19698         }
19699         ]
19700     }
19701 });
19702
19703  
19704
19705  /*
19706  * - LGPL
19707  *
19708  * TimeField
19709  * 
19710  */
19711
19712 /**
19713  * @class Roo.bootstrap.TimeField
19714  * @extends Roo.bootstrap.Input
19715  * Bootstrap DateField class
19716  * 
19717  * 
19718  * @constructor
19719  * Create a new TimeField
19720  * @param {Object} config The config object
19721  */
19722
19723 Roo.bootstrap.TimeField = function(config){
19724     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19725     this.addEvents({
19726             /**
19727              * @event show
19728              * Fires when this field show.
19729              * @param {Roo.bootstrap.DateField} thisthis
19730              * @param {Mixed} date The date value
19731              */
19732             show : true,
19733             /**
19734              * @event show
19735              * Fires when this field hide.
19736              * @param {Roo.bootstrap.DateField} this
19737              * @param {Mixed} date The date value
19738              */
19739             hide : true,
19740             /**
19741              * @event select
19742              * Fires when select a date.
19743              * @param {Roo.bootstrap.DateField} this
19744              * @param {Mixed} date The date value
19745              */
19746             select : true
19747         });
19748 };
19749
19750 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19751     
19752     /**
19753      * @cfg {String} format
19754      * The default time format string which can be overriden for localization support.  The format must be
19755      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19756      */
19757     format : "H:i",
19758        
19759     onRender: function(ct, position)
19760     {
19761         
19762         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19763                 
19764         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19765         
19766         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19767         
19768         this.pop = this.picker().select('>.datepicker-time',true).first();
19769         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19770         
19771         this.picker().on('mousedown', this.onMousedown, this);
19772         this.picker().on('click', this.onClick, this);
19773         
19774         this.picker().addClass('datepicker-dropdown');
19775     
19776         this.fillTime();
19777         this.update();
19778             
19779         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19780         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19781         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19782         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19783         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19784         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19785
19786     },
19787     
19788     fireKey: function(e){
19789         if (!this.picker().isVisible()){
19790             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19791                 this.show();
19792             }
19793             return;
19794         }
19795
19796         e.preventDefault();
19797         
19798         switch(e.keyCode){
19799             case 27: // escape
19800                 this.hide();
19801                 break;
19802             case 37: // left
19803             case 39: // right
19804                 this.onTogglePeriod();
19805                 break;
19806             case 38: // up
19807                 this.onIncrementMinutes();
19808                 break;
19809             case 40: // down
19810                 this.onDecrementMinutes();
19811                 break;
19812             case 13: // enter
19813             case 9: // tab
19814                 this.setTime();
19815                 break;
19816         }
19817     },
19818     
19819     onClick: function(e) {
19820         e.stopPropagation();
19821         e.preventDefault();
19822     },
19823     
19824     picker : function()
19825     {
19826         return this.el.select('.datepicker', true).first();
19827     },
19828     
19829     fillTime: function()
19830     {    
19831         var time = this.pop.select('tbody', true).first();
19832         
19833         time.dom.innerHTML = '';
19834         
19835         time.createChild({
19836             tag: 'tr',
19837             cn: [
19838                 {
19839                     tag: 'td',
19840                     cn: [
19841                         {
19842                             tag: 'a',
19843                             href: '#',
19844                             cls: 'btn',
19845                             cn: [
19846                                 {
19847                                     tag: 'span',
19848                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19849                                 }
19850                             ]
19851                         } 
19852                     ]
19853                 },
19854                 {
19855                     tag: 'td',
19856                     cls: 'separator'
19857                 },
19858                 {
19859                     tag: 'td',
19860                     cn: [
19861                         {
19862                             tag: 'a',
19863                             href: '#',
19864                             cls: 'btn',
19865                             cn: [
19866                                 {
19867                                     tag: 'span',
19868                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19869                                 }
19870                             ]
19871                         }
19872                     ]
19873                 },
19874                 {
19875                     tag: 'td',
19876                     cls: 'separator'
19877                 }
19878             ]
19879         });
19880         
19881         time.createChild({
19882             tag: 'tr',
19883             cn: [
19884                 {
19885                     tag: 'td',
19886                     cn: [
19887                         {
19888                             tag: 'span',
19889                             cls: 'timepicker-hour',
19890                             html: '00'
19891                         }  
19892                     ]
19893                 },
19894                 {
19895                     tag: 'td',
19896                     cls: 'separator',
19897                     html: ':'
19898                 },
19899                 {
19900                     tag: 'td',
19901                     cn: [
19902                         {
19903                             tag: 'span',
19904                             cls: 'timepicker-minute',
19905                             html: '00'
19906                         }  
19907                     ]
19908                 },
19909                 {
19910                     tag: 'td',
19911                     cls: 'separator'
19912                 },
19913                 {
19914                     tag: 'td',
19915                     cn: [
19916                         {
19917                             tag: 'button',
19918                             type: 'button',
19919                             cls: 'btn btn-primary period',
19920                             html: 'AM'
19921                             
19922                         }
19923                     ]
19924                 }
19925             ]
19926         });
19927         
19928         time.createChild({
19929             tag: 'tr',
19930             cn: [
19931                 {
19932                     tag: 'td',
19933                     cn: [
19934                         {
19935                             tag: 'a',
19936                             href: '#',
19937                             cls: 'btn',
19938                             cn: [
19939                                 {
19940                                     tag: 'span',
19941                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19942                                 }
19943                             ]
19944                         }
19945                     ]
19946                 },
19947                 {
19948                     tag: 'td',
19949                     cls: 'separator'
19950                 },
19951                 {
19952                     tag: 'td',
19953                     cn: [
19954                         {
19955                             tag: 'a',
19956                             href: '#',
19957                             cls: 'btn',
19958                             cn: [
19959                                 {
19960                                     tag: 'span',
19961                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19962                                 }
19963                             ]
19964                         }
19965                     ]
19966                 },
19967                 {
19968                     tag: 'td',
19969                     cls: 'separator'
19970                 }
19971             ]
19972         });
19973         
19974     },
19975     
19976     update: function()
19977     {
19978         
19979         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19980         
19981         this.fill();
19982     },
19983     
19984     fill: function() 
19985     {
19986         var hours = this.time.getHours();
19987         var minutes = this.time.getMinutes();
19988         var period = 'AM';
19989         
19990         if(hours > 11){
19991             period = 'PM';
19992         }
19993         
19994         if(hours == 0){
19995             hours = 12;
19996         }
19997         
19998         
19999         if(hours > 12){
20000             hours = hours - 12;
20001         }
20002         
20003         if(hours < 10){
20004             hours = '0' + hours;
20005         }
20006         
20007         if(minutes < 10){
20008             minutes = '0' + minutes;
20009         }
20010         
20011         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20012         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20013         this.pop.select('button', true).first().dom.innerHTML = period;
20014         
20015     },
20016     
20017     place: function()
20018     {   
20019         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20020         
20021         var cls = ['bottom'];
20022         
20023         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20024             cls.pop();
20025             cls.push('top');
20026         }
20027         
20028         cls.push('right');
20029         
20030         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20031             cls.pop();
20032             cls.push('left');
20033         }
20034         
20035         this.picker().addClass(cls.join('-'));
20036         
20037         var _this = this;
20038         
20039         Roo.each(cls, function(c){
20040             if(c == 'bottom'){
20041                 _this.picker().setTop(_this.inputEl().getHeight());
20042                 return;
20043             }
20044             if(c == 'top'){
20045                 _this.picker().setTop(0 - _this.picker().getHeight());
20046                 return;
20047             }
20048             
20049             if(c == 'left'){
20050                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20051                 return;
20052             }
20053             if(c == 'right'){
20054                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20055                 return;
20056             }
20057         });
20058         
20059     },
20060   
20061     onFocus : function()
20062     {
20063         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20064         this.show();
20065     },
20066     
20067     onBlur : function()
20068     {
20069         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20070         this.hide();
20071     },
20072     
20073     show : function()
20074     {
20075         this.picker().show();
20076         this.pop.show();
20077         this.update();
20078         this.place();
20079         
20080         this.fireEvent('show', this, this.date);
20081     },
20082     
20083     hide : function()
20084     {
20085         this.picker().hide();
20086         this.pop.hide();
20087         
20088         this.fireEvent('hide', this, this.date);
20089     },
20090     
20091     setTime : function()
20092     {
20093         this.hide();
20094         this.setValue(this.time.format(this.format));
20095         
20096         this.fireEvent('select', this, this.date);
20097         
20098         
20099     },
20100     
20101     onMousedown: function(e){
20102         e.stopPropagation();
20103         e.preventDefault();
20104     },
20105     
20106     onIncrementHours: function()
20107     {
20108         Roo.log('onIncrementHours');
20109         this.time = this.time.add(Date.HOUR, 1);
20110         this.update();
20111         
20112     },
20113     
20114     onDecrementHours: function()
20115     {
20116         Roo.log('onDecrementHours');
20117         this.time = this.time.add(Date.HOUR, -1);
20118         this.update();
20119     },
20120     
20121     onIncrementMinutes: function()
20122     {
20123         Roo.log('onIncrementMinutes');
20124         this.time = this.time.add(Date.MINUTE, 1);
20125         this.update();
20126     },
20127     
20128     onDecrementMinutes: function()
20129     {
20130         Roo.log('onDecrementMinutes');
20131         this.time = this.time.add(Date.MINUTE, -1);
20132         this.update();
20133     },
20134     
20135     onTogglePeriod: function()
20136     {
20137         Roo.log('onTogglePeriod');
20138         this.time = this.time.add(Date.HOUR, 12);
20139         this.update();
20140     }
20141     
20142    
20143 });
20144
20145 Roo.apply(Roo.bootstrap.TimeField,  {
20146     
20147     content : {
20148         tag: 'tbody',
20149         cn: [
20150             {
20151                 tag: 'tr',
20152                 cn: [
20153                 {
20154                     tag: 'td',
20155                     colspan: '7'
20156                 }
20157                 ]
20158             }
20159         ]
20160     },
20161     
20162     footer : {
20163         tag: 'tfoot',
20164         cn: [
20165             {
20166                 tag: 'tr',
20167                 cn: [
20168                 {
20169                     tag: 'th',
20170                     colspan: '7',
20171                     cls: '',
20172                     cn: [
20173                         {
20174                             tag: 'button',
20175                             cls: 'btn btn-info ok',
20176                             html: 'OK'
20177                         }
20178                     ]
20179                 }
20180
20181                 ]
20182             }
20183         ]
20184     }
20185 });
20186
20187 Roo.apply(Roo.bootstrap.TimeField,  {
20188   
20189     template : {
20190         tag: 'div',
20191         cls: 'datepicker dropdown-menu',
20192         cn: [
20193             {
20194                 tag: 'div',
20195                 cls: 'datepicker-time',
20196                 cn: [
20197                 {
20198                     tag: 'table',
20199                     cls: 'table-condensed',
20200                     cn:[
20201                     Roo.bootstrap.TimeField.content,
20202                     Roo.bootstrap.TimeField.footer
20203                     ]
20204                 }
20205                 ]
20206             }
20207         ]
20208     }
20209 });
20210
20211  
20212
20213  /*
20214  * - LGPL
20215  *
20216  * MonthField
20217  * 
20218  */
20219
20220 /**
20221  * @class Roo.bootstrap.MonthField
20222  * @extends Roo.bootstrap.Input
20223  * Bootstrap MonthField class
20224  * 
20225  * @cfg {String} language default en
20226  * 
20227  * @constructor
20228  * Create a new MonthField
20229  * @param {Object} config The config object
20230  */
20231
20232 Roo.bootstrap.MonthField = function(config){
20233     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20234     
20235     this.addEvents({
20236         /**
20237          * @event show
20238          * Fires when this field show.
20239          * @param {Roo.bootstrap.MonthField} this
20240          * @param {Mixed} date The date value
20241          */
20242         show : true,
20243         /**
20244          * @event show
20245          * Fires when this field hide.
20246          * @param {Roo.bootstrap.MonthField} this
20247          * @param {Mixed} date The date value
20248          */
20249         hide : true,
20250         /**
20251          * @event select
20252          * Fires when select a date.
20253          * @param {Roo.bootstrap.MonthField} this
20254          * @param {String} oldvalue The old value
20255          * @param {String} newvalue The new value
20256          */
20257         select : true
20258     });
20259 };
20260
20261 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20262     
20263     onRender: function(ct, position)
20264     {
20265         
20266         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20267         
20268         this.language = this.language || 'en';
20269         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20270         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20271         
20272         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20273         this.isInline = false;
20274         this.isInput = true;
20275         this.component = this.el.select('.add-on', true).first() || false;
20276         this.component = (this.component && this.component.length === 0) ? false : this.component;
20277         this.hasInput = this.component && this.inputEL().length;
20278         
20279         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20280         
20281         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20282         
20283         this.picker().on('mousedown', this.onMousedown, this);
20284         this.picker().on('click', this.onClick, this);
20285         
20286         this.picker().addClass('datepicker-dropdown');
20287         
20288         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20289             v.setStyle('width', '189px');
20290         });
20291         
20292         this.fillMonths();
20293         
20294         this.update();
20295         
20296         if(this.isInline) {
20297             this.show();
20298         }
20299         
20300     },
20301     
20302     setValue: function(v, suppressEvent)
20303     {   
20304         var o = this.getValue();
20305         
20306         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20307         
20308         this.update();
20309
20310         if(suppressEvent !== true){
20311             this.fireEvent('select', this, o, v);
20312         }
20313         
20314     },
20315     
20316     getValue: function()
20317     {
20318         return this.value;
20319     },
20320     
20321     onClick: function(e) 
20322     {
20323         e.stopPropagation();
20324         e.preventDefault();
20325         
20326         var target = e.getTarget();
20327         
20328         if(target.nodeName.toLowerCase() === 'i'){
20329             target = Roo.get(target).dom.parentNode;
20330         }
20331         
20332         var nodeName = target.nodeName;
20333         var className = target.className;
20334         var html = target.innerHTML;
20335         
20336         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20337             return;
20338         }
20339         
20340         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20341         
20342         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20343         
20344         this.hide();
20345                         
20346     },
20347     
20348     picker : function()
20349     {
20350         return this.pickerEl;
20351     },
20352     
20353     fillMonths: function()
20354     {    
20355         var i = 0;
20356         var months = this.picker().select('>.datepicker-months td', true).first();
20357         
20358         months.dom.innerHTML = '';
20359         
20360         while (i < 12) {
20361             var month = {
20362                 tag: 'span',
20363                 cls: 'month',
20364                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20365             };
20366             
20367             months.createChild(month);
20368         }
20369         
20370     },
20371     
20372     update: function()
20373     {
20374         var _this = this;
20375         
20376         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20377             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20378         }
20379         
20380         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20381             e.removeClass('active');
20382             
20383             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20384                 e.addClass('active');
20385             }
20386         })
20387     },
20388     
20389     place: function()
20390     {
20391         if(this.isInline) {
20392             return;
20393         }
20394         
20395         this.picker().removeClass(['bottom', 'top']);
20396         
20397         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20398             /*
20399              * place to the top of element!
20400              *
20401              */
20402             
20403             this.picker().addClass('top');
20404             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20405             
20406             return;
20407         }
20408         
20409         this.picker().addClass('bottom');
20410         
20411         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20412     },
20413     
20414     onFocus : function()
20415     {
20416         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20417         this.show();
20418     },
20419     
20420     onBlur : function()
20421     {
20422         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20423         
20424         var d = this.inputEl().getValue();
20425         
20426         this.setValue(d);
20427                 
20428         this.hide();
20429     },
20430     
20431     show : function()
20432     {
20433         this.picker().show();
20434         this.picker().select('>.datepicker-months', true).first().show();
20435         this.update();
20436         this.place();
20437         
20438         this.fireEvent('show', this, this.date);
20439     },
20440     
20441     hide : function()
20442     {
20443         if(this.isInline) {
20444             return;
20445         }
20446         this.picker().hide();
20447         this.fireEvent('hide', this, this.date);
20448         
20449     },
20450     
20451     onMousedown: function(e)
20452     {
20453         e.stopPropagation();
20454         e.preventDefault();
20455     },
20456     
20457     keyup: function(e)
20458     {
20459         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20460         this.update();
20461     },
20462
20463     fireKey: function(e)
20464     {
20465         if (!this.picker().isVisible()){
20466             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20467                 this.show();
20468             }
20469             return;
20470         }
20471         
20472         var dir;
20473         
20474         switch(e.keyCode){
20475             case 27: // escape
20476                 this.hide();
20477                 e.preventDefault();
20478                 break;
20479             case 37: // left
20480             case 39: // right
20481                 dir = e.keyCode == 37 ? -1 : 1;
20482                 
20483                 this.vIndex = this.vIndex + dir;
20484                 
20485                 if(this.vIndex < 0){
20486                     this.vIndex = 0;
20487                 }
20488                 
20489                 if(this.vIndex > 11){
20490                     this.vIndex = 11;
20491                 }
20492                 
20493                 if(isNaN(this.vIndex)){
20494                     this.vIndex = 0;
20495                 }
20496                 
20497                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20498                 
20499                 break;
20500             case 38: // up
20501             case 40: // down
20502                 
20503                 dir = e.keyCode == 38 ? -1 : 1;
20504                 
20505                 this.vIndex = this.vIndex + dir * 4;
20506                 
20507                 if(this.vIndex < 0){
20508                     this.vIndex = 0;
20509                 }
20510                 
20511                 if(this.vIndex > 11){
20512                     this.vIndex = 11;
20513                 }
20514                 
20515                 if(isNaN(this.vIndex)){
20516                     this.vIndex = 0;
20517                 }
20518                 
20519                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20520                 break;
20521                 
20522             case 13: // enter
20523                 
20524                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20525                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20526                 }
20527                 
20528                 this.hide();
20529                 e.preventDefault();
20530                 break;
20531             case 9: // tab
20532                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20533                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20534                 }
20535                 this.hide();
20536                 break;
20537             case 16: // shift
20538             case 17: // ctrl
20539             case 18: // alt
20540                 break;
20541             default :
20542                 this.hide();
20543                 
20544         }
20545     },
20546     
20547     remove: function() 
20548     {
20549         this.picker().remove();
20550     }
20551    
20552 });
20553
20554 Roo.apply(Roo.bootstrap.MonthField,  {
20555     
20556     content : {
20557         tag: 'tbody',
20558         cn: [
20559         {
20560             tag: 'tr',
20561             cn: [
20562             {
20563                 tag: 'td',
20564                 colspan: '7'
20565             }
20566             ]
20567         }
20568         ]
20569     },
20570     
20571     dates:{
20572         en: {
20573             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20574             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20575         }
20576     }
20577 });
20578
20579 Roo.apply(Roo.bootstrap.MonthField,  {
20580   
20581     template : {
20582         tag: 'div',
20583         cls: 'datepicker dropdown-menu roo-dynamic',
20584         cn: [
20585             {
20586                 tag: 'div',
20587                 cls: 'datepicker-months',
20588                 cn: [
20589                 {
20590                     tag: 'table',
20591                     cls: 'table-condensed',
20592                     cn:[
20593                         Roo.bootstrap.DateField.content
20594                     ]
20595                 }
20596                 ]
20597             }
20598         ]
20599     }
20600 });
20601
20602  
20603
20604  
20605  /*
20606  * - LGPL
20607  *
20608  * CheckBox
20609  * 
20610  */
20611
20612 /**
20613  * @class Roo.bootstrap.CheckBox
20614  * @extends Roo.bootstrap.Input
20615  * Bootstrap CheckBox class
20616  * 
20617  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20618  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20619  * @cfg {String} boxLabel The text that appears beside the checkbox
20620  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20621  * @cfg {Boolean} checked initnal the element
20622  * @cfg {Boolean} inline inline the element (default false)
20623  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20624  * @cfg {String} tooltip label tooltip
20625  * 
20626  * @constructor
20627  * Create a new CheckBox
20628  * @param {Object} config The config object
20629  */
20630
20631 Roo.bootstrap.CheckBox = function(config){
20632     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20633    
20634     this.addEvents({
20635         /**
20636         * @event check
20637         * Fires when the element is checked or unchecked.
20638         * @param {Roo.bootstrap.CheckBox} this This input
20639         * @param {Boolean} checked The new checked value
20640         */
20641        check : true,
20642        /**
20643         * @event click
20644         * Fires when the element is click.
20645         * @param {Roo.bootstrap.CheckBox} this This input
20646         */
20647        click : true
20648     });
20649     
20650 };
20651
20652 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20653   
20654     inputType: 'checkbox',
20655     inputValue: 1,
20656     valueOff: 0,
20657     boxLabel: false,
20658     checked: false,
20659     weight : false,
20660     inline: false,
20661     tooltip : '',
20662     
20663     getAutoCreate : function()
20664     {
20665         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20666         
20667         var id = Roo.id();
20668         
20669         var cfg = {};
20670         
20671         cfg.cls = 'form-group ' + this.inputType; //input-group
20672         
20673         if(this.inline){
20674             cfg.cls += ' ' + this.inputType + '-inline';
20675         }
20676         
20677         var input =  {
20678             tag: 'input',
20679             id : id,
20680             type : this.inputType,
20681             value : this.inputValue,
20682             cls : 'roo-' + this.inputType, //'form-box',
20683             placeholder : this.placeholder || ''
20684             
20685         };
20686         
20687         if(this.inputType != 'radio'){
20688             var hidden =  {
20689                 tag: 'input',
20690                 type : 'hidden',
20691                 cls : 'roo-hidden-value',
20692                 value : this.checked ? this.inputValue : this.valueOff
20693             };
20694         }
20695         
20696             
20697         if (this.weight) { // Validity check?
20698             cfg.cls += " " + this.inputType + "-" + this.weight;
20699         }
20700         
20701         if (this.disabled) {
20702             input.disabled=true;
20703         }
20704         
20705         if(this.checked){
20706             input.checked = this.checked;
20707         }
20708         
20709         if (this.name) {
20710             
20711             input.name = this.name;
20712             
20713             if(this.inputType != 'radio'){
20714                 hidden.name = this.name;
20715                 input.name = '_hidden_' + this.name;
20716             }
20717         }
20718         
20719         if (this.size) {
20720             input.cls += ' input-' + this.size;
20721         }
20722         
20723         var settings=this;
20724         
20725         ['xs','sm','md','lg'].map(function(size){
20726             if (settings[size]) {
20727                 cfg.cls += ' col-' + size + '-' + settings[size];
20728             }
20729         });
20730         
20731         var inputblock = input;
20732          
20733         if (this.before || this.after) {
20734             
20735             inputblock = {
20736                 cls : 'input-group',
20737                 cn :  [] 
20738             };
20739             
20740             if (this.before) {
20741                 inputblock.cn.push({
20742                     tag :'span',
20743                     cls : 'input-group-addon',
20744                     html : this.before
20745                 });
20746             }
20747             
20748             inputblock.cn.push(input);
20749             
20750             if(this.inputType != 'radio'){
20751                 inputblock.cn.push(hidden);
20752             }
20753             
20754             if (this.after) {
20755                 inputblock.cn.push({
20756                     tag :'span',
20757                     cls : 'input-group-addon',
20758                     html : this.after
20759                 });
20760             }
20761             
20762         }
20763         
20764         if (align ==='left' && this.fieldLabel.length) {
20765 //                Roo.log("left and has label");
20766             cfg.cn = [
20767                 {
20768                     tag: 'label',
20769                     'for' :  id,
20770                     cls : 'control-label',
20771                     html : this.fieldLabel
20772                 },
20773                 {
20774                     cls : "", 
20775                     cn: [
20776                         inputblock
20777                     ]
20778                 }
20779             ];
20780             
20781             if(this.labelWidth > 12){
20782                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20783             }
20784             
20785             if(this.labelWidth < 13 && this.labelmd == 0){
20786                 this.labelmd = this.labelWidth;
20787             }
20788             
20789             if(this.labellg > 0){
20790                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20791                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20792             }
20793             
20794             if(this.labelmd > 0){
20795                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20796                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20797             }
20798             
20799             if(this.labelsm > 0){
20800                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20801                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20802             }
20803             
20804             if(this.labelxs > 0){
20805                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20806                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20807             }
20808             
20809         } else if ( this.fieldLabel.length) {
20810 //                Roo.log(" label");
20811                 cfg.cn = [
20812                    
20813                     {
20814                         tag: this.boxLabel ? 'span' : 'label',
20815                         'for': id,
20816                         cls: 'control-label box-input-label',
20817                         //cls : 'input-group-addon',
20818                         html : this.fieldLabel
20819                     },
20820                     
20821                     inputblock
20822                     
20823                 ];
20824
20825         } else {
20826             
20827 //                Roo.log(" no label && no align");
20828                 cfg.cn = [  inputblock ] ;
20829                 
20830                 
20831         }
20832         
20833         if(this.boxLabel){
20834              var boxLabelCfg = {
20835                 tag: 'label',
20836                 //'for': id, // box label is handled by onclick - so no for...
20837                 cls: 'box-label',
20838                 html: this.boxLabel
20839             };
20840             
20841             if(this.tooltip){
20842                 boxLabelCfg.tooltip = this.tooltip;
20843             }
20844              
20845             cfg.cn.push(boxLabelCfg);
20846         }
20847         
20848         if(this.inputType != 'radio'){
20849             cfg.cn.push(hidden);
20850         }
20851         
20852         return cfg;
20853         
20854     },
20855     
20856     /**
20857      * return the real input element.
20858      */
20859     inputEl: function ()
20860     {
20861         return this.el.select('input.roo-' + this.inputType,true).first();
20862     },
20863     hiddenEl: function ()
20864     {
20865         return this.el.select('input.roo-hidden-value',true).first();
20866     },
20867     
20868     labelEl: function()
20869     {
20870         return this.el.select('label.control-label',true).first();
20871     },
20872     /* depricated... */
20873     
20874     label: function()
20875     {
20876         return this.labelEl();
20877     },
20878     
20879     boxLabelEl: function()
20880     {
20881         return this.el.select('label.box-label',true).first();
20882     },
20883     
20884     initEvents : function()
20885     {
20886 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20887         
20888         this.inputEl().on('click', this.onClick,  this);
20889         
20890         if (this.boxLabel) { 
20891             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20892         }
20893         
20894         this.startValue = this.getValue();
20895         
20896         if(this.groupId){
20897             Roo.bootstrap.CheckBox.register(this);
20898         }
20899     },
20900     
20901     onClick : function(e)
20902     {   
20903         if(this.fireEvent('click', this, e) !== false){
20904             this.setChecked(!this.checked);
20905         }
20906         
20907     },
20908     
20909     setChecked : function(state,suppressEvent)
20910     {
20911         this.startValue = this.getValue();
20912
20913         if(this.inputType == 'radio'){
20914             
20915             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20916                 e.dom.checked = false;
20917             });
20918             
20919             this.inputEl().dom.checked = true;
20920             
20921             this.inputEl().dom.value = this.inputValue;
20922             
20923             if(suppressEvent !== true){
20924                 this.fireEvent('check', this, true);
20925             }
20926             
20927             this.validate();
20928             
20929             return;
20930         }
20931         
20932         this.checked = state;
20933         
20934         this.inputEl().dom.checked = state;
20935         
20936         
20937         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20938         
20939         if(suppressEvent !== true){
20940             this.fireEvent('check', this, state);
20941         }
20942         
20943         this.validate();
20944     },
20945     
20946     getValue : function()
20947     {
20948         if(this.inputType == 'radio'){
20949             return this.getGroupValue();
20950         }
20951         
20952         return this.hiddenEl().dom.value;
20953         
20954     },
20955     
20956     getGroupValue : function()
20957     {
20958         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20959             return '';
20960         }
20961         
20962         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20963     },
20964     
20965     setValue : function(v,suppressEvent)
20966     {
20967         if(this.inputType == 'radio'){
20968             this.setGroupValue(v, suppressEvent);
20969             return;
20970         }
20971         
20972         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20973         
20974         this.validate();
20975     },
20976     
20977     setGroupValue : function(v, suppressEvent)
20978     {
20979         this.startValue = this.getValue();
20980         
20981         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20982             e.dom.checked = false;
20983             
20984             if(e.dom.value == v){
20985                 e.dom.checked = true;
20986             }
20987         });
20988         
20989         if(suppressEvent !== true){
20990             this.fireEvent('check', this, true);
20991         }
20992
20993         this.validate();
20994         
20995         return;
20996     },
20997     
20998     validate : function()
20999     {
21000         if(this.getVisibilityEl().hasClass('hidden')){
21001             return true;
21002         }
21003         
21004         if(
21005                 this.disabled || 
21006                 (this.inputType == 'radio' && this.validateRadio()) ||
21007                 (this.inputType == 'checkbox' && this.validateCheckbox())
21008         ){
21009             this.markValid();
21010             return true;
21011         }
21012         
21013         this.markInvalid();
21014         return false;
21015     },
21016     
21017     validateRadio : function()
21018     {
21019         if(this.getVisibilityEl().hasClass('hidden')){
21020             return true;
21021         }
21022         
21023         if(this.allowBlank){
21024             return true;
21025         }
21026         
21027         var valid = false;
21028         
21029         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21030             if(!e.dom.checked){
21031                 return;
21032             }
21033             
21034             valid = true;
21035             
21036             return false;
21037         });
21038         
21039         return valid;
21040     },
21041     
21042     validateCheckbox : function()
21043     {
21044         if(!this.groupId){
21045             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21046             //return (this.getValue() == this.inputValue) ? true : false;
21047         }
21048         
21049         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21050         
21051         if(!group){
21052             return false;
21053         }
21054         
21055         var r = false;
21056         
21057         for(var i in group){
21058             if(group[i].el.isVisible(true)){
21059                 r = false;
21060                 break;
21061             }
21062             
21063             r = true;
21064         }
21065         
21066         for(var i in group){
21067             if(r){
21068                 break;
21069             }
21070             
21071             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21072         }
21073         
21074         return r;
21075     },
21076     
21077     /**
21078      * Mark this field as valid
21079      */
21080     markValid : function()
21081     {
21082         var _this = this;
21083         
21084         this.fireEvent('valid', this);
21085         
21086         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21087         
21088         if(this.groupId){
21089             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21090         }
21091         
21092         if(label){
21093             label.markValid();
21094         }
21095
21096         if(this.inputType == 'radio'){
21097             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21098                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21099                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21100             });
21101             
21102             return;
21103         }
21104
21105         if(!this.groupId){
21106             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21107             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21108             return;
21109         }
21110         
21111         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21112         
21113         if(!group){
21114             return;
21115         }
21116         
21117         for(var i in group){
21118             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21119             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21120         }
21121     },
21122     
21123      /**
21124      * Mark this field as invalid
21125      * @param {String} msg The validation message
21126      */
21127     markInvalid : function(msg)
21128     {
21129         if(this.allowBlank){
21130             return;
21131         }
21132         
21133         var _this = this;
21134         
21135         this.fireEvent('invalid', this, msg);
21136         
21137         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21138         
21139         if(this.groupId){
21140             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21141         }
21142         
21143         if(label){
21144             label.markInvalid();
21145         }
21146             
21147         if(this.inputType == 'radio'){
21148             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21149                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21150                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21151             });
21152             
21153             return;
21154         }
21155         
21156         if(!this.groupId){
21157             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21158             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21159             return;
21160         }
21161         
21162         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21163         
21164         if(!group){
21165             return;
21166         }
21167         
21168         for(var i in group){
21169             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21170             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21171         }
21172         
21173     },
21174     
21175     clearInvalid : function()
21176     {
21177         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21178         
21179         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21180         
21181         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21182         
21183         if (label && label.iconEl) {
21184             label.iconEl.removeClass(label.validClass);
21185             label.iconEl.removeClass(label.invalidClass);
21186         }
21187     },
21188     
21189     disable : function()
21190     {
21191         if(this.inputType != 'radio'){
21192             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21193             return;
21194         }
21195         
21196         var _this = this;
21197         
21198         if(this.rendered){
21199             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21200                 _this.getActionEl().addClass(this.disabledClass);
21201                 e.dom.disabled = true;
21202             });
21203         }
21204         
21205         this.disabled = true;
21206         this.fireEvent("disable", this);
21207         return this;
21208     },
21209
21210     enable : function()
21211     {
21212         if(this.inputType != 'radio'){
21213             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21214             return;
21215         }
21216         
21217         var _this = this;
21218         
21219         if(this.rendered){
21220             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21221                 _this.getActionEl().removeClass(this.disabledClass);
21222                 e.dom.disabled = false;
21223             });
21224         }
21225         
21226         this.disabled = false;
21227         this.fireEvent("enable", this);
21228         return this;
21229     },
21230     
21231     setBoxLabel : function(v)
21232     {
21233         this.boxLabel = v;
21234         
21235         if(this.rendered){
21236             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21237         }
21238     }
21239
21240 });
21241
21242 Roo.apply(Roo.bootstrap.CheckBox, {
21243     
21244     groups: {},
21245     
21246      /**
21247     * register a CheckBox Group
21248     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21249     */
21250     register : function(checkbox)
21251     {
21252         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21253             this.groups[checkbox.groupId] = {};
21254         }
21255         
21256         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21257             return;
21258         }
21259         
21260         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21261         
21262     },
21263     /**
21264     * fetch a CheckBox Group based on the group ID
21265     * @param {string} the group ID
21266     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21267     */
21268     get: function(groupId) {
21269         if (typeof(this.groups[groupId]) == 'undefined') {
21270             return false;
21271         }
21272         
21273         return this.groups[groupId] ;
21274     }
21275     
21276     
21277 });
21278 /*
21279  * - LGPL
21280  *
21281  * RadioItem
21282  * 
21283  */
21284
21285 /**
21286  * @class Roo.bootstrap.Radio
21287  * @extends Roo.bootstrap.Component
21288  * Bootstrap Radio class
21289  * @cfg {String} boxLabel - the label associated
21290  * @cfg {String} value - the value of radio
21291  * 
21292  * @constructor
21293  * Create a new Radio
21294  * @param {Object} config The config object
21295  */
21296 Roo.bootstrap.Radio = function(config){
21297     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21298     
21299 };
21300
21301 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21302     
21303     boxLabel : '',
21304     
21305     value : '',
21306     
21307     getAutoCreate : function()
21308     {
21309         var cfg = {
21310             tag : 'div',
21311             cls : 'form-group radio',
21312             cn : [
21313                 {
21314                     tag : 'label',
21315                     cls : 'box-label',
21316                     html : this.boxLabel
21317                 }
21318             ]
21319         };
21320         
21321         return cfg;
21322     },
21323     
21324     initEvents : function() 
21325     {
21326         this.parent().register(this);
21327         
21328         this.el.on('click', this.onClick, this);
21329         
21330     },
21331     
21332     onClick : function(e)
21333     {
21334         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21335             this.setChecked(true);
21336         }
21337     },
21338     
21339     setChecked : function(state, suppressEvent)
21340     {
21341         this.parent().setValue(this.value, suppressEvent);
21342         
21343     },
21344     
21345     setBoxLabel : function(v)
21346     {
21347         this.boxLabel = v;
21348         
21349         if(this.rendered){
21350             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21351         }
21352     }
21353     
21354 });
21355  
21356
21357  /*
21358  * - LGPL
21359  *
21360  * Input
21361  * 
21362  */
21363
21364 /**
21365  * @class Roo.bootstrap.SecurePass
21366  * @extends Roo.bootstrap.Input
21367  * Bootstrap SecurePass class
21368  *
21369  * 
21370  * @constructor
21371  * Create a new SecurePass
21372  * @param {Object} config The config object
21373  */
21374  
21375 Roo.bootstrap.SecurePass = function (config) {
21376     // these go here, so the translation tool can replace them..
21377     this.errors = {
21378         PwdEmpty: "Please type a password, and then retype it to confirm.",
21379         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21380         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21381         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21382         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21383         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21384         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21385         TooWeak: "Your password is Too Weak."
21386     },
21387     this.meterLabel = "Password strength:";
21388     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21389     this.meterClass = [
21390         "roo-password-meter-tooweak", 
21391         "roo-password-meter-weak", 
21392         "roo-password-meter-medium", 
21393         "roo-password-meter-strong", 
21394         "roo-password-meter-grey"
21395     ];
21396     
21397     this.errors = {};
21398     
21399     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21400 }
21401
21402 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21403     /**
21404      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21405      * {
21406      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21407      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21408      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21409      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21410      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21411      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21412      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21413      * })
21414      */
21415     // private
21416     
21417     meterWidth: 300,
21418     errorMsg :'',    
21419     errors: false,
21420     imageRoot: '/',
21421     /**
21422      * @cfg {String/Object} Label for the strength meter (defaults to
21423      * 'Password strength:')
21424      */
21425     // private
21426     meterLabel: '',
21427     /**
21428      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21429      * ['Weak', 'Medium', 'Strong'])
21430      */
21431     // private    
21432     pwdStrengths: false,    
21433     // private
21434     strength: 0,
21435     // private
21436     _lastPwd: null,
21437     // private
21438     kCapitalLetter: 0,
21439     kSmallLetter: 1,
21440     kDigit: 2,
21441     kPunctuation: 3,
21442     
21443     insecure: false,
21444     // private
21445     initEvents: function ()
21446     {
21447         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21448
21449         if (this.el.is('input[type=password]') && Roo.isSafari) {
21450             this.el.on('keydown', this.SafariOnKeyDown, this);
21451         }
21452
21453         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21454     },
21455     // private
21456     onRender: function (ct, position)
21457     {
21458         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21459         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21460         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21461
21462         this.trigger.createChild({
21463                    cn: [
21464                     {
21465                     //id: 'PwdMeter',
21466                     tag: 'div',
21467                     cls: 'roo-password-meter-grey col-xs-12',
21468                     style: {
21469                         //width: 0,
21470                         //width: this.meterWidth + 'px'                                                
21471                         }
21472                     },
21473                     {                            
21474                          cls: 'roo-password-meter-text'                          
21475                     }
21476                 ]            
21477         });
21478
21479          
21480         if (this.hideTrigger) {
21481             this.trigger.setDisplayed(false);
21482         }
21483         this.setSize(this.width || '', this.height || '');
21484     },
21485     // private
21486     onDestroy: function ()
21487     {
21488         if (this.trigger) {
21489             this.trigger.removeAllListeners();
21490             this.trigger.remove();
21491         }
21492         if (this.wrap) {
21493             this.wrap.remove();
21494         }
21495         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21496     },
21497     // private
21498     checkStrength: function ()
21499     {
21500         var pwd = this.inputEl().getValue();
21501         if (pwd == this._lastPwd) {
21502             return;
21503         }
21504
21505         var strength;
21506         if (this.ClientSideStrongPassword(pwd)) {
21507             strength = 3;
21508         } else if (this.ClientSideMediumPassword(pwd)) {
21509             strength = 2;
21510         } else if (this.ClientSideWeakPassword(pwd)) {
21511             strength = 1;
21512         } else {
21513             strength = 0;
21514         }
21515         
21516         Roo.log('strength1: ' + strength);
21517         
21518         //var pm = this.trigger.child('div/div/div').dom;
21519         var pm = this.trigger.child('div/div');
21520         pm.removeClass(this.meterClass);
21521         pm.addClass(this.meterClass[strength]);
21522                 
21523         
21524         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21525                 
21526         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21527         
21528         this._lastPwd = pwd;
21529     },
21530     reset: function ()
21531     {
21532         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21533         
21534         this._lastPwd = '';
21535         
21536         var pm = this.trigger.child('div/div');
21537         pm.removeClass(this.meterClass);
21538         pm.addClass('roo-password-meter-grey');        
21539         
21540         
21541         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21542         
21543         pt.innerHTML = '';
21544         this.inputEl().dom.type='password';
21545     },
21546     // private
21547     validateValue: function (value)
21548     {
21549         
21550         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21551             return false;
21552         }
21553         if (value.length == 0) {
21554             if (this.allowBlank) {
21555                 this.clearInvalid();
21556                 return true;
21557             }
21558
21559             this.markInvalid(this.errors.PwdEmpty);
21560             this.errorMsg = this.errors.PwdEmpty;
21561             return false;
21562         }
21563         
21564         if(this.insecure){
21565             return true;
21566         }
21567         
21568         if ('[\x21-\x7e]*'.match(value)) {
21569             this.markInvalid(this.errors.PwdBadChar);
21570             this.errorMsg = this.errors.PwdBadChar;
21571             return false;
21572         }
21573         if (value.length < 6) {
21574             this.markInvalid(this.errors.PwdShort);
21575             this.errorMsg = this.errors.PwdShort;
21576             return false;
21577         }
21578         if (value.length > 16) {
21579             this.markInvalid(this.errors.PwdLong);
21580             this.errorMsg = this.errors.PwdLong;
21581             return false;
21582         }
21583         var strength;
21584         if (this.ClientSideStrongPassword(value)) {
21585             strength = 3;
21586         } else if (this.ClientSideMediumPassword(value)) {
21587             strength = 2;
21588         } else if (this.ClientSideWeakPassword(value)) {
21589             strength = 1;
21590         } else {
21591             strength = 0;
21592         }
21593
21594         
21595         if (strength < 2) {
21596             //this.markInvalid(this.errors.TooWeak);
21597             this.errorMsg = this.errors.TooWeak;
21598             //return false;
21599         }
21600         
21601         
21602         console.log('strength2: ' + strength);
21603         
21604         //var pm = this.trigger.child('div/div/div').dom;
21605         
21606         var pm = this.trigger.child('div/div');
21607         pm.removeClass(this.meterClass);
21608         pm.addClass(this.meterClass[strength]);
21609                 
21610         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21611                 
21612         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21613         
21614         this.errorMsg = ''; 
21615         return true;
21616     },
21617     // private
21618     CharacterSetChecks: function (type)
21619     {
21620         this.type = type;
21621         this.fResult = false;
21622     },
21623     // private
21624     isctype: function (character, type)
21625     {
21626         switch (type) {  
21627             case this.kCapitalLetter:
21628                 if (character >= 'A' && character <= 'Z') {
21629                     return true;
21630                 }
21631                 break;
21632             
21633             case this.kSmallLetter:
21634                 if (character >= 'a' && character <= 'z') {
21635                     return true;
21636                 }
21637                 break;
21638             
21639             case this.kDigit:
21640                 if (character >= '0' && character <= '9') {
21641                     return true;
21642                 }
21643                 break;
21644             
21645             case this.kPunctuation:
21646                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21647                     return true;
21648                 }
21649                 break;
21650             
21651             default:
21652                 return false;
21653         }
21654
21655     },
21656     // private
21657     IsLongEnough: function (pwd, size)
21658     {
21659         return !(pwd == null || isNaN(size) || pwd.length < size);
21660     },
21661     // private
21662     SpansEnoughCharacterSets: function (word, nb)
21663     {
21664         if (!this.IsLongEnough(word, nb))
21665         {
21666             return false;
21667         }
21668
21669         var characterSetChecks = new Array(
21670             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21671             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21672         );
21673         
21674         for (var index = 0; index < word.length; ++index) {
21675             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21676                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21677                     characterSetChecks[nCharSet].fResult = true;
21678                     break;
21679                 }
21680             }
21681         }
21682
21683         var nCharSets = 0;
21684         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21685             if (characterSetChecks[nCharSet].fResult) {
21686                 ++nCharSets;
21687             }
21688         }
21689
21690         if (nCharSets < nb) {
21691             return false;
21692         }
21693         return true;
21694     },
21695     // private
21696     ClientSideStrongPassword: function (pwd)
21697     {
21698         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21699     },
21700     // private
21701     ClientSideMediumPassword: function (pwd)
21702     {
21703         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21704     },
21705     // private
21706     ClientSideWeakPassword: function (pwd)
21707     {
21708         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21709     }
21710           
21711 })//<script type="text/javascript">
21712
21713 /*
21714  * Based  Ext JS Library 1.1.1
21715  * Copyright(c) 2006-2007, Ext JS, LLC.
21716  * LGPL
21717  *
21718  */
21719  
21720 /**
21721  * @class Roo.HtmlEditorCore
21722  * @extends Roo.Component
21723  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21724  *
21725  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21726  */
21727
21728 Roo.HtmlEditorCore = function(config){
21729     
21730     
21731     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21732     
21733     
21734     this.addEvents({
21735         /**
21736          * @event initialize
21737          * Fires when the editor is fully initialized (including the iframe)
21738          * @param {Roo.HtmlEditorCore} this
21739          */
21740         initialize: true,
21741         /**
21742          * @event activate
21743          * Fires when the editor is first receives the focus. Any insertion must wait
21744          * until after this event.
21745          * @param {Roo.HtmlEditorCore} this
21746          */
21747         activate: true,
21748          /**
21749          * @event beforesync
21750          * Fires before the textarea is updated with content from the editor iframe. Return false
21751          * to cancel the sync.
21752          * @param {Roo.HtmlEditorCore} this
21753          * @param {String} html
21754          */
21755         beforesync: true,
21756          /**
21757          * @event beforepush
21758          * Fires before the iframe editor is updated with content from the textarea. Return false
21759          * to cancel the push.
21760          * @param {Roo.HtmlEditorCore} this
21761          * @param {String} html
21762          */
21763         beforepush: true,
21764          /**
21765          * @event sync
21766          * Fires when the textarea is updated with content from the editor iframe.
21767          * @param {Roo.HtmlEditorCore} this
21768          * @param {String} html
21769          */
21770         sync: true,
21771          /**
21772          * @event push
21773          * Fires when the iframe editor is updated with content from the textarea.
21774          * @param {Roo.HtmlEditorCore} this
21775          * @param {String} html
21776          */
21777         push: true,
21778         
21779         /**
21780          * @event editorevent
21781          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21782          * @param {Roo.HtmlEditorCore} this
21783          */
21784         editorevent: true
21785         
21786     });
21787     
21788     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21789     
21790     // defaults : white / black...
21791     this.applyBlacklists();
21792     
21793     
21794     
21795 };
21796
21797
21798 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21799
21800
21801      /**
21802      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21803      */
21804     
21805     owner : false,
21806     
21807      /**
21808      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21809      *                        Roo.resizable.
21810      */
21811     resizable : false,
21812      /**
21813      * @cfg {Number} height (in pixels)
21814      */   
21815     height: 300,
21816    /**
21817      * @cfg {Number} width (in pixels)
21818      */   
21819     width: 500,
21820     
21821     /**
21822      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21823      * 
21824      */
21825     stylesheets: false,
21826     
21827     // id of frame..
21828     frameId: false,
21829     
21830     // private properties
21831     validationEvent : false,
21832     deferHeight: true,
21833     initialized : false,
21834     activated : false,
21835     sourceEditMode : false,
21836     onFocus : Roo.emptyFn,
21837     iframePad:3,
21838     hideMode:'offsets',
21839     
21840     clearUp: true,
21841     
21842     // blacklist + whitelisted elements..
21843     black: false,
21844     white: false,
21845      
21846     bodyCls : '',
21847
21848     /**
21849      * Protected method that will not generally be called directly. It
21850      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21851      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21852      */
21853     getDocMarkup : function(){
21854         // body styles..
21855         var st = '';
21856         
21857         // inherit styels from page...?? 
21858         if (this.stylesheets === false) {
21859             
21860             Roo.get(document.head).select('style').each(function(node) {
21861                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21862             });
21863             
21864             Roo.get(document.head).select('link').each(function(node) { 
21865                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21866             });
21867             
21868         } else if (!this.stylesheets.length) {
21869                 // simple..
21870                 st = '<style type="text/css">' +
21871                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21872                    '</style>';
21873         } else { 
21874             st = '<style type="text/css">' +
21875                     this.stylesheets +
21876                 '</style>';
21877         }
21878         
21879         st +=  '<style type="text/css">' +
21880             'IMG { cursor: pointer } ' +
21881         '</style>';
21882
21883         var cls = 'roo-htmleditor-body';
21884         
21885         if(this.bodyCls.length){
21886             cls += ' ' + this.bodyCls;
21887         }
21888         
21889         return '<html><head>' + st  +
21890             //<style type="text/css">' +
21891             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21892             //'</style>' +
21893             ' </head><body class="' +  cls + '"></body></html>';
21894     },
21895
21896     // private
21897     onRender : function(ct, position)
21898     {
21899         var _t = this;
21900         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21901         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21902         
21903         
21904         this.el.dom.style.border = '0 none';
21905         this.el.dom.setAttribute('tabIndex', -1);
21906         this.el.addClass('x-hidden hide');
21907         
21908         
21909         
21910         if(Roo.isIE){ // fix IE 1px bogus margin
21911             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21912         }
21913        
21914         
21915         this.frameId = Roo.id();
21916         
21917          
21918         
21919         var iframe = this.owner.wrap.createChild({
21920             tag: 'iframe',
21921             cls: 'form-control', // bootstrap..
21922             id: this.frameId,
21923             name: this.frameId,
21924             frameBorder : 'no',
21925             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21926         }, this.el
21927         );
21928         
21929         
21930         this.iframe = iframe.dom;
21931
21932          this.assignDocWin();
21933         
21934         this.doc.designMode = 'on';
21935        
21936         this.doc.open();
21937         this.doc.write(this.getDocMarkup());
21938         this.doc.close();
21939
21940         
21941         var task = { // must defer to wait for browser to be ready
21942             run : function(){
21943                 //console.log("run task?" + this.doc.readyState);
21944                 this.assignDocWin();
21945                 if(this.doc.body || this.doc.readyState == 'complete'){
21946                     try {
21947                         this.doc.designMode="on";
21948                     } catch (e) {
21949                         return;
21950                     }
21951                     Roo.TaskMgr.stop(task);
21952                     this.initEditor.defer(10, this);
21953                 }
21954             },
21955             interval : 10,
21956             duration: 10000,
21957             scope: this
21958         };
21959         Roo.TaskMgr.start(task);
21960
21961     },
21962
21963     // private
21964     onResize : function(w, h)
21965     {
21966          Roo.log('resize: ' +w + ',' + h );
21967         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21968         if(!this.iframe){
21969             return;
21970         }
21971         if(typeof w == 'number'){
21972             
21973             this.iframe.style.width = w + 'px';
21974         }
21975         if(typeof h == 'number'){
21976             
21977             this.iframe.style.height = h + 'px';
21978             if(this.doc){
21979                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21980             }
21981         }
21982         
21983     },
21984
21985     /**
21986      * Toggles the editor between standard and source edit mode.
21987      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21988      */
21989     toggleSourceEdit : function(sourceEditMode){
21990         
21991         this.sourceEditMode = sourceEditMode === true;
21992         
21993         if(this.sourceEditMode){
21994  
21995             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21996             
21997         }else{
21998             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21999             //this.iframe.className = '';
22000             this.deferFocus();
22001         }
22002         //this.setSize(this.owner.wrap.getSize());
22003         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22004     },
22005
22006     
22007   
22008
22009     /**
22010      * Protected method that will not generally be called directly. If you need/want
22011      * custom HTML cleanup, this is the method you should override.
22012      * @param {String} html The HTML to be cleaned
22013      * return {String} The cleaned HTML
22014      */
22015     cleanHtml : function(html){
22016         html = String(html);
22017         if(html.length > 5){
22018             if(Roo.isSafari){ // strip safari nonsense
22019                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22020             }
22021         }
22022         if(html == '&nbsp;'){
22023             html = '';
22024         }
22025         return html;
22026     },
22027
22028     /**
22029      * HTML Editor -> Textarea
22030      * Protected method that will not generally be called directly. Syncs the contents
22031      * of the editor iframe with the textarea.
22032      */
22033     syncValue : function(){
22034         if(this.initialized){
22035             var bd = (this.doc.body || this.doc.documentElement);
22036             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22037             var html = bd.innerHTML;
22038             if(Roo.isSafari){
22039                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22040                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22041                 if(m && m[1]){
22042                     html = '<div style="'+m[0]+'">' + html + '</div>';
22043                 }
22044             }
22045             html = this.cleanHtml(html);
22046             // fix up the special chars.. normaly like back quotes in word...
22047             // however we do not want to do this with chinese..
22048             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22049                 var cc = b.charCodeAt();
22050                 if (
22051                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22052                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22053                     (cc >= 0xf900 && cc < 0xfb00 )
22054                 ) {
22055                         return b;
22056                 }
22057                 return "&#"+cc+";" 
22058             });
22059             if(this.owner.fireEvent('beforesync', this, html) !== false){
22060                 this.el.dom.value = html;
22061                 this.owner.fireEvent('sync', this, html);
22062             }
22063         }
22064     },
22065
22066     /**
22067      * Protected method that will not generally be called directly. Pushes the value of the textarea
22068      * into the iframe editor.
22069      */
22070     pushValue : function(){
22071         if(this.initialized){
22072             var v = this.el.dom.value.trim();
22073             
22074 //            if(v.length < 1){
22075 //                v = '&#160;';
22076 //            }
22077             
22078             if(this.owner.fireEvent('beforepush', this, v) !== false){
22079                 var d = (this.doc.body || this.doc.documentElement);
22080                 d.innerHTML = v;
22081                 this.cleanUpPaste();
22082                 this.el.dom.value = d.innerHTML;
22083                 this.owner.fireEvent('push', this, v);
22084             }
22085         }
22086     },
22087
22088     // private
22089     deferFocus : function(){
22090         this.focus.defer(10, this);
22091     },
22092
22093     // doc'ed in Field
22094     focus : function(){
22095         if(this.win && !this.sourceEditMode){
22096             this.win.focus();
22097         }else{
22098             this.el.focus();
22099         }
22100     },
22101     
22102     assignDocWin: function()
22103     {
22104         var iframe = this.iframe;
22105         
22106          if(Roo.isIE){
22107             this.doc = iframe.contentWindow.document;
22108             this.win = iframe.contentWindow;
22109         } else {
22110 //            if (!Roo.get(this.frameId)) {
22111 //                return;
22112 //            }
22113 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22114 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22115             
22116             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22117                 return;
22118             }
22119             
22120             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22121             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22122         }
22123     },
22124     
22125     // private
22126     initEditor : function(){
22127         //console.log("INIT EDITOR");
22128         this.assignDocWin();
22129         
22130         
22131         
22132         this.doc.designMode="on";
22133         this.doc.open();
22134         this.doc.write(this.getDocMarkup());
22135         this.doc.close();
22136         
22137         var dbody = (this.doc.body || this.doc.documentElement);
22138         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22139         // this copies styles from the containing element into thsi one..
22140         // not sure why we need all of this..
22141         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22142         
22143         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22144         //ss['background-attachment'] = 'fixed'; // w3c
22145         dbody.bgProperties = 'fixed'; // ie
22146         //Roo.DomHelper.applyStyles(dbody, ss);
22147         Roo.EventManager.on(this.doc, {
22148             //'mousedown': this.onEditorEvent,
22149             'mouseup': this.onEditorEvent,
22150             'dblclick': this.onEditorEvent,
22151             'click': this.onEditorEvent,
22152             'keyup': this.onEditorEvent,
22153             buffer:100,
22154             scope: this
22155         });
22156         if(Roo.isGecko){
22157             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22158         }
22159         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22160             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22161         }
22162         this.initialized = true;
22163
22164         this.owner.fireEvent('initialize', this);
22165         this.pushValue();
22166     },
22167
22168     // private
22169     onDestroy : function(){
22170         
22171         
22172         
22173         if(this.rendered){
22174             
22175             //for (var i =0; i < this.toolbars.length;i++) {
22176             //    // fixme - ask toolbars for heights?
22177             //    this.toolbars[i].onDestroy();
22178            // }
22179             
22180             //this.wrap.dom.innerHTML = '';
22181             //this.wrap.remove();
22182         }
22183     },
22184
22185     // private
22186     onFirstFocus : function(){
22187         
22188         this.assignDocWin();
22189         
22190         
22191         this.activated = true;
22192          
22193     
22194         if(Roo.isGecko){ // prevent silly gecko errors
22195             this.win.focus();
22196             var s = this.win.getSelection();
22197             if(!s.focusNode || s.focusNode.nodeType != 3){
22198                 var r = s.getRangeAt(0);
22199                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22200                 r.collapse(true);
22201                 this.deferFocus();
22202             }
22203             try{
22204                 this.execCmd('useCSS', true);
22205                 this.execCmd('styleWithCSS', false);
22206             }catch(e){}
22207         }
22208         this.owner.fireEvent('activate', this);
22209     },
22210
22211     // private
22212     adjustFont: function(btn){
22213         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22214         //if(Roo.isSafari){ // safari
22215         //    adjust *= 2;
22216        // }
22217         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22218         if(Roo.isSafari){ // safari
22219             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22220             v =  (v < 10) ? 10 : v;
22221             v =  (v > 48) ? 48 : v;
22222             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22223             
22224         }
22225         
22226         
22227         v = Math.max(1, v+adjust);
22228         
22229         this.execCmd('FontSize', v  );
22230     },
22231
22232     onEditorEvent : function(e)
22233     {
22234         this.owner.fireEvent('editorevent', this, e);
22235       //  this.updateToolbar();
22236         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22237     },
22238
22239     insertTag : function(tg)
22240     {
22241         // could be a bit smarter... -> wrap the current selected tRoo..
22242         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22243             
22244             range = this.createRange(this.getSelection());
22245             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22246             wrappingNode.appendChild(range.extractContents());
22247             range.insertNode(wrappingNode);
22248
22249             return;
22250             
22251             
22252             
22253         }
22254         this.execCmd("formatblock",   tg);
22255         
22256     },
22257     
22258     insertText : function(txt)
22259     {
22260         
22261         
22262         var range = this.createRange();
22263         range.deleteContents();
22264                //alert(Sender.getAttribute('label'));
22265                
22266         range.insertNode(this.doc.createTextNode(txt));
22267     } ,
22268     
22269      
22270
22271     /**
22272      * Executes a Midas editor command on the editor document and performs necessary focus and
22273      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22274      * @param {String} cmd The Midas command
22275      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22276      */
22277     relayCmd : function(cmd, value){
22278         this.win.focus();
22279         this.execCmd(cmd, value);
22280         this.owner.fireEvent('editorevent', this);
22281         //this.updateToolbar();
22282         this.owner.deferFocus();
22283     },
22284
22285     /**
22286      * Executes a Midas editor command directly on the editor document.
22287      * For visual commands, you should use {@link #relayCmd} instead.
22288      * <b>This should only be called after the editor is initialized.</b>
22289      * @param {String} cmd The Midas command
22290      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22291      */
22292     execCmd : function(cmd, value){
22293         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22294         this.syncValue();
22295     },
22296  
22297  
22298    
22299     /**
22300      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22301      * to insert tRoo.
22302      * @param {String} text | dom node.. 
22303      */
22304     insertAtCursor : function(text)
22305     {
22306         
22307         if(!this.activated){
22308             return;
22309         }
22310         /*
22311         if(Roo.isIE){
22312             this.win.focus();
22313             var r = this.doc.selection.createRange();
22314             if(r){
22315                 r.collapse(true);
22316                 r.pasteHTML(text);
22317                 this.syncValue();
22318                 this.deferFocus();
22319             
22320             }
22321             return;
22322         }
22323         */
22324         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22325             this.win.focus();
22326             
22327             
22328             // from jquery ui (MIT licenced)
22329             var range, node;
22330             var win = this.win;
22331             
22332             if (win.getSelection && win.getSelection().getRangeAt) {
22333                 range = win.getSelection().getRangeAt(0);
22334                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22335                 range.insertNode(node);
22336             } else if (win.document.selection && win.document.selection.createRange) {
22337                 // no firefox support
22338                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22339                 win.document.selection.createRange().pasteHTML(txt);
22340             } else {
22341                 // no firefox support
22342                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22343                 this.execCmd('InsertHTML', txt);
22344             } 
22345             
22346             this.syncValue();
22347             
22348             this.deferFocus();
22349         }
22350     },
22351  // private
22352     mozKeyPress : function(e){
22353         if(e.ctrlKey){
22354             var c = e.getCharCode(), cmd;
22355           
22356             if(c > 0){
22357                 c = String.fromCharCode(c).toLowerCase();
22358                 switch(c){
22359                     case 'b':
22360                         cmd = 'bold';
22361                         break;
22362                     case 'i':
22363                         cmd = 'italic';
22364                         break;
22365                     
22366                     case 'u':
22367                         cmd = 'underline';
22368                         break;
22369                     
22370                     case 'v':
22371                         this.cleanUpPaste.defer(100, this);
22372                         return;
22373                         
22374                 }
22375                 if(cmd){
22376                     this.win.focus();
22377                     this.execCmd(cmd);
22378                     this.deferFocus();
22379                     e.preventDefault();
22380                 }
22381                 
22382             }
22383         }
22384     },
22385
22386     // private
22387     fixKeys : function(){ // load time branching for fastest keydown performance
22388         if(Roo.isIE){
22389             return function(e){
22390                 var k = e.getKey(), r;
22391                 if(k == e.TAB){
22392                     e.stopEvent();
22393                     r = this.doc.selection.createRange();
22394                     if(r){
22395                         r.collapse(true);
22396                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22397                         this.deferFocus();
22398                     }
22399                     return;
22400                 }
22401                 
22402                 if(k == e.ENTER){
22403                     r = this.doc.selection.createRange();
22404                     if(r){
22405                         var target = r.parentElement();
22406                         if(!target || target.tagName.toLowerCase() != 'li'){
22407                             e.stopEvent();
22408                             r.pasteHTML('<br />');
22409                             r.collapse(false);
22410                             r.select();
22411                         }
22412                     }
22413                 }
22414                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22415                     this.cleanUpPaste.defer(100, this);
22416                     return;
22417                 }
22418                 
22419                 
22420             };
22421         }else if(Roo.isOpera){
22422             return function(e){
22423                 var k = e.getKey();
22424                 if(k == e.TAB){
22425                     e.stopEvent();
22426                     this.win.focus();
22427                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22428                     this.deferFocus();
22429                 }
22430                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22431                     this.cleanUpPaste.defer(100, this);
22432                     return;
22433                 }
22434                 
22435             };
22436         }else if(Roo.isSafari){
22437             return function(e){
22438                 var k = e.getKey();
22439                 
22440                 if(k == e.TAB){
22441                     e.stopEvent();
22442                     this.execCmd('InsertText','\t');
22443                     this.deferFocus();
22444                     return;
22445                 }
22446                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22447                     this.cleanUpPaste.defer(100, this);
22448                     return;
22449                 }
22450                 
22451              };
22452         }
22453     }(),
22454     
22455     getAllAncestors: function()
22456     {
22457         var p = this.getSelectedNode();
22458         var a = [];
22459         if (!p) {
22460             a.push(p); // push blank onto stack..
22461             p = this.getParentElement();
22462         }
22463         
22464         
22465         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22466             a.push(p);
22467             p = p.parentNode;
22468         }
22469         a.push(this.doc.body);
22470         return a;
22471     },
22472     lastSel : false,
22473     lastSelNode : false,
22474     
22475     
22476     getSelection : function() 
22477     {
22478         this.assignDocWin();
22479         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22480     },
22481     
22482     getSelectedNode: function() 
22483     {
22484         // this may only work on Gecko!!!
22485         
22486         // should we cache this!!!!
22487         
22488         
22489         
22490          
22491         var range = this.createRange(this.getSelection()).cloneRange();
22492         
22493         if (Roo.isIE) {
22494             var parent = range.parentElement();
22495             while (true) {
22496                 var testRange = range.duplicate();
22497                 testRange.moveToElementText(parent);
22498                 if (testRange.inRange(range)) {
22499                     break;
22500                 }
22501                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22502                     break;
22503                 }
22504                 parent = parent.parentElement;
22505             }
22506             return parent;
22507         }
22508         
22509         // is ancestor a text element.
22510         var ac =  range.commonAncestorContainer;
22511         if (ac.nodeType == 3) {
22512             ac = ac.parentNode;
22513         }
22514         
22515         var ar = ac.childNodes;
22516          
22517         var nodes = [];
22518         var other_nodes = [];
22519         var has_other_nodes = false;
22520         for (var i=0;i<ar.length;i++) {
22521             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22522                 continue;
22523             }
22524             // fullly contained node.
22525             
22526             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22527                 nodes.push(ar[i]);
22528                 continue;
22529             }
22530             
22531             // probably selected..
22532             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22533                 other_nodes.push(ar[i]);
22534                 continue;
22535             }
22536             // outer..
22537             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22538                 continue;
22539             }
22540             
22541             
22542             has_other_nodes = true;
22543         }
22544         if (!nodes.length && other_nodes.length) {
22545             nodes= other_nodes;
22546         }
22547         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22548             return false;
22549         }
22550         
22551         return nodes[0];
22552     },
22553     createRange: function(sel)
22554     {
22555         // this has strange effects when using with 
22556         // top toolbar - not sure if it's a great idea.
22557         //this.editor.contentWindow.focus();
22558         if (typeof sel != "undefined") {
22559             try {
22560                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22561             } catch(e) {
22562                 return this.doc.createRange();
22563             }
22564         } else {
22565             return this.doc.createRange();
22566         }
22567     },
22568     getParentElement: function()
22569     {
22570         
22571         this.assignDocWin();
22572         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22573         
22574         var range = this.createRange(sel);
22575          
22576         try {
22577             var p = range.commonAncestorContainer;
22578             while (p.nodeType == 3) { // text node
22579                 p = p.parentNode;
22580             }
22581             return p;
22582         } catch (e) {
22583             return null;
22584         }
22585     
22586     },
22587     /***
22588      *
22589      * Range intersection.. the hard stuff...
22590      *  '-1' = before
22591      *  '0' = hits..
22592      *  '1' = after.
22593      *         [ -- selected range --- ]
22594      *   [fail]                        [fail]
22595      *
22596      *    basically..
22597      *      if end is before start or  hits it. fail.
22598      *      if start is after end or hits it fail.
22599      *
22600      *   if either hits (but other is outside. - then it's not 
22601      *   
22602      *    
22603      **/
22604     
22605     
22606     // @see http://www.thismuchiknow.co.uk/?p=64.
22607     rangeIntersectsNode : function(range, node)
22608     {
22609         var nodeRange = node.ownerDocument.createRange();
22610         try {
22611             nodeRange.selectNode(node);
22612         } catch (e) {
22613             nodeRange.selectNodeContents(node);
22614         }
22615     
22616         var rangeStartRange = range.cloneRange();
22617         rangeStartRange.collapse(true);
22618     
22619         var rangeEndRange = range.cloneRange();
22620         rangeEndRange.collapse(false);
22621     
22622         var nodeStartRange = nodeRange.cloneRange();
22623         nodeStartRange.collapse(true);
22624     
22625         var nodeEndRange = nodeRange.cloneRange();
22626         nodeEndRange.collapse(false);
22627     
22628         return rangeStartRange.compareBoundaryPoints(
22629                  Range.START_TO_START, nodeEndRange) == -1 &&
22630                rangeEndRange.compareBoundaryPoints(
22631                  Range.START_TO_START, nodeStartRange) == 1;
22632         
22633          
22634     },
22635     rangeCompareNode : function(range, node)
22636     {
22637         var nodeRange = node.ownerDocument.createRange();
22638         try {
22639             nodeRange.selectNode(node);
22640         } catch (e) {
22641             nodeRange.selectNodeContents(node);
22642         }
22643         
22644         
22645         range.collapse(true);
22646     
22647         nodeRange.collapse(true);
22648      
22649         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22650         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22651          
22652         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22653         
22654         var nodeIsBefore   =  ss == 1;
22655         var nodeIsAfter    = ee == -1;
22656         
22657         if (nodeIsBefore && nodeIsAfter) {
22658             return 0; // outer
22659         }
22660         if (!nodeIsBefore && nodeIsAfter) {
22661             return 1; //right trailed.
22662         }
22663         
22664         if (nodeIsBefore && !nodeIsAfter) {
22665             return 2;  // left trailed.
22666         }
22667         // fully contined.
22668         return 3;
22669     },
22670
22671     // private? - in a new class?
22672     cleanUpPaste :  function()
22673     {
22674         // cleans up the whole document..
22675         Roo.log('cleanuppaste');
22676         
22677         this.cleanUpChildren(this.doc.body);
22678         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22679         if (clean != this.doc.body.innerHTML) {
22680             this.doc.body.innerHTML = clean;
22681         }
22682         
22683     },
22684     
22685     cleanWordChars : function(input) {// change the chars to hex code
22686         var he = Roo.HtmlEditorCore;
22687         
22688         var output = input;
22689         Roo.each(he.swapCodes, function(sw) { 
22690             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22691             
22692             output = output.replace(swapper, sw[1]);
22693         });
22694         
22695         return output;
22696     },
22697     
22698     
22699     cleanUpChildren : function (n)
22700     {
22701         if (!n.childNodes.length) {
22702             return;
22703         }
22704         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22705            this.cleanUpChild(n.childNodes[i]);
22706         }
22707     },
22708     
22709     
22710         
22711     
22712     cleanUpChild : function (node)
22713     {
22714         var ed = this;
22715         //console.log(node);
22716         if (node.nodeName == "#text") {
22717             // clean up silly Windows -- stuff?
22718             return; 
22719         }
22720         if (node.nodeName == "#comment") {
22721             node.parentNode.removeChild(node);
22722             // clean up silly Windows -- stuff?
22723             return; 
22724         }
22725         var lcname = node.tagName.toLowerCase();
22726         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22727         // whitelist of tags..
22728         
22729         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22730             // remove node.
22731             node.parentNode.removeChild(node);
22732             return;
22733             
22734         }
22735         
22736         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22737         
22738         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22739         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22740         
22741         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22742         //    remove_keep_children = true;
22743         //}
22744         
22745         if (remove_keep_children) {
22746             this.cleanUpChildren(node);
22747             // inserts everything just before this node...
22748             while (node.childNodes.length) {
22749                 var cn = node.childNodes[0];
22750                 node.removeChild(cn);
22751                 node.parentNode.insertBefore(cn, node);
22752             }
22753             node.parentNode.removeChild(node);
22754             return;
22755         }
22756         
22757         if (!node.attributes || !node.attributes.length) {
22758             this.cleanUpChildren(node);
22759             return;
22760         }
22761         
22762         function cleanAttr(n,v)
22763         {
22764             
22765             if (v.match(/^\./) || v.match(/^\//)) {
22766                 return;
22767             }
22768             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22769                 return;
22770             }
22771             if (v.match(/^#/)) {
22772                 return;
22773             }
22774 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22775             node.removeAttribute(n);
22776             
22777         }
22778         
22779         var cwhite = this.cwhite;
22780         var cblack = this.cblack;
22781             
22782         function cleanStyle(n,v)
22783         {
22784             if (v.match(/expression/)) { //XSS?? should we even bother..
22785                 node.removeAttribute(n);
22786                 return;
22787             }
22788             
22789             var parts = v.split(/;/);
22790             var clean = [];
22791             
22792             Roo.each(parts, function(p) {
22793                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22794                 if (!p.length) {
22795                     return true;
22796                 }
22797                 var l = p.split(':').shift().replace(/\s+/g,'');
22798                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22799                 
22800                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22801 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22802                     //node.removeAttribute(n);
22803                     return true;
22804                 }
22805                 //Roo.log()
22806                 // only allow 'c whitelisted system attributes'
22807                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22808 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22809                     //node.removeAttribute(n);
22810                     return true;
22811                 }
22812                 
22813                 
22814                  
22815                 
22816                 clean.push(p);
22817                 return true;
22818             });
22819             if (clean.length) { 
22820                 node.setAttribute(n, clean.join(';'));
22821             } else {
22822                 node.removeAttribute(n);
22823             }
22824             
22825         }
22826         
22827         
22828         for (var i = node.attributes.length-1; i > -1 ; i--) {
22829             var a = node.attributes[i];
22830             //console.log(a);
22831             
22832             if (a.name.toLowerCase().substr(0,2)=='on')  {
22833                 node.removeAttribute(a.name);
22834                 continue;
22835             }
22836             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22837                 node.removeAttribute(a.name);
22838                 continue;
22839             }
22840             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22841                 cleanAttr(a.name,a.value); // fixme..
22842                 continue;
22843             }
22844             if (a.name == 'style') {
22845                 cleanStyle(a.name,a.value);
22846                 continue;
22847             }
22848             /// clean up MS crap..
22849             // tecnically this should be a list of valid class'es..
22850             
22851             
22852             if (a.name == 'class') {
22853                 if (a.value.match(/^Mso/)) {
22854                     node.className = '';
22855                 }
22856                 
22857                 if (a.value.match(/^body$/)) {
22858                     node.className = '';
22859                 }
22860                 continue;
22861             }
22862             
22863             // style cleanup!?
22864             // class cleanup?
22865             
22866         }
22867         
22868         
22869         this.cleanUpChildren(node);
22870         
22871         
22872     },
22873     
22874     /**
22875      * Clean up MS wordisms...
22876      */
22877     cleanWord : function(node)
22878     {
22879         
22880         
22881         if (!node) {
22882             this.cleanWord(this.doc.body);
22883             return;
22884         }
22885         if (node.nodeName == "#text") {
22886             // clean up silly Windows -- stuff?
22887             return; 
22888         }
22889         if (node.nodeName == "#comment") {
22890             node.parentNode.removeChild(node);
22891             // clean up silly Windows -- stuff?
22892             return; 
22893         }
22894         
22895         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22896             node.parentNode.removeChild(node);
22897             return;
22898         }
22899         
22900         // remove - but keep children..
22901         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22902             while (node.childNodes.length) {
22903                 var cn = node.childNodes[0];
22904                 node.removeChild(cn);
22905                 node.parentNode.insertBefore(cn, node);
22906             }
22907             node.parentNode.removeChild(node);
22908             this.iterateChildren(node, this.cleanWord);
22909             return;
22910         }
22911         // clean styles
22912         if (node.className.length) {
22913             
22914             var cn = node.className.split(/\W+/);
22915             var cna = [];
22916             Roo.each(cn, function(cls) {
22917                 if (cls.match(/Mso[a-zA-Z]+/)) {
22918                     return;
22919                 }
22920                 cna.push(cls);
22921             });
22922             node.className = cna.length ? cna.join(' ') : '';
22923             if (!cna.length) {
22924                 node.removeAttribute("class");
22925             }
22926         }
22927         
22928         if (node.hasAttribute("lang")) {
22929             node.removeAttribute("lang");
22930         }
22931         
22932         if (node.hasAttribute("style")) {
22933             
22934             var styles = node.getAttribute("style").split(";");
22935             var nstyle = [];
22936             Roo.each(styles, function(s) {
22937                 if (!s.match(/:/)) {
22938                     return;
22939                 }
22940                 var kv = s.split(":");
22941                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22942                     return;
22943                 }
22944                 // what ever is left... we allow.
22945                 nstyle.push(s);
22946             });
22947             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22948             if (!nstyle.length) {
22949                 node.removeAttribute('style');
22950             }
22951         }
22952         this.iterateChildren(node, this.cleanWord);
22953         
22954         
22955         
22956     },
22957     /**
22958      * iterateChildren of a Node, calling fn each time, using this as the scole..
22959      * @param {DomNode} node node to iterate children of.
22960      * @param {Function} fn method of this class to call on each item.
22961      */
22962     iterateChildren : function(node, fn)
22963     {
22964         if (!node.childNodes.length) {
22965                 return;
22966         }
22967         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22968            fn.call(this, node.childNodes[i])
22969         }
22970     },
22971     
22972     
22973     /**
22974      * cleanTableWidths.
22975      *
22976      * Quite often pasting from word etc.. results in tables with column and widths.
22977      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22978      *
22979      */
22980     cleanTableWidths : function(node)
22981     {
22982          
22983          
22984         if (!node) {
22985             this.cleanTableWidths(this.doc.body);
22986             return;
22987         }
22988         
22989         // ignore list...
22990         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22991             return; 
22992         }
22993         Roo.log(node.tagName);
22994         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22995             this.iterateChildren(node, this.cleanTableWidths);
22996             return;
22997         }
22998         if (node.hasAttribute('width')) {
22999             node.removeAttribute('width');
23000         }
23001         
23002          
23003         if (node.hasAttribute("style")) {
23004             // pretty basic...
23005             
23006             var styles = node.getAttribute("style").split(";");
23007             var nstyle = [];
23008             Roo.each(styles, function(s) {
23009                 if (!s.match(/:/)) {
23010                     return;
23011                 }
23012                 var kv = s.split(":");
23013                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23014                     return;
23015                 }
23016                 // what ever is left... we allow.
23017                 nstyle.push(s);
23018             });
23019             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23020             if (!nstyle.length) {
23021                 node.removeAttribute('style');
23022             }
23023         }
23024         
23025         this.iterateChildren(node, this.cleanTableWidths);
23026         
23027         
23028     },
23029     
23030     
23031     
23032     
23033     domToHTML : function(currentElement, depth, nopadtext) {
23034         
23035         depth = depth || 0;
23036         nopadtext = nopadtext || false;
23037     
23038         if (!currentElement) {
23039             return this.domToHTML(this.doc.body);
23040         }
23041         
23042         //Roo.log(currentElement);
23043         var j;
23044         var allText = false;
23045         var nodeName = currentElement.nodeName;
23046         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23047         
23048         if  (nodeName == '#text') {
23049             
23050             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23051         }
23052         
23053         
23054         var ret = '';
23055         if (nodeName != 'BODY') {
23056              
23057             var i = 0;
23058             // Prints the node tagName, such as <A>, <IMG>, etc
23059             if (tagName) {
23060                 var attr = [];
23061                 for(i = 0; i < currentElement.attributes.length;i++) {
23062                     // quoting?
23063                     var aname = currentElement.attributes.item(i).name;
23064                     if (!currentElement.attributes.item(i).value.length) {
23065                         continue;
23066                     }
23067                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23068                 }
23069                 
23070                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23071             } 
23072             else {
23073                 
23074                 // eack
23075             }
23076         } else {
23077             tagName = false;
23078         }
23079         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23080             return ret;
23081         }
23082         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23083             nopadtext = true;
23084         }
23085         
23086         
23087         // Traverse the tree
23088         i = 0;
23089         var currentElementChild = currentElement.childNodes.item(i);
23090         var allText = true;
23091         var innerHTML  = '';
23092         lastnode = '';
23093         while (currentElementChild) {
23094             // Formatting code (indent the tree so it looks nice on the screen)
23095             var nopad = nopadtext;
23096             if (lastnode == 'SPAN') {
23097                 nopad  = true;
23098             }
23099             // text
23100             if  (currentElementChild.nodeName == '#text') {
23101                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23102                 toadd = nopadtext ? toadd : toadd.trim();
23103                 if (!nopad && toadd.length > 80) {
23104                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23105                 }
23106                 innerHTML  += toadd;
23107                 
23108                 i++;
23109                 currentElementChild = currentElement.childNodes.item(i);
23110                 lastNode = '';
23111                 continue;
23112             }
23113             allText = false;
23114             
23115             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23116                 
23117             // Recursively traverse the tree structure of the child node
23118             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23119             lastnode = currentElementChild.nodeName;
23120             i++;
23121             currentElementChild=currentElement.childNodes.item(i);
23122         }
23123         
23124         ret += innerHTML;
23125         
23126         if (!allText) {
23127                 // The remaining code is mostly for formatting the tree
23128             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23129         }
23130         
23131         
23132         if (tagName) {
23133             ret+= "</"+tagName+">";
23134         }
23135         return ret;
23136         
23137     },
23138         
23139     applyBlacklists : function()
23140     {
23141         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23142         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23143         
23144         this.white = [];
23145         this.black = [];
23146         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23147             if (b.indexOf(tag) > -1) {
23148                 return;
23149             }
23150             this.white.push(tag);
23151             
23152         }, this);
23153         
23154         Roo.each(w, function(tag) {
23155             if (b.indexOf(tag) > -1) {
23156                 return;
23157             }
23158             if (this.white.indexOf(tag) > -1) {
23159                 return;
23160             }
23161             this.white.push(tag);
23162             
23163         }, this);
23164         
23165         
23166         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23167             if (w.indexOf(tag) > -1) {
23168                 return;
23169             }
23170             this.black.push(tag);
23171             
23172         }, this);
23173         
23174         Roo.each(b, function(tag) {
23175             if (w.indexOf(tag) > -1) {
23176                 return;
23177             }
23178             if (this.black.indexOf(tag) > -1) {
23179                 return;
23180             }
23181             this.black.push(tag);
23182             
23183         }, this);
23184         
23185         
23186         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23187         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23188         
23189         this.cwhite = [];
23190         this.cblack = [];
23191         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23192             if (b.indexOf(tag) > -1) {
23193                 return;
23194             }
23195             this.cwhite.push(tag);
23196             
23197         }, this);
23198         
23199         Roo.each(w, function(tag) {
23200             if (b.indexOf(tag) > -1) {
23201                 return;
23202             }
23203             if (this.cwhite.indexOf(tag) > -1) {
23204                 return;
23205             }
23206             this.cwhite.push(tag);
23207             
23208         }, this);
23209         
23210         
23211         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23212             if (w.indexOf(tag) > -1) {
23213                 return;
23214             }
23215             this.cblack.push(tag);
23216             
23217         }, this);
23218         
23219         Roo.each(b, function(tag) {
23220             if (w.indexOf(tag) > -1) {
23221                 return;
23222             }
23223             if (this.cblack.indexOf(tag) > -1) {
23224                 return;
23225             }
23226             this.cblack.push(tag);
23227             
23228         }, this);
23229     },
23230     
23231     setStylesheets : function(stylesheets)
23232     {
23233         if(typeof(stylesheets) == 'string'){
23234             Roo.get(this.iframe.contentDocument.head).createChild({
23235                 tag : 'link',
23236                 rel : 'stylesheet',
23237                 type : 'text/css',
23238                 href : stylesheets
23239             });
23240             
23241             return;
23242         }
23243         var _this = this;
23244      
23245         Roo.each(stylesheets, function(s) {
23246             if(!s.length){
23247                 return;
23248             }
23249             
23250             Roo.get(_this.iframe.contentDocument.head).createChild({
23251                 tag : 'link',
23252                 rel : 'stylesheet',
23253                 type : 'text/css',
23254                 href : s
23255             });
23256         });
23257
23258         
23259     },
23260     
23261     removeStylesheets : function()
23262     {
23263         var _this = this;
23264         
23265         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23266             s.remove();
23267         });
23268     },
23269     
23270     setStyle : function(style)
23271     {
23272         Roo.get(this.iframe.contentDocument.head).createChild({
23273             tag : 'style',
23274             type : 'text/css',
23275             html : style
23276         });
23277
23278         return;
23279     }
23280     
23281     // hide stuff that is not compatible
23282     /**
23283      * @event blur
23284      * @hide
23285      */
23286     /**
23287      * @event change
23288      * @hide
23289      */
23290     /**
23291      * @event focus
23292      * @hide
23293      */
23294     /**
23295      * @event specialkey
23296      * @hide
23297      */
23298     /**
23299      * @cfg {String} fieldClass @hide
23300      */
23301     /**
23302      * @cfg {String} focusClass @hide
23303      */
23304     /**
23305      * @cfg {String} autoCreate @hide
23306      */
23307     /**
23308      * @cfg {String} inputType @hide
23309      */
23310     /**
23311      * @cfg {String} invalidClass @hide
23312      */
23313     /**
23314      * @cfg {String} invalidText @hide
23315      */
23316     /**
23317      * @cfg {String} msgFx @hide
23318      */
23319     /**
23320      * @cfg {String} validateOnBlur @hide
23321      */
23322 });
23323
23324 Roo.HtmlEditorCore.white = [
23325         'area', 'br', 'img', 'input', 'hr', 'wbr',
23326         
23327        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23328        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23329        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23330        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23331        'table',   'ul',         'xmp', 
23332        
23333        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23334       'thead',   'tr', 
23335      
23336       'dir', 'menu', 'ol', 'ul', 'dl',
23337        
23338       'embed',  'object'
23339 ];
23340
23341
23342 Roo.HtmlEditorCore.black = [
23343     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23344         'applet', // 
23345         'base',   'basefont', 'bgsound', 'blink',  'body', 
23346         'frame',  'frameset', 'head',    'html',   'ilayer', 
23347         'iframe', 'layer',  'link',     'meta',    'object',   
23348         'script', 'style' ,'title',  'xml' // clean later..
23349 ];
23350 Roo.HtmlEditorCore.clean = [
23351     'script', 'style', 'title', 'xml'
23352 ];
23353 Roo.HtmlEditorCore.remove = [
23354     'font'
23355 ];
23356 // attributes..
23357
23358 Roo.HtmlEditorCore.ablack = [
23359     'on'
23360 ];
23361     
23362 Roo.HtmlEditorCore.aclean = [ 
23363     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23364 ];
23365
23366 // protocols..
23367 Roo.HtmlEditorCore.pwhite= [
23368         'http',  'https',  'mailto'
23369 ];
23370
23371 // white listed style attributes.
23372 Roo.HtmlEditorCore.cwhite= [
23373       //  'text-align', /// default is to allow most things..
23374       
23375          
23376 //        'font-size'//??
23377 ];
23378
23379 // black listed style attributes.
23380 Roo.HtmlEditorCore.cblack= [
23381       //  'font-size' -- this can be set by the project 
23382 ];
23383
23384
23385 Roo.HtmlEditorCore.swapCodes   =[ 
23386     [    8211, "--" ], 
23387     [    8212, "--" ], 
23388     [    8216,  "'" ],  
23389     [    8217, "'" ],  
23390     [    8220, '"' ],  
23391     [    8221, '"' ],  
23392     [    8226, "*" ],  
23393     [    8230, "..." ]
23394 ]; 
23395
23396     /*
23397  * - LGPL
23398  *
23399  * HtmlEditor
23400  * 
23401  */
23402
23403 /**
23404  * @class Roo.bootstrap.HtmlEditor
23405  * @extends Roo.bootstrap.TextArea
23406  * Bootstrap HtmlEditor class
23407
23408  * @constructor
23409  * Create a new HtmlEditor
23410  * @param {Object} config The config object
23411  */
23412
23413 Roo.bootstrap.HtmlEditor = function(config){
23414     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23415     if (!this.toolbars) {
23416         this.toolbars = [];
23417     }
23418     
23419     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23420     this.addEvents({
23421             /**
23422              * @event initialize
23423              * Fires when the editor is fully initialized (including the iframe)
23424              * @param {HtmlEditor} this
23425              */
23426             initialize: true,
23427             /**
23428              * @event activate
23429              * Fires when the editor is first receives the focus. Any insertion must wait
23430              * until after this event.
23431              * @param {HtmlEditor} this
23432              */
23433             activate: true,
23434              /**
23435              * @event beforesync
23436              * Fires before the textarea is updated with content from the editor iframe. Return false
23437              * to cancel the sync.
23438              * @param {HtmlEditor} this
23439              * @param {String} html
23440              */
23441             beforesync: true,
23442              /**
23443              * @event beforepush
23444              * Fires before the iframe editor is updated with content from the textarea. Return false
23445              * to cancel the push.
23446              * @param {HtmlEditor} this
23447              * @param {String} html
23448              */
23449             beforepush: true,
23450              /**
23451              * @event sync
23452              * Fires when the textarea is updated with content from the editor iframe.
23453              * @param {HtmlEditor} this
23454              * @param {String} html
23455              */
23456             sync: true,
23457              /**
23458              * @event push
23459              * Fires when the iframe editor is updated with content from the textarea.
23460              * @param {HtmlEditor} this
23461              * @param {String} html
23462              */
23463             push: true,
23464              /**
23465              * @event editmodechange
23466              * Fires when the editor switches edit modes
23467              * @param {HtmlEditor} this
23468              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23469              */
23470             editmodechange: true,
23471             /**
23472              * @event editorevent
23473              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23474              * @param {HtmlEditor} this
23475              */
23476             editorevent: true,
23477             /**
23478              * @event firstfocus
23479              * Fires when on first focus - needed by toolbars..
23480              * @param {HtmlEditor} this
23481              */
23482             firstfocus: true,
23483             /**
23484              * @event autosave
23485              * Auto save the htmlEditor value as a file into Events
23486              * @param {HtmlEditor} this
23487              */
23488             autosave: true,
23489             /**
23490              * @event savedpreview
23491              * preview the saved version of htmlEditor
23492              * @param {HtmlEditor} this
23493              */
23494             savedpreview: true
23495         });
23496 };
23497
23498
23499 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23500     
23501     
23502       /**
23503      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23504      */
23505     toolbars : false,
23506     
23507      /**
23508     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23509     */
23510     btns : [],
23511    
23512      /**
23513      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23514      *                        Roo.resizable.
23515      */
23516     resizable : false,
23517      /**
23518      * @cfg {Number} height (in pixels)
23519      */   
23520     height: 300,
23521    /**
23522      * @cfg {Number} width (in pixels)
23523      */   
23524     width: false,
23525     
23526     /**
23527      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23528      * 
23529      */
23530     stylesheets: false,
23531     
23532     // id of frame..
23533     frameId: false,
23534     
23535     // private properties
23536     validationEvent : false,
23537     deferHeight: true,
23538     initialized : false,
23539     activated : false,
23540     
23541     onFocus : Roo.emptyFn,
23542     iframePad:3,
23543     hideMode:'offsets',
23544     
23545     tbContainer : false,
23546     
23547     bodyCls : '',
23548     
23549     toolbarContainer :function() {
23550         return this.wrap.select('.x-html-editor-tb',true).first();
23551     },
23552
23553     /**
23554      * Protected method that will not generally be called directly. It
23555      * is called when the editor creates its toolbar. Override this method if you need to
23556      * add custom toolbar buttons.
23557      * @param {HtmlEditor} editor
23558      */
23559     createToolbar : function(){
23560         Roo.log('renewing');
23561         Roo.log("create toolbars");
23562         
23563         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23564         this.toolbars[0].render(this.toolbarContainer());
23565         
23566         return;
23567         
23568 //        if (!editor.toolbars || !editor.toolbars.length) {
23569 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23570 //        }
23571 //        
23572 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23573 //            editor.toolbars[i] = Roo.factory(
23574 //                    typeof(editor.toolbars[i]) == 'string' ?
23575 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23576 //                Roo.bootstrap.HtmlEditor);
23577 //            editor.toolbars[i].init(editor);
23578 //        }
23579     },
23580
23581      
23582     // private
23583     onRender : function(ct, position)
23584     {
23585        // Roo.log("Call onRender: " + this.xtype);
23586         var _t = this;
23587         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23588       
23589         this.wrap = this.inputEl().wrap({
23590             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23591         });
23592         
23593         this.editorcore.onRender(ct, position);
23594          
23595         if (this.resizable) {
23596             this.resizeEl = new Roo.Resizable(this.wrap, {
23597                 pinned : true,
23598                 wrap: true,
23599                 dynamic : true,
23600                 minHeight : this.height,
23601                 height: this.height,
23602                 handles : this.resizable,
23603                 width: this.width,
23604                 listeners : {
23605                     resize : function(r, w, h) {
23606                         _t.onResize(w,h); // -something
23607                     }
23608                 }
23609             });
23610             
23611         }
23612         this.createToolbar(this);
23613        
23614         
23615         if(!this.width && this.resizable){
23616             this.setSize(this.wrap.getSize());
23617         }
23618         if (this.resizeEl) {
23619             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23620             // should trigger onReize..
23621         }
23622         
23623     },
23624
23625     // private
23626     onResize : function(w, h)
23627     {
23628         Roo.log('resize: ' +w + ',' + h );
23629         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23630         var ew = false;
23631         var eh = false;
23632         
23633         if(this.inputEl() ){
23634             if(typeof w == 'number'){
23635                 var aw = w - this.wrap.getFrameWidth('lr');
23636                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23637                 ew = aw;
23638             }
23639             if(typeof h == 'number'){
23640                  var tbh = -11;  // fixme it needs to tool bar size!
23641                 for (var i =0; i < this.toolbars.length;i++) {
23642                     // fixme - ask toolbars for heights?
23643                     tbh += this.toolbars[i].el.getHeight();
23644                     //if (this.toolbars[i].footer) {
23645                     //    tbh += this.toolbars[i].footer.el.getHeight();
23646                     //}
23647                 }
23648               
23649                 
23650                 
23651                 
23652                 
23653                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23654                 ah -= 5; // knock a few pixes off for look..
23655                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23656                 var eh = ah;
23657             }
23658         }
23659         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23660         this.editorcore.onResize(ew,eh);
23661         
23662     },
23663
23664     /**
23665      * Toggles the editor between standard and source edit mode.
23666      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23667      */
23668     toggleSourceEdit : function(sourceEditMode)
23669     {
23670         this.editorcore.toggleSourceEdit(sourceEditMode);
23671         
23672         if(this.editorcore.sourceEditMode){
23673             Roo.log('editor - showing textarea');
23674             
23675 //            Roo.log('in');
23676 //            Roo.log(this.syncValue());
23677             this.syncValue();
23678             this.inputEl().removeClass(['hide', 'x-hidden']);
23679             this.inputEl().dom.removeAttribute('tabIndex');
23680             this.inputEl().focus();
23681         }else{
23682             Roo.log('editor - hiding textarea');
23683 //            Roo.log('out')
23684 //            Roo.log(this.pushValue()); 
23685             this.pushValue();
23686             
23687             this.inputEl().addClass(['hide', 'x-hidden']);
23688             this.inputEl().dom.setAttribute('tabIndex', -1);
23689             //this.deferFocus();
23690         }
23691          
23692         if(this.resizable){
23693             this.setSize(this.wrap.getSize());
23694         }
23695         
23696         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23697     },
23698  
23699     // private (for BoxComponent)
23700     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23701
23702     // private (for BoxComponent)
23703     getResizeEl : function(){
23704         return this.wrap;
23705     },
23706
23707     // private (for BoxComponent)
23708     getPositionEl : function(){
23709         return this.wrap;
23710     },
23711
23712     // private
23713     initEvents : function(){
23714         this.originalValue = this.getValue();
23715     },
23716
23717 //    /**
23718 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23719 //     * @method
23720 //     */
23721 //    markInvalid : Roo.emptyFn,
23722 //    /**
23723 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23724 //     * @method
23725 //     */
23726 //    clearInvalid : Roo.emptyFn,
23727
23728     setValue : function(v){
23729         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23730         this.editorcore.pushValue();
23731     },
23732
23733      
23734     // private
23735     deferFocus : function(){
23736         this.focus.defer(10, this);
23737     },
23738
23739     // doc'ed in Field
23740     focus : function(){
23741         this.editorcore.focus();
23742         
23743     },
23744       
23745
23746     // private
23747     onDestroy : function(){
23748         
23749         
23750         
23751         if(this.rendered){
23752             
23753             for (var i =0; i < this.toolbars.length;i++) {
23754                 // fixme - ask toolbars for heights?
23755                 this.toolbars[i].onDestroy();
23756             }
23757             
23758             this.wrap.dom.innerHTML = '';
23759             this.wrap.remove();
23760         }
23761     },
23762
23763     // private
23764     onFirstFocus : function(){
23765         //Roo.log("onFirstFocus");
23766         this.editorcore.onFirstFocus();
23767          for (var i =0; i < this.toolbars.length;i++) {
23768             this.toolbars[i].onFirstFocus();
23769         }
23770         
23771     },
23772     
23773     // private
23774     syncValue : function()
23775     {   
23776         this.editorcore.syncValue();
23777     },
23778     
23779     pushValue : function()
23780     {   
23781         this.editorcore.pushValue();
23782     }
23783      
23784     
23785     // hide stuff that is not compatible
23786     /**
23787      * @event blur
23788      * @hide
23789      */
23790     /**
23791      * @event change
23792      * @hide
23793      */
23794     /**
23795      * @event focus
23796      * @hide
23797      */
23798     /**
23799      * @event specialkey
23800      * @hide
23801      */
23802     /**
23803      * @cfg {String} fieldClass @hide
23804      */
23805     /**
23806      * @cfg {String} focusClass @hide
23807      */
23808     /**
23809      * @cfg {String} autoCreate @hide
23810      */
23811     /**
23812      * @cfg {String} inputType @hide
23813      */
23814     /**
23815      * @cfg {String} invalidClass @hide
23816      */
23817     /**
23818      * @cfg {String} invalidText @hide
23819      */
23820     /**
23821      * @cfg {String} msgFx @hide
23822      */
23823     /**
23824      * @cfg {String} validateOnBlur @hide
23825      */
23826 });
23827  
23828     
23829    
23830    
23831    
23832       
23833 Roo.namespace('Roo.bootstrap.htmleditor');
23834 /**
23835  * @class Roo.bootstrap.HtmlEditorToolbar1
23836  * Basic Toolbar
23837  * 
23838  * Usage:
23839  *
23840  new Roo.bootstrap.HtmlEditor({
23841     ....
23842     toolbars : [
23843         new Roo.bootstrap.HtmlEditorToolbar1({
23844             disable : { fonts: 1 , format: 1, ..., ... , ...],
23845             btns : [ .... ]
23846         })
23847     }
23848      
23849  * 
23850  * @cfg {Object} disable List of elements to disable..
23851  * @cfg {Array} btns List of additional buttons.
23852  * 
23853  * 
23854  * NEEDS Extra CSS? 
23855  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23856  */
23857  
23858 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23859 {
23860     
23861     Roo.apply(this, config);
23862     
23863     // default disabled, based on 'good practice'..
23864     this.disable = this.disable || {};
23865     Roo.applyIf(this.disable, {
23866         fontSize : true,
23867         colors : true,
23868         specialElements : true
23869     });
23870     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23871     
23872     this.editor = config.editor;
23873     this.editorcore = config.editor.editorcore;
23874     
23875     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23876     
23877     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23878     // dont call parent... till later.
23879 }
23880 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23881      
23882     bar : true,
23883     
23884     editor : false,
23885     editorcore : false,
23886     
23887     
23888     formats : [
23889         "p" ,  
23890         "h1","h2","h3","h4","h5","h6", 
23891         "pre", "code", 
23892         "abbr", "acronym", "address", "cite", "samp", "var",
23893         'div','span'
23894     ],
23895     
23896     onRender : function(ct, position)
23897     {
23898        // Roo.log("Call onRender: " + this.xtype);
23899         
23900        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23901        Roo.log(this.el);
23902        this.el.dom.style.marginBottom = '0';
23903        var _this = this;
23904        var editorcore = this.editorcore;
23905        var editor= this.editor;
23906        
23907        var children = [];
23908        var btn = function(id,cmd , toggle, handler, html){
23909        
23910             var  event = toggle ? 'toggle' : 'click';
23911        
23912             var a = {
23913                 size : 'sm',
23914                 xtype: 'Button',
23915                 xns: Roo.bootstrap,
23916                 glyphicon : id,
23917                 cmd : id || cmd,
23918                 enableToggle:toggle !== false,
23919                 html : html || '',
23920                 pressed : toggle ? false : null,
23921                 listeners : {}
23922             };
23923             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23924                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23925             };
23926             children.push(a);
23927             return a;
23928        }
23929        
23930     //    var cb_box = function...
23931         
23932         var style = {
23933                 xtype: 'Button',
23934                 size : 'sm',
23935                 xns: Roo.bootstrap,
23936                 glyphicon : 'font',
23937                 //html : 'submit'
23938                 menu : {
23939                     xtype: 'Menu',
23940                     xns: Roo.bootstrap,
23941                     items:  []
23942                 }
23943         };
23944         Roo.each(this.formats, function(f) {
23945             style.menu.items.push({
23946                 xtype :'MenuItem',
23947                 xns: Roo.bootstrap,
23948                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23949                 tagname : f,
23950                 listeners : {
23951                     click : function()
23952                     {
23953                         editorcore.insertTag(this.tagname);
23954                         editor.focus();
23955                     }
23956                 }
23957                 
23958             });
23959         });
23960         children.push(style);   
23961         
23962         btn('bold',false,true);
23963         btn('italic',false,true);
23964         btn('align-left', 'justifyleft',true);
23965         btn('align-center', 'justifycenter',true);
23966         btn('align-right' , 'justifyright',true);
23967         btn('link', false, false, function(btn) {
23968             //Roo.log("create link?");
23969             var url = prompt(this.createLinkText, this.defaultLinkValue);
23970             if(url && url != 'http:/'+'/'){
23971                 this.editorcore.relayCmd('createlink', url);
23972             }
23973         }),
23974         btn('list','insertunorderedlist',true);
23975         btn('pencil', false,true, function(btn){
23976                 Roo.log(this);
23977                 this.toggleSourceEdit(btn.pressed);
23978         });
23979         
23980         if (this.editor.btns.length > 0) {
23981             for (var i = 0; i<this.editor.btns.length; i++) {
23982                 children.push(this.editor.btns[i]);
23983             }
23984         }
23985         
23986         /*
23987         var cog = {
23988                 xtype: 'Button',
23989                 size : 'sm',
23990                 xns: Roo.bootstrap,
23991                 glyphicon : 'cog',
23992                 //html : 'submit'
23993                 menu : {
23994                     xtype: 'Menu',
23995                     xns: Roo.bootstrap,
23996                     items:  []
23997                 }
23998         };
23999         
24000         cog.menu.items.push({
24001             xtype :'MenuItem',
24002             xns: Roo.bootstrap,
24003             html : Clean styles,
24004             tagname : f,
24005             listeners : {
24006                 click : function()
24007                 {
24008                     editorcore.insertTag(this.tagname);
24009                     editor.focus();
24010                 }
24011             }
24012             
24013         });
24014        */
24015         
24016          
24017        this.xtype = 'NavSimplebar';
24018         
24019         for(var i=0;i< children.length;i++) {
24020             
24021             this.buttons.add(this.addxtypeChild(children[i]));
24022             
24023         }
24024         
24025         editor.on('editorevent', this.updateToolbar, this);
24026     },
24027     onBtnClick : function(id)
24028     {
24029        this.editorcore.relayCmd(id);
24030        this.editorcore.focus();
24031     },
24032     
24033     /**
24034      * Protected method that will not generally be called directly. It triggers
24035      * a toolbar update by reading the markup state of the current selection in the editor.
24036      */
24037     updateToolbar: function(){
24038
24039         if(!this.editorcore.activated){
24040             this.editor.onFirstFocus(); // is this neeed?
24041             return;
24042         }
24043
24044         var btns = this.buttons; 
24045         var doc = this.editorcore.doc;
24046         btns.get('bold').setActive(doc.queryCommandState('bold'));
24047         btns.get('italic').setActive(doc.queryCommandState('italic'));
24048         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24049         
24050         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24051         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24052         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24053         
24054         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24055         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24056          /*
24057         
24058         var ans = this.editorcore.getAllAncestors();
24059         if (this.formatCombo) {
24060             
24061             
24062             var store = this.formatCombo.store;
24063             this.formatCombo.setValue("");
24064             for (var i =0; i < ans.length;i++) {
24065                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24066                     // select it..
24067                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24068                     break;
24069                 }
24070             }
24071         }
24072         
24073         
24074         
24075         // hides menus... - so this cant be on a menu...
24076         Roo.bootstrap.MenuMgr.hideAll();
24077         */
24078         Roo.bootstrap.MenuMgr.hideAll();
24079         //this.editorsyncValue();
24080     },
24081     onFirstFocus: function() {
24082         this.buttons.each(function(item){
24083            item.enable();
24084         });
24085     },
24086     toggleSourceEdit : function(sourceEditMode){
24087         
24088           
24089         if(sourceEditMode){
24090             Roo.log("disabling buttons");
24091            this.buttons.each( function(item){
24092                 if(item.cmd != 'pencil'){
24093                     item.disable();
24094                 }
24095             });
24096           
24097         }else{
24098             Roo.log("enabling buttons");
24099             if(this.editorcore.initialized){
24100                 this.buttons.each( function(item){
24101                     item.enable();
24102                 });
24103             }
24104             
24105         }
24106         Roo.log("calling toggole on editor");
24107         // tell the editor that it's been pressed..
24108         this.editor.toggleSourceEdit(sourceEditMode);
24109        
24110     }
24111 });
24112
24113
24114
24115
24116
24117 /**
24118  * @class Roo.bootstrap.Table.AbstractSelectionModel
24119  * @extends Roo.util.Observable
24120  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24121  * implemented by descendant classes.  This class should not be directly instantiated.
24122  * @constructor
24123  */
24124 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24125     this.locked = false;
24126     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24127 };
24128
24129
24130 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24131     /** @ignore Called by the grid automatically. Do not call directly. */
24132     init : function(grid){
24133         this.grid = grid;
24134         this.initEvents();
24135     },
24136
24137     /**
24138      * Locks the selections.
24139      */
24140     lock : function(){
24141         this.locked = true;
24142     },
24143
24144     /**
24145      * Unlocks the selections.
24146      */
24147     unlock : function(){
24148         this.locked = false;
24149     },
24150
24151     /**
24152      * Returns true if the selections are locked.
24153      * @return {Boolean}
24154      */
24155     isLocked : function(){
24156         return this.locked;
24157     }
24158 });
24159 /**
24160  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24161  * @class Roo.bootstrap.Table.RowSelectionModel
24162  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24163  * It supports multiple selections and keyboard selection/navigation. 
24164  * @constructor
24165  * @param {Object} config
24166  */
24167
24168 Roo.bootstrap.Table.RowSelectionModel = function(config){
24169     Roo.apply(this, config);
24170     this.selections = new Roo.util.MixedCollection(false, function(o){
24171         return o.id;
24172     });
24173
24174     this.last = false;
24175     this.lastActive = false;
24176
24177     this.addEvents({
24178         /**
24179              * @event selectionchange
24180              * Fires when the selection changes
24181              * @param {SelectionModel} this
24182              */
24183             "selectionchange" : true,
24184         /**
24185              * @event afterselectionchange
24186              * Fires after the selection changes (eg. by key press or clicking)
24187              * @param {SelectionModel} this
24188              */
24189             "afterselectionchange" : true,
24190         /**
24191              * @event beforerowselect
24192              * Fires when a row is selected being selected, return false to cancel.
24193              * @param {SelectionModel} this
24194              * @param {Number} rowIndex The selected index
24195              * @param {Boolean} keepExisting False if other selections will be cleared
24196              */
24197             "beforerowselect" : true,
24198         /**
24199              * @event rowselect
24200              * Fires when a row is selected.
24201              * @param {SelectionModel} this
24202              * @param {Number} rowIndex The selected index
24203              * @param {Roo.data.Record} r The record
24204              */
24205             "rowselect" : true,
24206         /**
24207              * @event rowdeselect
24208              * Fires when a row is deselected.
24209              * @param {SelectionModel} this
24210              * @param {Number} rowIndex The selected index
24211              */
24212         "rowdeselect" : true
24213     });
24214     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24215     this.locked = false;
24216  };
24217
24218 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24219     /**
24220      * @cfg {Boolean} singleSelect
24221      * True to allow selection of only one row at a time (defaults to false)
24222      */
24223     singleSelect : false,
24224
24225     // private
24226     initEvents : function()
24227     {
24228
24229         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24230         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24231         //}else{ // allow click to work like normal
24232          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24233         //}
24234         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24235         this.grid.on("rowclick", this.handleMouseDown, this);
24236         
24237         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24238             "up" : function(e){
24239                 if(!e.shiftKey){
24240                     this.selectPrevious(e.shiftKey);
24241                 }else if(this.last !== false && this.lastActive !== false){
24242                     var last = this.last;
24243                     this.selectRange(this.last,  this.lastActive-1);
24244                     this.grid.getView().focusRow(this.lastActive);
24245                     if(last !== false){
24246                         this.last = last;
24247                     }
24248                 }else{
24249                     this.selectFirstRow();
24250                 }
24251                 this.fireEvent("afterselectionchange", this);
24252             },
24253             "down" : function(e){
24254                 if(!e.shiftKey){
24255                     this.selectNext(e.shiftKey);
24256                 }else if(this.last !== false && this.lastActive !== false){
24257                     var last = this.last;
24258                     this.selectRange(this.last,  this.lastActive+1);
24259                     this.grid.getView().focusRow(this.lastActive);
24260                     if(last !== false){
24261                         this.last = last;
24262                     }
24263                 }else{
24264                     this.selectFirstRow();
24265                 }
24266                 this.fireEvent("afterselectionchange", this);
24267             },
24268             scope: this
24269         });
24270         this.grid.store.on('load', function(){
24271             this.selections.clear();
24272         },this);
24273         /*
24274         var view = this.grid.view;
24275         view.on("refresh", this.onRefresh, this);
24276         view.on("rowupdated", this.onRowUpdated, this);
24277         view.on("rowremoved", this.onRemove, this);
24278         */
24279     },
24280
24281     // private
24282     onRefresh : function()
24283     {
24284         var ds = this.grid.store, i, v = this.grid.view;
24285         var s = this.selections;
24286         s.each(function(r){
24287             if((i = ds.indexOfId(r.id)) != -1){
24288                 v.onRowSelect(i);
24289             }else{
24290                 s.remove(r);
24291             }
24292         });
24293     },
24294
24295     // private
24296     onRemove : function(v, index, r){
24297         this.selections.remove(r);
24298     },
24299
24300     // private
24301     onRowUpdated : function(v, index, r){
24302         if(this.isSelected(r)){
24303             v.onRowSelect(index);
24304         }
24305     },
24306
24307     /**
24308      * Select records.
24309      * @param {Array} records The records to select
24310      * @param {Boolean} keepExisting (optional) True to keep existing selections
24311      */
24312     selectRecords : function(records, keepExisting)
24313     {
24314         if(!keepExisting){
24315             this.clearSelections();
24316         }
24317             var ds = this.grid.store;
24318         for(var i = 0, len = records.length; i < len; i++){
24319             this.selectRow(ds.indexOf(records[i]), true);
24320         }
24321     },
24322
24323     /**
24324      * Gets the number of selected rows.
24325      * @return {Number}
24326      */
24327     getCount : function(){
24328         return this.selections.length;
24329     },
24330
24331     /**
24332      * Selects the first row in the grid.
24333      */
24334     selectFirstRow : function(){
24335         this.selectRow(0);
24336     },
24337
24338     /**
24339      * Select the last row.
24340      * @param {Boolean} keepExisting (optional) True to keep existing selections
24341      */
24342     selectLastRow : function(keepExisting){
24343         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24344         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24345     },
24346
24347     /**
24348      * Selects the row immediately following the last selected row.
24349      * @param {Boolean} keepExisting (optional) True to keep existing selections
24350      */
24351     selectNext : function(keepExisting)
24352     {
24353             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24354             this.selectRow(this.last+1, keepExisting);
24355             this.grid.getView().focusRow(this.last);
24356         }
24357     },
24358
24359     /**
24360      * Selects the row that precedes the last selected row.
24361      * @param {Boolean} keepExisting (optional) True to keep existing selections
24362      */
24363     selectPrevious : function(keepExisting){
24364         if(this.last){
24365             this.selectRow(this.last-1, keepExisting);
24366             this.grid.getView().focusRow(this.last);
24367         }
24368     },
24369
24370     /**
24371      * Returns the selected records
24372      * @return {Array} Array of selected records
24373      */
24374     getSelections : function(){
24375         return [].concat(this.selections.items);
24376     },
24377
24378     /**
24379      * Returns the first selected record.
24380      * @return {Record}
24381      */
24382     getSelected : function(){
24383         return this.selections.itemAt(0);
24384     },
24385
24386
24387     /**
24388      * Clears all selections.
24389      */
24390     clearSelections : function(fast)
24391     {
24392         if(this.locked) {
24393             return;
24394         }
24395         if(fast !== true){
24396                 var ds = this.grid.store;
24397             var s = this.selections;
24398             s.each(function(r){
24399                 this.deselectRow(ds.indexOfId(r.id));
24400             }, this);
24401             s.clear();
24402         }else{
24403             this.selections.clear();
24404         }
24405         this.last = false;
24406     },
24407
24408
24409     /**
24410      * Selects all rows.
24411      */
24412     selectAll : function(){
24413         if(this.locked) {
24414             return;
24415         }
24416         this.selections.clear();
24417         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24418             this.selectRow(i, true);
24419         }
24420     },
24421
24422     /**
24423      * Returns True if there is a selection.
24424      * @return {Boolean}
24425      */
24426     hasSelection : function(){
24427         return this.selections.length > 0;
24428     },
24429
24430     /**
24431      * Returns True if the specified row is selected.
24432      * @param {Number/Record} record The record or index of the record to check
24433      * @return {Boolean}
24434      */
24435     isSelected : function(index){
24436             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24437         return (r && this.selections.key(r.id) ? true : false);
24438     },
24439
24440     /**
24441      * Returns True if the specified record id is selected.
24442      * @param {String} id The id of record to check
24443      * @return {Boolean}
24444      */
24445     isIdSelected : function(id){
24446         return (this.selections.key(id) ? true : false);
24447     },
24448
24449
24450     // private
24451     handleMouseDBClick : function(e, t){
24452         
24453     },
24454     // private
24455     handleMouseDown : function(e, t)
24456     {
24457             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24458         if(this.isLocked() || rowIndex < 0 ){
24459             return;
24460         };
24461         if(e.shiftKey && this.last !== false){
24462             var last = this.last;
24463             this.selectRange(last, rowIndex, e.ctrlKey);
24464             this.last = last; // reset the last
24465             t.focus();
24466     
24467         }else{
24468             var isSelected = this.isSelected(rowIndex);
24469             //Roo.log("select row:" + rowIndex);
24470             if(isSelected){
24471                 this.deselectRow(rowIndex);
24472             } else {
24473                         this.selectRow(rowIndex, true);
24474             }
24475     
24476             /*
24477                 if(e.button !== 0 && isSelected){
24478                 alert('rowIndex 2: ' + rowIndex);
24479                     view.focusRow(rowIndex);
24480                 }else if(e.ctrlKey && isSelected){
24481                     this.deselectRow(rowIndex);
24482                 }else if(!isSelected){
24483                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24484                     view.focusRow(rowIndex);
24485                 }
24486             */
24487         }
24488         this.fireEvent("afterselectionchange", this);
24489     },
24490     // private
24491     handleDragableRowClick :  function(grid, rowIndex, e) 
24492     {
24493         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24494             this.selectRow(rowIndex, false);
24495             grid.view.focusRow(rowIndex);
24496              this.fireEvent("afterselectionchange", this);
24497         }
24498     },
24499     
24500     /**
24501      * Selects multiple rows.
24502      * @param {Array} rows Array of the indexes of the row to select
24503      * @param {Boolean} keepExisting (optional) True to keep existing selections
24504      */
24505     selectRows : function(rows, keepExisting){
24506         if(!keepExisting){
24507             this.clearSelections();
24508         }
24509         for(var i = 0, len = rows.length; i < len; i++){
24510             this.selectRow(rows[i], true);
24511         }
24512     },
24513
24514     /**
24515      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24516      * @param {Number} startRow The index of the first row in the range
24517      * @param {Number} endRow The index of the last row in the range
24518      * @param {Boolean} keepExisting (optional) True to retain existing selections
24519      */
24520     selectRange : function(startRow, endRow, keepExisting){
24521         if(this.locked) {
24522             return;
24523         }
24524         if(!keepExisting){
24525             this.clearSelections();
24526         }
24527         if(startRow <= endRow){
24528             for(var i = startRow; i <= endRow; i++){
24529                 this.selectRow(i, true);
24530             }
24531         }else{
24532             for(var i = startRow; i >= endRow; i--){
24533                 this.selectRow(i, true);
24534             }
24535         }
24536     },
24537
24538     /**
24539      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24540      * @param {Number} startRow The index of the first row in the range
24541      * @param {Number} endRow The index of the last row in the range
24542      */
24543     deselectRange : function(startRow, endRow, preventViewNotify){
24544         if(this.locked) {
24545             return;
24546         }
24547         for(var i = startRow; i <= endRow; i++){
24548             this.deselectRow(i, preventViewNotify);
24549         }
24550     },
24551
24552     /**
24553      * Selects a row.
24554      * @param {Number} row The index of the row to select
24555      * @param {Boolean} keepExisting (optional) True to keep existing selections
24556      */
24557     selectRow : function(index, keepExisting, preventViewNotify)
24558     {
24559             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24560             return;
24561         }
24562         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24563             if(!keepExisting || this.singleSelect){
24564                 this.clearSelections();
24565             }
24566             
24567             var r = this.grid.store.getAt(index);
24568             //console.log('selectRow - record id :' + r.id);
24569             
24570             this.selections.add(r);
24571             this.last = this.lastActive = index;
24572             if(!preventViewNotify){
24573                 var proxy = new Roo.Element(
24574                                 this.grid.getRowDom(index)
24575                 );
24576                 proxy.addClass('bg-info info');
24577             }
24578             this.fireEvent("rowselect", this, index, r);
24579             this.fireEvent("selectionchange", this);
24580         }
24581     },
24582
24583     /**
24584      * Deselects a row.
24585      * @param {Number} row The index of the row to deselect
24586      */
24587     deselectRow : function(index, preventViewNotify)
24588     {
24589         if(this.locked) {
24590             return;
24591         }
24592         if(this.last == index){
24593             this.last = false;
24594         }
24595         if(this.lastActive == index){
24596             this.lastActive = false;
24597         }
24598         
24599         var r = this.grid.store.getAt(index);
24600         if (!r) {
24601             return;
24602         }
24603         
24604         this.selections.remove(r);
24605         //.console.log('deselectRow - record id :' + r.id);
24606         if(!preventViewNotify){
24607         
24608             var proxy = new Roo.Element(
24609                 this.grid.getRowDom(index)
24610             );
24611             proxy.removeClass('bg-info info');
24612         }
24613         this.fireEvent("rowdeselect", this, index);
24614         this.fireEvent("selectionchange", this);
24615     },
24616
24617     // private
24618     restoreLast : function(){
24619         if(this._last){
24620             this.last = this._last;
24621         }
24622     },
24623
24624     // private
24625     acceptsNav : function(row, col, cm){
24626         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24627     },
24628
24629     // private
24630     onEditorKey : function(field, e){
24631         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24632         if(k == e.TAB){
24633             e.stopEvent();
24634             ed.completeEdit();
24635             if(e.shiftKey){
24636                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24637             }else{
24638                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24639             }
24640         }else if(k == e.ENTER && !e.ctrlKey){
24641             e.stopEvent();
24642             ed.completeEdit();
24643             if(e.shiftKey){
24644                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24645             }else{
24646                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24647             }
24648         }else if(k == e.ESC){
24649             ed.cancelEdit();
24650         }
24651         if(newCell){
24652             g.startEditing(newCell[0], newCell[1]);
24653         }
24654     }
24655 });
24656 /*
24657  * Based on:
24658  * Ext JS Library 1.1.1
24659  * Copyright(c) 2006-2007, Ext JS, LLC.
24660  *
24661  * Originally Released Under LGPL - original licence link has changed is not relivant.
24662  *
24663  * Fork - LGPL
24664  * <script type="text/javascript">
24665  */
24666  
24667 /**
24668  * @class Roo.bootstrap.PagingToolbar
24669  * @extends Roo.bootstrap.NavSimplebar
24670  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24671  * @constructor
24672  * Create a new PagingToolbar
24673  * @param {Object} config The config object
24674  * @param {Roo.data.Store} store
24675  */
24676 Roo.bootstrap.PagingToolbar = function(config)
24677 {
24678     // old args format still supported... - xtype is prefered..
24679         // created from xtype...
24680     
24681     this.ds = config.dataSource;
24682     
24683     if (config.store && !this.ds) {
24684         this.store= Roo.factory(config.store, Roo.data);
24685         this.ds = this.store;
24686         this.ds.xmodule = this.xmodule || false;
24687     }
24688     
24689     this.toolbarItems = [];
24690     if (config.items) {
24691         this.toolbarItems = config.items;
24692     }
24693     
24694     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24695     
24696     this.cursor = 0;
24697     
24698     if (this.ds) { 
24699         this.bind(this.ds);
24700     }
24701     
24702     if (Roo.bootstrap.version == 4) {
24703         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24704     } else {
24705         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24706     }
24707     
24708 };
24709
24710 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24711     /**
24712      * @cfg {Roo.data.Store} dataSource
24713      * The underlying data store providing the paged data
24714      */
24715     /**
24716      * @cfg {String/HTMLElement/Element} container
24717      * container The id or element that will contain the toolbar
24718      */
24719     /**
24720      * @cfg {Boolean} displayInfo
24721      * True to display the displayMsg (defaults to false)
24722      */
24723     /**
24724      * @cfg {Number} pageSize
24725      * The number of records to display per page (defaults to 20)
24726      */
24727     pageSize: 20,
24728     /**
24729      * @cfg {String} displayMsg
24730      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24731      */
24732     displayMsg : 'Displaying {0} - {1} of {2}',
24733     /**
24734      * @cfg {String} emptyMsg
24735      * The message to display when no records are found (defaults to "No data to display")
24736      */
24737     emptyMsg : 'No data to display',
24738     /**
24739      * Customizable piece of the default paging text (defaults to "Page")
24740      * @type String
24741      */
24742     beforePageText : "Page",
24743     /**
24744      * Customizable piece of the default paging text (defaults to "of %0")
24745      * @type String
24746      */
24747     afterPageText : "of {0}",
24748     /**
24749      * Customizable piece of the default paging text (defaults to "First Page")
24750      * @type String
24751      */
24752     firstText : "First Page",
24753     /**
24754      * Customizable piece of the default paging text (defaults to "Previous Page")
24755      * @type String
24756      */
24757     prevText : "Previous Page",
24758     /**
24759      * Customizable piece of the default paging text (defaults to "Next Page")
24760      * @type String
24761      */
24762     nextText : "Next Page",
24763     /**
24764      * Customizable piece of the default paging text (defaults to "Last Page")
24765      * @type String
24766      */
24767     lastText : "Last Page",
24768     /**
24769      * Customizable piece of the default paging text (defaults to "Refresh")
24770      * @type String
24771      */
24772     refreshText : "Refresh",
24773
24774     buttons : false,
24775     // private
24776     onRender : function(ct, position) 
24777     {
24778         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24779         this.navgroup.parentId = this.id;
24780         this.navgroup.onRender(this.el, null);
24781         // add the buttons to the navgroup
24782         
24783         if(this.displayInfo){
24784             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24785             this.displayEl = this.el.select('.x-paging-info', true).first();
24786 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24787 //            this.displayEl = navel.el.select('span',true).first();
24788         }
24789         
24790         var _this = this;
24791         
24792         if(this.buttons){
24793             Roo.each(_this.buttons, function(e){ // this might need to use render????
24794                Roo.factory(e).render(_this.el);
24795             });
24796         }
24797             
24798         Roo.each(_this.toolbarItems, function(e) {
24799             _this.navgroup.addItem(e);
24800         });
24801         
24802         
24803         this.first = this.navgroup.addItem({
24804             tooltip: this.firstText,
24805             cls: "prev btn-outline-secondary",
24806             html : ' <i class="fa fa-step-backward"></i>',
24807             disabled: true,
24808             preventDefault: true,
24809             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24810         });
24811         
24812         this.prev =  this.navgroup.addItem({
24813             tooltip: this.prevText,
24814             cls: "prev btn-outline-secondary",
24815             html : ' <i class="fa fa-backward"></i>',
24816             disabled: true,
24817             preventDefault: true,
24818             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24819         });
24820     //this.addSeparator();
24821         
24822         
24823         var field = this.navgroup.addItem( {
24824             tagtype : 'span',
24825             cls : 'x-paging-position  btn-outline-secondary',
24826              disabled: true,
24827             html : this.beforePageText  +
24828                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24829                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24830          } ); //?? escaped?
24831         
24832         this.field = field.el.select('input', true).first();
24833         this.field.on("keydown", this.onPagingKeydown, this);
24834         this.field.on("focus", function(){this.dom.select();});
24835     
24836     
24837         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24838         //this.field.setHeight(18);
24839         //this.addSeparator();
24840         this.next = this.navgroup.addItem({
24841             tooltip: this.nextText,
24842             cls: "next btn-outline-secondary",
24843             html : ' <i class="fa fa-forward"></i>',
24844             disabled: true,
24845             preventDefault: true,
24846             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24847         });
24848         this.last = this.navgroup.addItem({
24849             tooltip: this.lastText,
24850             html : ' <i class="fa fa-step-forward"></i>',
24851             cls: "next btn-outline-secondary",
24852             disabled: true,
24853             preventDefault: true,
24854             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24855         });
24856     //this.addSeparator();
24857         this.loading = this.navgroup.addItem({
24858             tooltip: this.refreshText,
24859             cls: "btn-outline-secondary",
24860             html : ' <i class="fa fa-refresh"></i>',
24861             preventDefault: true,
24862             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24863         });
24864         
24865     },
24866
24867     // private
24868     updateInfo : function(){
24869         if(this.displayEl){
24870             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24871             var msg = count == 0 ?
24872                 this.emptyMsg :
24873                 String.format(
24874                     this.displayMsg,
24875                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24876                 );
24877             this.displayEl.update(msg);
24878         }
24879     },
24880
24881     // private
24882     onLoad : function(ds, r, o)
24883     {
24884         this.cursor = o.params.start ? o.params.start : 0;
24885         
24886         var d = this.getPageData(),
24887             ap = d.activePage,
24888             ps = d.pages;
24889         
24890         
24891         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24892         this.field.dom.value = ap;
24893         this.first.setDisabled(ap == 1);
24894         this.prev.setDisabled(ap == 1);
24895         this.next.setDisabled(ap == ps);
24896         this.last.setDisabled(ap == ps);
24897         this.loading.enable();
24898         this.updateInfo();
24899     },
24900
24901     // private
24902     getPageData : function(){
24903         var total = this.ds.getTotalCount();
24904         return {
24905             total : total,
24906             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24907             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24908         };
24909     },
24910
24911     // private
24912     onLoadError : function(){
24913         this.loading.enable();
24914     },
24915
24916     // private
24917     onPagingKeydown : function(e){
24918         var k = e.getKey();
24919         var d = this.getPageData();
24920         if(k == e.RETURN){
24921             var v = this.field.dom.value, pageNum;
24922             if(!v || isNaN(pageNum = parseInt(v, 10))){
24923                 this.field.dom.value = d.activePage;
24924                 return;
24925             }
24926             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24927             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24928             e.stopEvent();
24929         }
24930         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))
24931         {
24932           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24933           this.field.dom.value = pageNum;
24934           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24935           e.stopEvent();
24936         }
24937         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24938         {
24939           var v = this.field.dom.value, pageNum; 
24940           var increment = (e.shiftKey) ? 10 : 1;
24941           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24942                 increment *= -1;
24943           }
24944           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24945             this.field.dom.value = d.activePage;
24946             return;
24947           }
24948           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24949           {
24950             this.field.dom.value = parseInt(v, 10) + increment;
24951             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24952             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24953           }
24954           e.stopEvent();
24955         }
24956     },
24957
24958     // private
24959     beforeLoad : function(){
24960         if(this.loading){
24961             this.loading.disable();
24962         }
24963     },
24964
24965     // private
24966     onClick : function(which){
24967         
24968         var ds = this.ds;
24969         if (!ds) {
24970             return;
24971         }
24972         
24973         switch(which){
24974             case "first":
24975                 ds.load({params:{start: 0, limit: this.pageSize}});
24976             break;
24977             case "prev":
24978                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24979             break;
24980             case "next":
24981                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24982             break;
24983             case "last":
24984                 var total = ds.getTotalCount();
24985                 var extra = total % this.pageSize;
24986                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24987                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24988             break;
24989             case "refresh":
24990                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24991             break;
24992         }
24993     },
24994
24995     /**
24996      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24997      * @param {Roo.data.Store} store The data store to unbind
24998      */
24999     unbind : function(ds){
25000         ds.un("beforeload", this.beforeLoad, this);
25001         ds.un("load", this.onLoad, this);
25002         ds.un("loadexception", this.onLoadError, this);
25003         ds.un("remove", this.updateInfo, this);
25004         ds.un("add", this.updateInfo, this);
25005         this.ds = undefined;
25006     },
25007
25008     /**
25009      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25010      * @param {Roo.data.Store} store The data store to bind
25011      */
25012     bind : function(ds){
25013         ds.on("beforeload", this.beforeLoad, this);
25014         ds.on("load", this.onLoad, this);
25015         ds.on("loadexception", this.onLoadError, this);
25016         ds.on("remove", this.updateInfo, this);
25017         ds.on("add", this.updateInfo, this);
25018         this.ds = ds;
25019     }
25020 });/*
25021  * - LGPL
25022  *
25023  * element
25024  * 
25025  */
25026
25027 /**
25028  * @class Roo.bootstrap.MessageBar
25029  * @extends Roo.bootstrap.Component
25030  * Bootstrap MessageBar class
25031  * @cfg {String} html contents of the MessageBar
25032  * @cfg {String} weight (info | success | warning | danger) default info
25033  * @cfg {String} beforeClass insert the bar before the given class
25034  * @cfg {Boolean} closable (true | false) default false
25035  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25036  * 
25037  * @constructor
25038  * Create a new Element
25039  * @param {Object} config The config object
25040  */
25041
25042 Roo.bootstrap.MessageBar = function(config){
25043     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25044 };
25045
25046 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25047     
25048     html: '',
25049     weight: 'info',
25050     closable: false,
25051     fixed: false,
25052     beforeClass: 'bootstrap-sticky-wrap',
25053     
25054     getAutoCreate : function(){
25055         
25056         var cfg = {
25057             tag: 'div',
25058             cls: 'alert alert-dismissable alert-' + this.weight,
25059             cn: [
25060                 {
25061                     tag: 'span',
25062                     cls: 'message',
25063                     html: this.html || ''
25064                 }
25065             ]
25066         };
25067         
25068         if(this.fixed){
25069             cfg.cls += ' alert-messages-fixed';
25070         }
25071         
25072         if(this.closable){
25073             cfg.cn.push({
25074                 tag: 'button',
25075                 cls: 'close',
25076                 html: 'x'
25077             });
25078         }
25079         
25080         return cfg;
25081     },
25082     
25083     onRender : function(ct, position)
25084     {
25085         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25086         
25087         if(!this.el){
25088             var cfg = Roo.apply({},  this.getAutoCreate());
25089             cfg.id = Roo.id();
25090             
25091             if (this.cls) {
25092                 cfg.cls += ' ' + this.cls;
25093             }
25094             if (this.style) {
25095                 cfg.style = this.style;
25096             }
25097             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25098             
25099             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25100         }
25101         
25102         this.el.select('>button.close').on('click', this.hide, this);
25103         
25104     },
25105     
25106     show : function()
25107     {
25108         if (!this.rendered) {
25109             this.render();
25110         }
25111         
25112         this.el.show();
25113         
25114         this.fireEvent('show', this);
25115         
25116     },
25117     
25118     hide : function()
25119     {
25120         if (!this.rendered) {
25121             this.render();
25122         }
25123         
25124         this.el.hide();
25125         
25126         this.fireEvent('hide', this);
25127     },
25128     
25129     update : function()
25130     {
25131 //        var e = this.el.dom.firstChild;
25132 //        
25133 //        if(this.closable){
25134 //            e = e.nextSibling;
25135 //        }
25136 //        
25137 //        e.data = this.html || '';
25138
25139         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25140     }
25141    
25142 });
25143
25144  
25145
25146      /*
25147  * - LGPL
25148  *
25149  * Graph
25150  * 
25151  */
25152
25153
25154 /**
25155  * @class Roo.bootstrap.Graph
25156  * @extends Roo.bootstrap.Component
25157  * Bootstrap Graph class
25158 > Prameters
25159  -sm {number} sm 4
25160  -md {number} md 5
25161  @cfg {String} graphtype  bar | vbar | pie
25162  @cfg {number} g_x coodinator | centre x (pie)
25163  @cfg {number} g_y coodinator | centre y (pie)
25164  @cfg {number} g_r radius (pie)
25165  @cfg {number} g_height height of the chart (respected by all elements in the set)
25166  @cfg {number} g_width width of the chart (respected by all elements in the set)
25167  @cfg {Object} title The title of the chart
25168     
25169  -{Array}  values
25170  -opts (object) options for the chart 
25171      o {
25172      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25173      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25174      o vgutter (number)
25175      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.
25176      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25177      o to
25178      o stretch (boolean)
25179      o }
25180  -opts (object) options for the pie
25181      o{
25182      o cut
25183      o startAngle (number)
25184      o endAngle (number)
25185      } 
25186  *
25187  * @constructor
25188  * Create a new Input
25189  * @param {Object} config The config object
25190  */
25191
25192 Roo.bootstrap.Graph = function(config){
25193     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25194     
25195     this.addEvents({
25196         // img events
25197         /**
25198          * @event click
25199          * The img click event for the img.
25200          * @param {Roo.EventObject} e
25201          */
25202         "click" : true
25203     });
25204 };
25205
25206 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25207     
25208     sm: 4,
25209     md: 5,
25210     graphtype: 'bar',
25211     g_height: 250,
25212     g_width: 400,
25213     g_x: 50,
25214     g_y: 50,
25215     g_r: 30,
25216     opts:{
25217         //g_colors: this.colors,
25218         g_type: 'soft',
25219         g_gutter: '20%'
25220
25221     },
25222     title : false,
25223
25224     getAutoCreate : function(){
25225         
25226         var cfg = {
25227             tag: 'div',
25228             html : null
25229         };
25230         
25231         
25232         return  cfg;
25233     },
25234
25235     onRender : function(ct,position){
25236         
25237         
25238         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25239         
25240         if (typeof(Raphael) == 'undefined') {
25241             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25242             return;
25243         }
25244         
25245         this.raphael = Raphael(this.el.dom);
25246         
25247                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25248                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25249                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25250                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25251                 /*
25252                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25253                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25254                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25255                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25256                 
25257                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25258                 r.barchart(330, 10, 300, 220, data1);
25259                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25260                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25261                 */
25262                 
25263                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25264                 // r.barchart(30, 30, 560, 250,  xdata, {
25265                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25266                 //     axis : "0 0 1 1",
25267                 //     axisxlabels :  xdata
25268                 //     //yvalues : cols,
25269                    
25270                 // });
25271 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25272 //        
25273 //        this.load(null,xdata,{
25274 //                axis : "0 0 1 1",
25275 //                axisxlabels :  xdata
25276 //                });
25277
25278     },
25279
25280     load : function(graphtype,xdata,opts)
25281     {
25282         this.raphael.clear();
25283         if(!graphtype) {
25284             graphtype = this.graphtype;
25285         }
25286         if(!opts){
25287             opts = this.opts;
25288         }
25289         var r = this.raphael,
25290             fin = function () {
25291                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25292             },
25293             fout = function () {
25294                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25295             },
25296             pfin = function() {
25297                 this.sector.stop();
25298                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25299
25300                 if (this.label) {
25301                     this.label[0].stop();
25302                     this.label[0].attr({ r: 7.5 });
25303                     this.label[1].attr({ "font-weight": 800 });
25304                 }
25305             },
25306             pfout = function() {
25307                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25308
25309                 if (this.label) {
25310                     this.label[0].animate({ r: 5 }, 500, "bounce");
25311                     this.label[1].attr({ "font-weight": 400 });
25312                 }
25313             };
25314
25315         switch(graphtype){
25316             case 'bar':
25317                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25318                 break;
25319             case 'hbar':
25320                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25321                 break;
25322             case 'pie':
25323 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25324 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25325 //            
25326                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25327                 
25328                 break;
25329
25330         }
25331         
25332         if(this.title){
25333             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25334         }
25335         
25336     },
25337     
25338     setTitle: function(o)
25339     {
25340         this.title = o;
25341     },
25342     
25343     initEvents: function() {
25344         
25345         if(!this.href){
25346             this.el.on('click', this.onClick, this);
25347         }
25348     },
25349     
25350     onClick : function(e)
25351     {
25352         Roo.log('img onclick');
25353         this.fireEvent('click', this, e);
25354     }
25355    
25356 });
25357
25358  
25359 /*
25360  * - LGPL
25361  *
25362  * numberBox
25363  * 
25364  */
25365 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25366
25367 /**
25368  * @class Roo.bootstrap.dash.NumberBox
25369  * @extends Roo.bootstrap.Component
25370  * Bootstrap NumberBox class
25371  * @cfg {String} headline Box headline
25372  * @cfg {String} content Box content
25373  * @cfg {String} icon Box icon
25374  * @cfg {String} footer Footer text
25375  * @cfg {String} fhref Footer href
25376  * 
25377  * @constructor
25378  * Create a new NumberBox
25379  * @param {Object} config The config object
25380  */
25381
25382
25383 Roo.bootstrap.dash.NumberBox = function(config){
25384     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25385     
25386 };
25387
25388 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25389     
25390     headline : '',
25391     content : '',
25392     icon : '',
25393     footer : '',
25394     fhref : '',
25395     ficon : '',
25396     
25397     getAutoCreate : function(){
25398         
25399         var cfg = {
25400             tag : 'div',
25401             cls : 'small-box ',
25402             cn : [
25403                 {
25404                     tag : 'div',
25405                     cls : 'inner',
25406                     cn :[
25407                         {
25408                             tag : 'h3',
25409                             cls : 'roo-headline',
25410                             html : this.headline
25411                         },
25412                         {
25413                             tag : 'p',
25414                             cls : 'roo-content',
25415                             html : this.content
25416                         }
25417                     ]
25418                 }
25419             ]
25420         };
25421         
25422         if(this.icon){
25423             cfg.cn.push({
25424                 tag : 'div',
25425                 cls : 'icon',
25426                 cn :[
25427                     {
25428                         tag : 'i',
25429                         cls : 'ion ' + this.icon
25430                     }
25431                 ]
25432             });
25433         }
25434         
25435         if(this.footer){
25436             var footer = {
25437                 tag : 'a',
25438                 cls : 'small-box-footer',
25439                 href : this.fhref || '#',
25440                 html : this.footer
25441             };
25442             
25443             cfg.cn.push(footer);
25444             
25445         }
25446         
25447         return  cfg;
25448     },
25449
25450     onRender : function(ct,position){
25451         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25452
25453
25454        
25455                 
25456     },
25457
25458     setHeadline: function (value)
25459     {
25460         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25461     },
25462     
25463     setFooter: function (value, href)
25464     {
25465         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25466         
25467         if(href){
25468             this.el.select('a.small-box-footer',true).first().attr('href', href);
25469         }
25470         
25471     },
25472
25473     setContent: function (value)
25474     {
25475         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25476     },
25477
25478     initEvents: function() 
25479     {   
25480         
25481     }
25482     
25483 });
25484
25485  
25486 /*
25487  * - LGPL
25488  *
25489  * TabBox
25490  * 
25491  */
25492 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25493
25494 /**
25495  * @class Roo.bootstrap.dash.TabBox
25496  * @extends Roo.bootstrap.Component
25497  * Bootstrap TabBox class
25498  * @cfg {String} title Title of the TabBox
25499  * @cfg {String} icon Icon of the TabBox
25500  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25501  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25502  * 
25503  * @constructor
25504  * Create a new TabBox
25505  * @param {Object} config The config object
25506  */
25507
25508
25509 Roo.bootstrap.dash.TabBox = function(config){
25510     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25511     this.addEvents({
25512         // raw events
25513         /**
25514          * @event addpane
25515          * When a pane is added
25516          * @param {Roo.bootstrap.dash.TabPane} pane
25517          */
25518         "addpane" : true,
25519         /**
25520          * @event activatepane
25521          * When a pane is activated
25522          * @param {Roo.bootstrap.dash.TabPane} pane
25523          */
25524         "activatepane" : true
25525         
25526          
25527     });
25528     
25529     this.panes = [];
25530 };
25531
25532 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25533
25534     title : '',
25535     icon : false,
25536     showtabs : true,
25537     tabScrollable : false,
25538     
25539     getChildContainer : function()
25540     {
25541         return this.el.select('.tab-content', true).first();
25542     },
25543     
25544     getAutoCreate : function(){
25545         
25546         var header = {
25547             tag: 'li',
25548             cls: 'pull-left header',
25549             html: this.title,
25550             cn : []
25551         };
25552         
25553         if(this.icon){
25554             header.cn.push({
25555                 tag: 'i',
25556                 cls: 'fa ' + this.icon
25557             });
25558         }
25559         
25560         var h = {
25561             tag: 'ul',
25562             cls: 'nav nav-tabs pull-right',
25563             cn: [
25564                 header
25565             ]
25566         };
25567         
25568         if(this.tabScrollable){
25569             h = {
25570                 tag: 'div',
25571                 cls: 'tab-header',
25572                 cn: [
25573                     {
25574                         tag: 'ul',
25575                         cls: 'nav nav-tabs pull-right',
25576                         cn: [
25577                             header
25578                         ]
25579                     }
25580                 ]
25581             };
25582         }
25583         
25584         var cfg = {
25585             tag: 'div',
25586             cls: 'nav-tabs-custom',
25587             cn: [
25588                 h,
25589                 {
25590                     tag: 'div',
25591                     cls: 'tab-content no-padding',
25592                     cn: []
25593                 }
25594             ]
25595         };
25596
25597         return  cfg;
25598     },
25599     initEvents : function()
25600     {
25601         //Roo.log('add add pane handler');
25602         this.on('addpane', this.onAddPane, this);
25603     },
25604      /**
25605      * Updates the box title
25606      * @param {String} html to set the title to.
25607      */
25608     setTitle : function(value)
25609     {
25610         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25611     },
25612     onAddPane : function(pane)
25613     {
25614         this.panes.push(pane);
25615         //Roo.log('addpane');
25616         //Roo.log(pane);
25617         // tabs are rendere left to right..
25618         if(!this.showtabs){
25619             return;
25620         }
25621         
25622         var ctr = this.el.select('.nav-tabs', true).first();
25623          
25624          
25625         var existing = ctr.select('.nav-tab',true);
25626         var qty = existing.getCount();;
25627         
25628         
25629         var tab = ctr.createChild({
25630             tag : 'li',
25631             cls : 'nav-tab' + (qty ? '' : ' active'),
25632             cn : [
25633                 {
25634                     tag : 'a',
25635                     href:'#',
25636                     html : pane.title
25637                 }
25638             ]
25639         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25640         pane.tab = tab;
25641         
25642         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25643         if (!qty) {
25644             pane.el.addClass('active');
25645         }
25646         
25647                 
25648     },
25649     onTabClick : function(ev,un,ob,pane)
25650     {
25651         //Roo.log('tab - prev default');
25652         ev.preventDefault();
25653         
25654         
25655         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25656         pane.tab.addClass('active');
25657         //Roo.log(pane.title);
25658         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25659         // technically we should have a deactivate event.. but maybe add later.
25660         // and it should not de-activate the selected tab...
25661         this.fireEvent('activatepane', pane);
25662         pane.el.addClass('active');
25663         pane.fireEvent('activate');
25664         
25665         
25666     },
25667     
25668     getActivePane : function()
25669     {
25670         var r = false;
25671         Roo.each(this.panes, function(p) {
25672             if(p.el.hasClass('active')){
25673                 r = p;
25674                 return false;
25675             }
25676             
25677             return;
25678         });
25679         
25680         return r;
25681     }
25682     
25683     
25684 });
25685
25686  
25687 /*
25688  * - LGPL
25689  *
25690  * Tab pane
25691  * 
25692  */
25693 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25694 /**
25695  * @class Roo.bootstrap.TabPane
25696  * @extends Roo.bootstrap.Component
25697  * Bootstrap TabPane class
25698  * @cfg {Boolean} active (false | true) Default false
25699  * @cfg {String} title title of panel
25700
25701  * 
25702  * @constructor
25703  * Create a new TabPane
25704  * @param {Object} config The config object
25705  */
25706
25707 Roo.bootstrap.dash.TabPane = function(config){
25708     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25709     
25710     this.addEvents({
25711         // raw events
25712         /**
25713          * @event activate
25714          * When a pane is activated
25715          * @param {Roo.bootstrap.dash.TabPane} pane
25716          */
25717         "activate" : true
25718          
25719     });
25720 };
25721
25722 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25723     
25724     active : false,
25725     title : '',
25726     
25727     // the tabBox that this is attached to.
25728     tab : false,
25729      
25730     getAutoCreate : function() 
25731     {
25732         var cfg = {
25733             tag: 'div',
25734             cls: 'tab-pane'
25735         };
25736         
25737         if(this.active){
25738             cfg.cls += ' active';
25739         }
25740         
25741         return cfg;
25742     },
25743     initEvents  : function()
25744     {
25745         //Roo.log('trigger add pane handler');
25746         this.parent().fireEvent('addpane', this)
25747     },
25748     
25749      /**
25750      * Updates the tab title 
25751      * @param {String} html to set the title to.
25752      */
25753     setTitle: function(str)
25754     {
25755         if (!this.tab) {
25756             return;
25757         }
25758         this.title = str;
25759         this.tab.select('a', true).first().dom.innerHTML = str;
25760         
25761     }
25762     
25763     
25764     
25765 });
25766
25767  
25768
25769
25770  /*
25771  * - LGPL
25772  *
25773  * menu
25774  * 
25775  */
25776 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25777
25778 /**
25779  * @class Roo.bootstrap.menu.Menu
25780  * @extends Roo.bootstrap.Component
25781  * Bootstrap Menu class - container for Menu
25782  * @cfg {String} html Text of the menu
25783  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25784  * @cfg {String} icon Font awesome icon
25785  * @cfg {String} pos Menu align to (top | bottom) default bottom
25786  * 
25787  * 
25788  * @constructor
25789  * Create a new Menu
25790  * @param {Object} config The config object
25791  */
25792
25793
25794 Roo.bootstrap.menu.Menu = function(config){
25795     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25796     
25797     this.addEvents({
25798         /**
25799          * @event beforeshow
25800          * Fires before this menu is displayed
25801          * @param {Roo.bootstrap.menu.Menu} this
25802          */
25803         beforeshow : true,
25804         /**
25805          * @event beforehide
25806          * Fires before this menu is hidden
25807          * @param {Roo.bootstrap.menu.Menu} this
25808          */
25809         beforehide : true,
25810         /**
25811          * @event show
25812          * Fires after this menu is displayed
25813          * @param {Roo.bootstrap.menu.Menu} this
25814          */
25815         show : true,
25816         /**
25817          * @event hide
25818          * Fires after this menu is hidden
25819          * @param {Roo.bootstrap.menu.Menu} this
25820          */
25821         hide : true,
25822         /**
25823          * @event click
25824          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25825          * @param {Roo.bootstrap.menu.Menu} this
25826          * @param {Roo.EventObject} e
25827          */
25828         click : true
25829     });
25830     
25831 };
25832
25833 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25834     
25835     submenu : false,
25836     html : '',
25837     weight : 'default',
25838     icon : false,
25839     pos : 'bottom',
25840     
25841     
25842     getChildContainer : function() {
25843         if(this.isSubMenu){
25844             return this.el;
25845         }
25846         
25847         return this.el.select('ul.dropdown-menu', true).first();  
25848     },
25849     
25850     getAutoCreate : function()
25851     {
25852         var text = [
25853             {
25854                 tag : 'span',
25855                 cls : 'roo-menu-text',
25856                 html : this.html
25857             }
25858         ];
25859         
25860         if(this.icon){
25861             text.unshift({
25862                 tag : 'i',
25863                 cls : 'fa ' + this.icon
25864             })
25865         }
25866         
25867         
25868         var cfg = {
25869             tag : 'div',
25870             cls : 'btn-group',
25871             cn : [
25872                 {
25873                     tag : 'button',
25874                     cls : 'dropdown-button btn btn-' + this.weight,
25875                     cn : text
25876                 },
25877                 {
25878                     tag : 'button',
25879                     cls : 'dropdown-toggle btn btn-' + this.weight,
25880                     cn : [
25881                         {
25882                             tag : 'span',
25883                             cls : 'caret'
25884                         }
25885                     ]
25886                 },
25887                 {
25888                     tag : 'ul',
25889                     cls : 'dropdown-menu'
25890                 }
25891             ]
25892             
25893         };
25894         
25895         if(this.pos == 'top'){
25896             cfg.cls += ' dropup';
25897         }
25898         
25899         if(this.isSubMenu){
25900             cfg = {
25901                 tag : 'ul',
25902                 cls : 'dropdown-menu'
25903             }
25904         }
25905         
25906         return cfg;
25907     },
25908     
25909     onRender : function(ct, position)
25910     {
25911         this.isSubMenu = ct.hasClass('dropdown-submenu');
25912         
25913         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25914     },
25915     
25916     initEvents : function() 
25917     {
25918         if(this.isSubMenu){
25919             return;
25920         }
25921         
25922         this.hidden = true;
25923         
25924         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25925         this.triggerEl.on('click', this.onTriggerPress, this);
25926         
25927         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25928         this.buttonEl.on('click', this.onClick, this);
25929         
25930     },
25931     
25932     list : function()
25933     {
25934         if(this.isSubMenu){
25935             return this.el;
25936         }
25937         
25938         return this.el.select('ul.dropdown-menu', true).first();
25939     },
25940     
25941     onClick : function(e)
25942     {
25943         this.fireEvent("click", this, e);
25944     },
25945     
25946     onTriggerPress  : function(e)
25947     {   
25948         if (this.isVisible()) {
25949             this.hide();
25950         } else {
25951             this.show();
25952         }
25953     },
25954     
25955     isVisible : function(){
25956         return !this.hidden;
25957     },
25958     
25959     show : function()
25960     {
25961         this.fireEvent("beforeshow", this);
25962         
25963         this.hidden = false;
25964         this.el.addClass('open');
25965         
25966         Roo.get(document).on("mouseup", this.onMouseUp, this);
25967         
25968         this.fireEvent("show", this);
25969         
25970         
25971     },
25972     
25973     hide : function()
25974     {
25975         this.fireEvent("beforehide", this);
25976         
25977         this.hidden = true;
25978         this.el.removeClass('open');
25979         
25980         Roo.get(document).un("mouseup", this.onMouseUp);
25981         
25982         this.fireEvent("hide", this);
25983     },
25984     
25985     onMouseUp : function()
25986     {
25987         this.hide();
25988     }
25989     
25990 });
25991
25992  
25993  /*
25994  * - LGPL
25995  *
25996  * menu item
25997  * 
25998  */
25999 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26000
26001 /**
26002  * @class Roo.bootstrap.menu.Item
26003  * @extends Roo.bootstrap.Component
26004  * Bootstrap MenuItem class
26005  * @cfg {Boolean} submenu (true | false) default false
26006  * @cfg {String} html text of the item
26007  * @cfg {String} href the link
26008  * @cfg {Boolean} disable (true | false) default false
26009  * @cfg {Boolean} preventDefault (true | false) default true
26010  * @cfg {String} icon Font awesome icon
26011  * @cfg {String} pos Submenu align to (left | right) default right 
26012  * 
26013  * 
26014  * @constructor
26015  * Create a new Item
26016  * @param {Object} config The config object
26017  */
26018
26019
26020 Roo.bootstrap.menu.Item = function(config){
26021     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26022     this.addEvents({
26023         /**
26024          * @event mouseover
26025          * Fires when the mouse is hovering over this menu
26026          * @param {Roo.bootstrap.menu.Item} this
26027          * @param {Roo.EventObject} e
26028          */
26029         mouseover : true,
26030         /**
26031          * @event mouseout
26032          * Fires when the mouse exits this menu
26033          * @param {Roo.bootstrap.menu.Item} this
26034          * @param {Roo.EventObject} e
26035          */
26036         mouseout : true,
26037         // raw events
26038         /**
26039          * @event click
26040          * The raw click event for the entire grid.
26041          * @param {Roo.EventObject} e
26042          */
26043         click : true
26044     });
26045 };
26046
26047 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26048     
26049     submenu : false,
26050     href : '',
26051     html : '',
26052     preventDefault: true,
26053     disable : false,
26054     icon : false,
26055     pos : 'right',
26056     
26057     getAutoCreate : function()
26058     {
26059         var text = [
26060             {
26061                 tag : 'span',
26062                 cls : 'roo-menu-item-text',
26063                 html : this.html
26064             }
26065         ];
26066         
26067         if(this.icon){
26068             text.unshift({
26069                 tag : 'i',
26070                 cls : 'fa ' + this.icon
26071             })
26072         }
26073         
26074         var cfg = {
26075             tag : 'li',
26076             cn : [
26077                 {
26078                     tag : 'a',
26079                     href : this.href || '#',
26080                     cn : text
26081                 }
26082             ]
26083         };
26084         
26085         if(this.disable){
26086             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26087         }
26088         
26089         if(this.submenu){
26090             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26091             
26092             if(this.pos == 'left'){
26093                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26094             }
26095         }
26096         
26097         return cfg;
26098     },
26099     
26100     initEvents : function() 
26101     {
26102         this.el.on('mouseover', this.onMouseOver, this);
26103         this.el.on('mouseout', this.onMouseOut, this);
26104         
26105         this.el.select('a', true).first().on('click', this.onClick, this);
26106         
26107     },
26108     
26109     onClick : function(e)
26110     {
26111         if(this.preventDefault){
26112             e.preventDefault();
26113         }
26114         
26115         this.fireEvent("click", this, e);
26116     },
26117     
26118     onMouseOver : function(e)
26119     {
26120         if(this.submenu && this.pos == 'left'){
26121             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26122         }
26123         
26124         this.fireEvent("mouseover", this, e);
26125     },
26126     
26127     onMouseOut : function(e)
26128     {
26129         this.fireEvent("mouseout", this, e);
26130     }
26131 });
26132
26133  
26134
26135  /*
26136  * - LGPL
26137  *
26138  * menu separator
26139  * 
26140  */
26141 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26142
26143 /**
26144  * @class Roo.bootstrap.menu.Separator
26145  * @extends Roo.bootstrap.Component
26146  * Bootstrap Separator class
26147  * 
26148  * @constructor
26149  * Create a new Separator
26150  * @param {Object} config The config object
26151  */
26152
26153
26154 Roo.bootstrap.menu.Separator = function(config){
26155     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26156 };
26157
26158 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26159     
26160     getAutoCreate : function(){
26161         var cfg = {
26162             tag : 'li',
26163             cls: 'divider'
26164         };
26165         
26166         return cfg;
26167     }
26168    
26169 });
26170
26171  
26172
26173  /*
26174  * - LGPL
26175  *
26176  * Tooltip
26177  * 
26178  */
26179
26180 /**
26181  * @class Roo.bootstrap.Tooltip
26182  * Bootstrap Tooltip class
26183  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26184  * to determine which dom element triggers the tooltip.
26185  * 
26186  * It needs to add support for additional attributes like tooltip-position
26187  * 
26188  * @constructor
26189  * Create a new Toolti
26190  * @param {Object} config The config object
26191  */
26192
26193 Roo.bootstrap.Tooltip = function(config){
26194     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26195     
26196     this.alignment = Roo.bootstrap.Tooltip.alignment;
26197     
26198     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26199         this.alignment = config.alignment;
26200     }
26201     
26202 };
26203
26204 Roo.apply(Roo.bootstrap.Tooltip, {
26205     /**
26206      * @function init initialize tooltip monitoring.
26207      * @static
26208      */
26209     currentEl : false,
26210     currentTip : false,
26211     currentRegion : false,
26212     
26213     //  init : delay?
26214     
26215     init : function()
26216     {
26217         Roo.get(document).on('mouseover', this.enter ,this);
26218         Roo.get(document).on('mouseout', this.leave, this);
26219          
26220         
26221         this.currentTip = new Roo.bootstrap.Tooltip();
26222     },
26223     
26224     enter : function(ev)
26225     {
26226         var dom = ev.getTarget();
26227         
26228         //Roo.log(['enter',dom]);
26229         var el = Roo.fly(dom);
26230         if (this.currentEl) {
26231             //Roo.log(dom);
26232             //Roo.log(this.currentEl);
26233             //Roo.log(this.currentEl.contains(dom));
26234             if (this.currentEl == el) {
26235                 return;
26236             }
26237             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26238                 return;
26239             }
26240
26241         }
26242         
26243         if (this.currentTip.el) {
26244             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26245         }    
26246         //Roo.log(ev);
26247         
26248         if(!el || el.dom == document){
26249             return;
26250         }
26251         
26252         var bindEl = el;
26253         
26254         // you can not look for children, as if el is the body.. then everythign is the child..
26255         if (!el.attr('tooltip')) { //
26256             if (!el.select("[tooltip]").elements.length) {
26257                 return;
26258             }
26259             // is the mouse over this child...?
26260             bindEl = el.select("[tooltip]").first();
26261             var xy = ev.getXY();
26262             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26263                 //Roo.log("not in region.");
26264                 return;
26265             }
26266             //Roo.log("child element over..");
26267             
26268         }
26269         this.currentEl = bindEl;
26270         this.currentTip.bind(bindEl);
26271         this.currentRegion = Roo.lib.Region.getRegion(dom);
26272         this.currentTip.enter();
26273         
26274     },
26275     leave : function(ev)
26276     {
26277         var dom = ev.getTarget();
26278         //Roo.log(['leave',dom]);
26279         if (!this.currentEl) {
26280             return;
26281         }
26282         
26283         
26284         if (dom != this.currentEl.dom) {
26285             return;
26286         }
26287         var xy = ev.getXY();
26288         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26289             return;
26290         }
26291         // only activate leave if mouse cursor is outside... bounding box..
26292         
26293         
26294         
26295         
26296         if (this.currentTip) {
26297             this.currentTip.leave();
26298         }
26299         //Roo.log('clear currentEl');
26300         this.currentEl = false;
26301         
26302         
26303     },
26304     alignment : {
26305         'left' : ['r-l', [-2,0], 'right'],
26306         'right' : ['l-r', [2,0], 'left'],
26307         'bottom' : ['t-b', [0,2], 'top'],
26308         'top' : [ 'b-t', [0,-2], 'bottom']
26309     }
26310     
26311 });
26312
26313
26314 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26315     
26316     
26317     bindEl : false,
26318     
26319     delay : null, // can be { show : 300 , hide: 500}
26320     
26321     timeout : null,
26322     
26323     hoverState : null, //???
26324     
26325     placement : 'bottom', 
26326     
26327     alignment : false,
26328     
26329     getAutoCreate : function(){
26330     
26331         var cfg = {
26332            cls : 'tooltip',
26333            role : 'tooltip',
26334            cn : [
26335                 {
26336                     cls : 'tooltip-arrow'
26337                 },
26338                 {
26339                     cls : 'tooltip-inner'
26340                 }
26341            ]
26342         };
26343         
26344         return cfg;
26345     },
26346     bind : function(el)
26347     {
26348         this.bindEl = el;
26349     },
26350       
26351     
26352     enter : function () {
26353        
26354         if (this.timeout != null) {
26355             clearTimeout(this.timeout);
26356         }
26357         
26358         this.hoverState = 'in';
26359          //Roo.log("enter - show");
26360         if (!this.delay || !this.delay.show) {
26361             this.show();
26362             return;
26363         }
26364         var _t = this;
26365         this.timeout = setTimeout(function () {
26366             if (_t.hoverState == 'in') {
26367                 _t.show();
26368             }
26369         }, this.delay.show);
26370     },
26371     leave : function()
26372     {
26373         clearTimeout(this.timeout);
26374     
26375         this.hoverState = 'out';
26376          if (!this.delay || !this.delay.hide) {
26377             this.hide();
26378             return;
26379         }
26380        
26381         var _t = this;
26382         this.timeout = setTimeout(function () {
26383             //Roo.log("leave - timeout");
26384             
26385             if (_t.hoverState == 'out') {
26386                 _t.hide();
26387                 Roo.bootstrap.Tooltip.currentEl = false;
26388             }
26389         }, delay);
26390     },
26391     
26392     show : function (msg)
26393     {
26394         if (!this.el) {
26395             this.render(document.body);
26396         }
26397         // set content.
26398         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26399         
26400         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26401         
26402         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26403         
26404         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26405         
26406         var placement = typeof this.placement == 'function' ?
26407             this.placement.call(this, this.el, on_el) :
26408             this.placement;
26409             
26410         var autoToken = /\s?auto?\s?/i;
26411         var autoPlace = autoToken.test(placement);
26412         if (autoPlace) {
26413             placement = placement.replace(autoToken, '') || 'top';
26414         }
26415         
26416         //this.el.detach()
26417         //this.el.setXY([0,0]);
26418         this.el.show();
26419         //this.el.dom.style.display='block';
26420         
26421         //this.el.appendTo(on_el);
26422         
26423         var p = this.getPosition();
26424         var box = this.el.getBox();
26425         
26426         if (autoPlace) {
26427             // fixme..
26428         }
26429         
26430         var align = this.alignment[placement];
26431         
26432         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26433         
26434         if(placement == 'top' || placement == 'bottom'){
26435             if(xy[0] < 0){
26436                 placement = 'right';
26437             }
26438             
26439             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26440                 placement = 'left';
26441             }
26442             
26443             var scroll = Roo.select('body', true).first().getScroll();
26444             
26445             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26446                 placement = 'top';
26447             }
26448             
26449             align = this.alignment[placement];
26450         }
26451         
26452         this.el.alignTo(this.bindEl, align[0],align[1]);
26453         //var arrow = this.el.select('.arrow',true).first();
26454         //arrow.set(align[2], 
26455         
26456         this.el.addClass(placement);
26457         
26458         this.el.addClass('in fade');
26459         
26460         this.hoverState = null;
26461         
26462         if (this.el.hasClass('fade')) {
26463             // fade it?
26464         }
26465         
26466     },
26467     hide : function()
26468     {
26469          
26470         if (!this.el) {
26471             return;
26472         }
26473         //this.el.setXY([0,0]);
26474         this.el.removeClass('in');
26475         //this.el.hide();
26476         
26477     }
26478     
26479 });
26480  
26481
26482  /*
26483  * - LGPL
26484  *
26485  * Location Picker
26486  * 
26487  */
26488
26489 /**
26490  * @class Roo.bootstrap.LocationPicker
26491  * @extends Roo.bootstrap.Component
26492  * Bootstrap LocationPicker class
26493  * @cfg {Number} latitude Position when init default 0
26494  * @cfg {Number} longitude Position when init default 0
26495  * @cfg {Number} zoom default 15
26496  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26497  * @cfg {Boolean} mapTypeControl default false
26498  * @cfg {Boolean} disableDoubleClickZoom default false
26499  * @cfg {Boolean} scrollwheel default true
26500  * @cfg {Boolean} streetViewControl default false
26501  * @cfg {Number} radius default 0
26502  * @cfg {String} locationName
26503  * @cfg {Boolean} draggable default true
26504  * @cfg {Boolean} enableAutocomplete default false
26505  * @cfg {Boolean} enableReverseGeocode default true
26506  * @cfg {String} markerTitle
26507  * 
26508  * @constructor
26509  * Create a new LocationPicker
26510  * @param {Object} config The config object
26511  */
26512
26513
26514 Roo.bootstrap.LocationPicker = function(config){
26515     
26516     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26517     
26518     this.addEvents({
26519         /**
26520          * @event initial
26521          * Fires when the picker initialized.
26522          * @param {Roo.bootstrap.LocationPicker} this
26523          * @param {Google Location} location
26524          */
26525         initial : true,
26526         /**
26527          * @event positionchanged
26528          * Fires when the picker position changed.
26529          * @param {Roo.bootstrap.LocationPicker} this
26530          * @param {Google Location} location
26531          */
26532         positionchanged : true,
26533         /**
26534          * @event resize
26535          * Fires when the map resize.
26536          * @param {Roo.bootstrap.LocationPicker} this
26537          */
26538         resize : true,
26539         /**
26540          * @event show
26541          * Fires when the map show.
26542          * @param {Roo.bootstrap.LocationPicker} this
26543          */
26544         show : true,
26545         /**
26546          * @event hide
26547          * Fires when the map hide.
26548          * @param {Roo.bootstrap.LocationPicker} this
26549          */
26550         hide : true,
26551         /**
26552          * @event mapClick
26553          * Fires when click the map.
26554          * @param {Roo.bootstrap.LocationPicker} this
26555          * @param {Map event} e
26556          */
26557         mapClick : true,
26558         /**
26559          * @event mapRightClick
26560          * Fires when right click the map.
26561          * @param {Roo.bootstrap.LocationPicker} this
26562          * @param {Map event} e
26563          */
26564         mapRightClick : true,
26565         /**
26566          * @event markerClick
26567          * Fires when click the marker.
26568          * @param {Roo.bootstrap.LocationPicker} this
26569          * @param {Map event} e
26570          */
26571         markerClick : true,
26572         /**
26573          * @event markerRightClick
26574          * Fires when right click the marker.
26575          * @param {Roo.bootstrap.LocationPicker} this
26576          * @param {Map event} e
26577          */
26578         markerRightClick : true,
26579         /**
26580          * @event OverlayViewDraw
26581          * Fires when OverlayView Draw
26582          * @param {Roo.bootstrap.LocationPicker} this
26583          */
26584         OverlayViewDraw : true,
26585         /**
26586          * @event OverlayViewOnAdd
26587          * Fires when OverlayView Draw
26588          * @param {Roo.bootstrap.LocationPicker} this
26589          */
26590         OverlayViewOnAdd : true,
26591         /**
26592          * @event OverlayViewOnRemove
26593          * Fires when OverlayView Draw
26594          * @param {Roo.bootstrap.LocationPicker} this
26595          */
26596         OverlayViewOnRemove : true,
26597         /**
26598          * @event OverlayViewShow
26599          * Fires when OverlayView Draw
26600          * @param {Roo.bootstrap.LocationPicker} this
26601          * @param {Pixel} cpx
26602          */
26603         OverlayViewShow : true,
26604         /**
26605          * @event OverlayViewHide
26606          * Fires when OverlayView Draw
26607          * @param {Roo.bootstrap.LocationPicker} this
26608          */
26609         OverlayViewHide : true,
26610         /**
26611          * @event loadexception
26612          * Fires when load google lib failed.
26613          * @param {Roo.bootstrap.LocationPicker} this
26614          */
26615         loadexception : true
26616     });
26617         
26618 };
26619
26620 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26621     
26622     gMapContext: false,
26623     
26624     latitude: 0,
26625     longitude: 0,
26626     zoom: 15,
26627     mapTypeId: false,
26628     mapTypeControl: false,
26629     disableDoubleClickZoom: false,
26630     scrollwheel: true,
26631     streetViewControl: false,
26632     radius: 0,
26633     locationName: '',
26634     draggable: true,
26635     enableAutocomplete: false,
26636     enableReverseGeocode: true,
26637     markerTitle: '',
26638     
26639     getAutoCreate: function()
26640     {
26641
26642         var cfg = {
26643             tag: 'div',
26644             cls: 'roo-location-picker'
26645         };
26646         
26647         return cfg
26648     },
26649     
26650     initEvents: function(ct, position)
26651     {       
26652         if(!this.el.getWidth() || this.isApplied()){
26653             return;
26654         }
26655         
26656         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26657         
26658         this.initial();
26659     },
26660     
26661     initial: function()
26662     {
26663         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26664             this.fireEvent('loadexception', this);
26665             return;
26666         }
26667         
26668         if(!this.mapTypeId){
26669             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26670         }
26671         
26672         this.gMapContext = this.GMapContext();
26673         
26674         this.initOverlayView();
26675         
26676         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26677         
26678         var _this = this;
26679                 
26680         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26681             _this.setPosition(_this.gMapContext.marker.position);
26682         });
26683         
26684         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26685             _this.fireEvent('mapClick', this, event);
26686             
26687         });
26688
26689         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26690             _this.fireEvent('mapRightClick', this, event);
26691             
26692         });
26693         
26694         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26695             _this.fireEvent('markerClick', this, event);
26696             
26697         });
26698
26699         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26700             _this.fireEvent('markerRightClick', this, event);
26701             
26702         });
26703         
26704         this.setPosition(this.gMapContext.location);
26705         
26706         this.fireEvent('initial', this, this.gMapContext.location);
26707     },
26708     
26709     initOverlayView: function()
26710     {
26711         var _this = this;
26712         
26713         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26714             
26715             draw: function()
26716             {
26717                 _this.fireEvent('OverlayViewDraw', _this);
26718             },
26719             
26720             onAdd: function()
26721             {
26722                 _this.fireEvent('OverlayViewOnAdd', _this);
26723             },
26724             
26725             onRemove: function()
26726             {
26727                 _this.fireEvent('OverlayViewOnRemove', _this);
26728             },
26729             
26730             show: function(cpx)
26731             {
26732                 _this.fireEvent('OverlayViewShow', _this, cpx);
26733             },
26734             
26735             hide: function()
26736             {
26737                 _this.fireEvent('OverlayViewHide', _this);
26738             }
26739             
26740         });
26741     },
26742     
26743     fromLatLngToContainerPixel: function(event)
26744     {
26745         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26746     },
26747     
26748     isApplied: function() 
26749     {
26750         return this.getGmapContext() == false ? false : true;
26751     },
26752     
26753     getGmapContext: function() 
26754     {
26755         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26756     },
26757     
26758     GMapContext: function() 
26759     {
26760         var position = new google.maps.LatLng(this.latitude, this.longitude);
26761         
26762         var _map = new google.maps.Map(this.el.dom, {
26763             center: position,
26764             zoom: this.zoom,
26765             mapTypeId: this.mapTypeId,
26766             mapTypeControl: this.mapTypeControl,
26767             disableDoubleClickZoom: this.disableDoubleClickZoom,
26768             scrollwheel: this.scrollwheel,
26769             streetViewControl: this.streetViewControl,
26770             locationName: this.locationName,
26771             draggable: this.draggable,
26772             enableAutocomplete: this.enableAutocomplete,
26773             enableReverseGeocode: this.enableReverseGeocode
26774         });
26775         
26776         var _marker = new google.maps.Marker({
26777             position: position,
26778             map: _map,
26779             title: this.markerTitle,
26780             draggable: this.draggable
26781         });
26782         
26783         return {
26784             map: _map,
26785             marker: _marker,
26786             circle: null,
26787             location: position,
26788             radius: this.radius,
26789             locationName: this.locationName,
26790             addressComponents: {
26791                 formatted_address: null,
26792                 addressLine1: null,
26793                 addressLine2: null,
26794                 streetName: null,
26795                 streetNumber: null,
26796                 city: null,
26797                 district: null,
26798                 state: null,
26799                 stateOrProvince: null
26800             },
26801             settings: this,
26802             domContainer: this.el.dom,
26803             geodecoder: new google.maps.Geocoder()
26804         };
26805     },
26806     
26807     drawCircle: function(center, radius, options) 
26808     {
26809         if (this.gMapContext.circle != null) {
26810             this.gMapContext.circle.setMap(null);
26811         }
26812         if (radius > 0) {
26813             radius *= 1;
26814             options = Roo.apply({}, options, {
26815                 strokeColor: "#0000FF",
26816                 strokeOpacity: .35,
26817                 strokeWeight: 2,
26818                 fillColor: "#0000FF",
26819                 fillOpacity: .2
26820             });
26821             
26822             options.map = this.gMapContext.map;
26823             options.radius = radius;
26824             options.center = center;
26825             this.gMapContext.circle = new google.maps.Circle(options);
26826             return this.gMapContext.circle;
26827         }
26828         
26829         return null;
26830     },
26831     
26832     setPosition: function(location) 
26833     {
26834         this.gMapContext.location = location;
26835         this.gMapContext.marker.setPosition(location);
26836         this.gMapContext.map.panTo(location);
26837         this.drawCircle(location, this.gMapContext.radius, {});
26838         
26839         var _this = this;
26840         
26841         if (this.gMapContext.settings.enableReverseGeocode) {
26842             this.gMapContext.geodecoder.geocode({
26843                 latLng: this.gMapContext.location
26844             }, function(results, status) {
26845                 
26846                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26847                     _this.gMapContext.locationName = results[0].formatted_address;
26848                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26849                     
26850                     _this.fireEvent('positionchanged', this, location);
26851                 }
26852             });
26853             
26854             return;
26855         }
26856         
26857         this.fireEvent('positionchanged', this, location);
26858     },
26859     
26860     resize: function()
26861     {
26862         google.maps.event.trigger(this.gMapContext.map, "resize");
26863         
26864         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26865         
26866         this.fireEvent('resize', this);
26867     },
26868     
26869     setPositionByLatLng: function(latitude, longitude)
26870     {
26871         this.setPosition(new google.maps.LatLng(latitude, longitude));
26872     },
26873     
26874     getCurrentPosition: function() 
26875     {
26876         return {
26877             latitude: this.gMapContext.location.lat(),
26878             longitude: this.gMapContext.location.lng()
26879         };
26880     },
26881     
26882     getAddressName: function() 
26883     {
26884         return this.gMapContext.locationName;
26885     },
26886     
26887     getAddressComponents: function() 
26888     {
26889         return this.gMapContext.addressComponents;
26890     },
26891     
26892     address_component_from_google_geocode: function(address_components) 
26893     {
26894         var result = {};
26895         
26896         for (var i = 0; i < address_components.length; i++) {
26897             var component = address_components[i];
26898             if (component.types.indexOf("postal_code") >= 0) {
26899                 result.postalCode = component.short_name;
26900             } else if (component.types.indexOf("street_number") >= 0) {
26901                 result.streetNumber = component.short_name;
26902             } else if (component.types.indexOf("route") >= 0) {
26903                 result.streetName = component.short_name;
26904             } else if (component.types.indexOf("neighborhood") >= 0) {
26905                 result.city = component.short_name;
26906             } else if (component.types.indexOf("locality") >= 0) {
26907                 result.city = component.short_name;
26908             } else if (component.types.indexOf("sublocality") >= 0) {
26909                 result.district = component.short_name;
26910             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26911                 result.stateOrProvince = component.short_name;
26912             } else if (component.types.indexOf("country") >= 0) {
26913                 result.country = component.short_name;
26914             }
26915         }
26916         
26917         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26918         result.addressLine2 = "";
26919         return result;
26920     },
26921     
26922     setZoomLevel: function(zoom)
26923     {
26924         this.gMapContext.map.setZoom(zoom);
26925     },
26926     
26927     show: function()
26928     {
26929         if(!this.el){
26930             return;
26931         }
26932         
26933         this.el.show();
26934         
26935         this.resize();
26936         
26937         this.fireEvent('show', this);
26938     },
26939     
26940     hide: function()
26941     {
26942         if(!this.el){
26943             return;
26944         }
26945         
26946         this.el.hide();
26947         
26948         this.fireEvent('hide', this);
26949     }
26950     
26951 });
26952
26953 Roo.apply(Roo.bootstrap.LocationPicker, {
26954     
26955     OverlayView : function(map, options)
26956     {
26957         options = options || {};
26958         
26959         this.setMap(map);
26960     }
26961     
26962     
26963 });/*
26964  * - LGPL
26965  *
26966  * Alert
26967  * 
26968  */
26969
26970 /**
26971  * @class Roo.bootstrap.Alert
26972  * @extends Roo.bootstrap.Component
26973  * Bootstrap Alert class
26974  * @cfg {String} title The title of alert
26975  * @cfg {String} html The content of alert
26976  * @cfg {String} weight (  success | info | warning | danger )
26977  * @cfg {String} faicon font-awesomeicon
26978  * 
26979  * @constructor
26980  * Create a new alert
26981  * @param {Object} config The config object
26982  */
26983
26984
26985 Roo.bootstrap.Alert = function(config){
26986     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26987     
26988 };
26989
26990 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26991     
26992     title: '',
26993     html: '',
26994     weight: false,
26995     faicon: false,
26996     
26997     getAutoCreate : function()
26998     {
26999         
27000         var cfg = {
27001             tag : 'div',
27002             cls : 'alert',
27003             cn : [
27004                 {
27005                     tag : 'i',
27006                     cls : 'roo-alert-icon'
27007                     
27008                 },
27009                 {
27010                     tag : 'b',
27011                     cls : 'roo-alert-title',
27012                     html : this.title
27013                 },
27014                 {
27015                     tag : 'span',
27016                     cls : 'roo-alert-text',
27017                     html : this.html
27018                 }
27019             ]
27020         };
27021         
27022         if(this.faicon){
27023             cfg.cn[0].cls += ' fa ' + this.faicon;
27024         }
27025         
27026         if(this.weight){
27027             cfg.cls += ' alert-' + this.weight;
27028         }
27029         
27030         return cfg;
27031     },
27032     
27033     initEvents: function() 
27034     {
27035         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27036     },
27037     
27038     setTitle : function(str)
27039     {
27040         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27041     },
27042     
27043     setText : function(str)
27044     {
27045         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27046     },
27047     
27048     setWeight : function(weight)
27049     {
27050         if(this.weight){
27051             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27052         }
27053         
27054         this.weight = weight;
27055         
27056         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27057     },
27058     
27059     setIcon : function(icon)
27060     {
27061         if(this.faicon){
27062             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27063         }
27064         
27065         this.faicon = icon;
27066         
27067         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27068     },
27069     
27070     hide: function() 
27071     {
27072         this.el.hide();   
27073     },
27074     
27075     show: function() 
27076     {  
27077         this.el.show();   
27078     }
27079     
27080 });
27081
27082  
27083 /*
27084 * Licence: LGPL
27085 */
27086
27087 /**
27088  * @class Roo.bootstrap.UploadCropbox
27089  * @extends Roo.bootstrap.Component
27090  * Bootstrap UploadCropbox class
27091  * @cfg {String} emptyText show when image has been loaded
27092  * @cfg {String} rotateNotify show when image too small to rotate
27093  * @cfg {Number} errorTimeout default 3000
27094  * @cfg {Number} minWidth default 300
27095  * @cfg {Number} minHeight default 300
27096  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27097  * @cfg {Boolean} isDocument (true|false) default false
27098  * @cfg {String} url action url
27099  * @cfg {String} paramName default 'imageUpload'
27100  * @cfg {String} method default POST
27101  * @cfg {Boolean} loadMask (true|false) default true
27102  * @cfg {Boolean} loadingText default 'Loading...'
27103  * 
27104  * @constructor
27105  * Create a new UploadCropbox
27106  * @param {Object} config The config object
27107  */
27108
27109 Roo.bootstrap.UploadCropbox = function(config){
27110     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27111     
27112     this.addEvents({
27113         /**
27114          * @event beforeselectfile
27115          * Fire before select file
27116          * @param {Roo.bootstrap.UploadCropbox} this
27117          */
27118         "beforeselectfile" : true,
27119         /**
27120          * @event initial
27121          * Fire after initEvent
27122          * @param {Roo.bootstrap.UploadCropbox} this
27123          */
27124         "initial" : true,
27125         /**
27126          * @event crop
27127          * Fire after initEvent
27128          * @param {Roo.bootstrap.UploadCropbox} this
27129          * @param {String} data
27130          */
27131         "crop" : true,
27132         /**
27133          * @event prepare
27134          * Fire when preparing the file data
27135          * @param {Roo.bootstrap.UploadCropbox} this
27136          * @param {Object} file
27137          */
27138         "prepare" : true,
27139         /**
27140          * @event exception
27141          * Fire when get exception
27142          * @param {Roo.bootstrap.UploadCropbox} this
27143          * @param {XMLHttpRequest} xhr
27144          */
27145         "exception" : true,
27146         /**
27147          * @event beforeloadcanvas
27148          * Fire before load the canvas
27149          * @param {Roo.bootstrap.UploadCropbox} this
27150          * @param {String} src
27151          */
27152         "beforeloadcanvas" : true,
27153         /**
27154          * @event trash
27155          * Fire when trash image
27156          * @param {Roo.bootstrap.UploadCropbox} this
27157          */
27158         "trash" : true,
27159         /**
27160          * @event download
27161          * Fire when download the image
27162          * @param {Roo.bootstrap.UploadCropbox} this
27163          */
27164         "download" : true,
27165         /**
27166          * @event footerbuttonclick
27167          * Fire when footerbuttonclick
27168          * @param {Roo.bootstrap.UploadCropbox} this
27169          * @param {String} type
27170          */
27171         "footerbuttonclick" : true,
27172         /**
27173          * @event resize
27174          * Fire when resize
27175          * @param {Roo.bootstrap.UploadCropbox} this
27176          */
27177         "resize" : true,
27178         /**
27179          * @event rotate
27180          * Fire when rotate the image
27181          * @param {Roo.bootstrap.UploadCropbox} this
27182          * @param {String} pos
27183          */
27184         "rotate" : true,
27185         /**
27186          * @event inspect
27187          * Fire when inspect the file
27188          * @param {Roo.bootstrap.UploadCropbox} this
27189          * @param {Object} file
27190          */
27191         "inspect" : true,
27192         /**
27193          * @event upload
27194          * Fire when xhr upload the file
27195          * @param {Roo.bootstrap.UploadCropbox} this
27196          * @param {Object} data
27197          */
27198         "upload" : true,
27199         /**
27200          * @event arrange
27201          * Fire when arrange the file data
27202          * @param {Roo.bootstrap.UploadCropbox} this
27203          * @param {Object} formData
27204          */
27205         "arrange" : true
27206     });
27207     
27208     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27209 };
27210
27211 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27212     
27213     emptyText : 'Click to upload image',
27214     rotateNotify : 'Image is too small to rotate',
27215     errorTimeout : 3000,
27216     scale : 0,
27217     baseScale : 1,
27218     rotate : 0,
27219     dragable : false,
27220     pinching : false,
27221     mouseX : 0,
27222     mouseY : 0,
27223     cropData : false,
27224     minWidth : 300,
27225     minHeight : 300,
27226     file : false,
27227     exif : {},
27228     baseRotate : 1,
27229     cropType : 'image/jpeg',
27230     buttons : false,
27231     canvasLoaded : false,
27232     isDocument : false,
27233     method : 'POST',
27234     paramName : 'imageUpload',
27235     loadMask : true,
27236     loadingText : 'Loading...',
27237     maskEl : false,
27238     
27239     getAutoCreate : function()
27240     {
27241         var cfg = {
27242             tag : 'div',
27243             cls : 'roo-upload-cropbox',
27244             cn : [
27245                 {
27246                     tag : 'input',
27247                     cls : 'roo-upload-cropbox-selector',
27248                     type : 'file'
27249                 },
27250                 {
27251                     tag : 'div',
27252                     cls : 'roo-upload-cropbox-body',
27253                     style : 'cursor:pointer',
27254                     cn : [
27255                         {
27256                             tag : 'div',
27257                             cls : 'roo-upload-cropbox-preview'
27258                         },
27259                         {
27260                             tag : 'div',
27261                             cls : 'roo-upload-cropbox-thumb'
27262                         },
27263                         {
27264                             tag : 'div',
27265                             cls : 'roo-upload-cropbox-empty-notify',
27266                             html : this.emptyText
27267                         },
27268                         {
27269                             tag : 'div',
27270                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27271                             html : this.rotateNotify
27272                         }
27273                     ]
27274                 },
27275                 {
27276                     tag : 'div',
27277                     cls : 'roo-upload-cropbox-footer',
27278                     cn : {
27279                         tag : 'div',
27280                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27281                         cn : []
27282                     }
27283                 }
27284             ]
27285         };
27286         
27287         return cfg;
27288     },
27289     
27290     onRender : function(ct, position)
27291     {
27292         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27293         
27294         if (this.buttons.length) {
27295             
27296             Roo.each(this.buttons, function(bb) {
27297                 
27298                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27299                 
27300                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27301                 
27302             }, this);
27303         }
27304         
27305         if(this.loadMask){
27306             this.maskEl = this.el;
27307         }
27308     },
27309     
27310     initEvents : function()
27311     {
27312         this.urlAPI = (window.createObjectURL && window) || 
27313                                 (window.URL && URL.revokeObjectURL && URL) || 
27314                                 (window.webkitURL && webkitURL);
27315                         
27316         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27317         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27318         
27319         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27320         this.selectorEl.hide();
27321         
27322         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27323         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27324         
27325         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27326         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27327         this.thumbEl.hide();
27328         
27329         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27330         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27331         
27332         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27333         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27334         this.errorEl.hide();
27335         
27336         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27337         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27338         this.footerEl.hide();
27339         
27340         this.setThumbBoxSize();
27341         
27342         this.bind();
27343         
27344         this.resize();
27345         
27346         this.fireEvent('initial', this);
27347     },
27348
27349     bind : function()
27350     {
27351         var _this = this;
27352         
27353         window.addEventListener("resize", function() { _this.resize(); } );
27354         
27355         this.bodyEl.on('click', this.beforeSelectFile, this);
27356         
27357         if(Roo.isTouch){
27358             this.bodyEl.on('touchstart', this.onTouchStart, this);
27359             this.bodyEl.on('touchmove', this.onTouchMove, this);
27360             this.bodyEl.on('touchend', this.onTouchEnd, this);
27361         }
27362         
27363         if(!Roo.isTouch){
27364             this.bodyEl.on('mousedown', this.onMouseDown, this);
27365             this.bodyEl.on('mousemove', this.onMouseMove, this);
27366             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27367             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27368             Roo.get(document).on('mouseup', this.onMouseUp, this);
27369         }
27370         
27371         this.selectorEl.on('change', this.onFileSelected, this);
27372     },
27373     
27374     reset : function()
27375     {    
27376         this.scale = 0;
27377         this.baseScale = 1;
27378         this.rotate = 0;
27379         this.baseRotate = 1;
27380         this.dragable = false;
27381         this.pinching = false;
27382         this.mouseX = 0;
27383         this.mouseY = 0;
27384         this.cropData = false;
27385         this.notifyEl.dom.innerHTML = this.emptyText;
27386         
27387         this.selectorEl.dom.value = '';
27388         
27389     },
27390     
27391     resize : function()
27392     {
27393         if(this.fireEvent('resize', this) != false){
27394             this.setThumbBoxPosition();
27395             this.setCanvasPosition();
27396         }
27397     },
27398     
27399     onFooterButtonClick : function(e, el, o, type)
27400     {
27401         switch (type) {
27402             case 'rotate-left' :
27403                 this.onRotateLeft(e);
27404                 break;
27405             case 'rotate-right' :
27406                 this.onRotateRight(e);
27407                 break;
27408             case 'picture' :
27409                 this.beforeSelectFile(e);
27410                 break;
27411             case 'trash' :
27412                 this.trash(e);
27413                 break;
27414             case 'crop' :
27415                 this.crop(e);
27416                 break;
27417             case 'download' :
27418                 this.download(e);
27419                 break;
27420             default :
27421                 break;
27422         }
27423         
27424         this.fireEvent('footerbuttonclick', this, type);
27425     },
27426     
27427     beforeSelectFile : function(e)
27428     {
27429         e.preventDefault();
27430         
27431         if(this.fireEvent('beforeselectfile', this) != false){
27432             this.selectorEl.dom.click();
27433         }
27434     },
27435     
27436     onFileSelected : function(e)
27437     {
27438         e.preventDefault();
27439         
27440         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27441             return;
27442         }
27443         
27444         var file = this.selectorEl.dom.files[0];
27445         
27446         if(this.fireEvent('inspect', this, file) != false){
27447             this.prepare(file);
27448         }
27449         
27450     },
27451     
27452     trash : function(e)
27453     {
27454         this.fireEvent('trash', this);
27455     },
27456     
27457     download : function(e)
27458     {
27459         this.fireEvent('download', this);
27460     },
27461     
27462     loadCanvas : function(src)
27463     {   
27464         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27465             
27466             this.reset();
27467             
27468             this.imageEl = document.createElement('img');
27469             
27470             var _this = this;
27471             
27472             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27473             
27474             this.imageEl.src = src;
27475         }
27476     },
27477     
27478     onLoadCanvas : function()
27479     {   
27480         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27481         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27482         
27483         this.bodyEl.un('click', this.beforeSelectFile, this);
27484         
27485         this.notifyEl.hide();
27486         this.thumbEl.show();
27487         this.footerEl.show();
27488         
27489         this.baseRotateLevel();
27490         
27491         if(this.isDocument){
27492             this.setThumbBoxSize();
27493         }
27494         
27495         this.setThumbBoxPosition();
27496         
27497         this.baseScaleLevel();
27498         
27499         this.draw();
27500         
27501         this.resize();
27502         
27503         this.canvasLoaded = true;
27504         
27505         if(this.loadMask){
27506             this.maskEl.unmask();
27507         }
27508         
27509     },
27510     
27511     setCanvasPosition : function()
27512     {   
27513         if(!this.canvasEl){
27514             return;
27515         }
27516         
27517         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27518         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27519         
27520         this.previewEl.setLeft(pw);
27521         this.previewEl.setTop(ph);
27522         
27523     },
27524     
27525     onMouseDown : function(e)
27526     {   
27527         e.stopEvent();
27528         
27529         this.dragable = true;
27530         this.pinching = false;
27531         
27532         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27533             this.dragable = false;
27534             return;
27535         }
27536         
27537         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27538         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27539         
27540     },
27541     
27542     onMouseMove : function(e)
27543     {   
27544         e.stopEvent();
27545         
27546         if(!this.canvasLoaded){
27547             return;
27548         }
27549         
27550         if (!this.dragable){
27551             return;
27552         }
27553         
27554         var minX = Math.ceil(this.thumbEl.getLeft(true));
27555         var minY = Math.ceil(this.thumbEl.getTop(true));
27556         
27557         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27558         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27559         
27560         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27561         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27562         
27563         x = x - this.mouseX;
27564         y = y - this.mouseY;
27565         
27566         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27567         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27568         
27569         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27570         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27571         
27572         this.previewEl.setLeft(bgX);
27573         this.previewEl.setTop(bgY);
27574         
27575         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27576         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27577     },
27578     
27579     onMouseUp : function(e)
27580     {   
27581         e.stopEvent();
27582         
27583         this.dragable = false;
27584     },
27585     
27586     onMouseWheel : function(e)
27587     {   
27588         e.stopEvent();
27589         
27590         this.startScale = this.scale;
27591         
27592         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27593         
27594         if(!this.zoomable()){
27595             this.scale = this.startScale;
27596             return;
27597         }
27598         
27599         this.draw();
27600         
27601         return;
27602     },
27603     
27604     zoomable : function()
27605     {
27606         var minScale = this.thumbEl.getWidth() / this.minWidth;
27607         
27608         if(this.minWidth < this.minHeight){
27609             minScale = this.thumbEl.getHeight() / this.minHeight;
27610         }
27611         
27612         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27613         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27614         
27615         if(
27616                 this.isDocument &&
27617                 (this.rotate == 0 || this.rotate == 180) && 
27618                 (
27619                     width > this.imageEl.OriginWidth || 
27620                     height > this.imageEl.OriginHeight ||
27621                     (width < this.minWidth && height < this.minHeight)
27622                 )
27623         ){
27624             return false;
27625         }
27626         
27627         if(
27628                 this.isDocument &&
27629                 (this.rotate == 90 || this.rotate == 270) && 
27630                 (
27631                     width > this.imageEl.OriginWidth || 
27632                     height > this.imageEl.OriginHeight ||
27633                     (width < this.minHeight && height < this.minWidth)
27634                 )
27635         ){
27636             return false;
27637         }
27638         
27639         if(
27640                 !this.isDocument &&
27641                 (this.rotate == 0 || this.rotate == 180) && 
27642                 (
27643                     width < this.minWidth || 
27644                     width > this.imageEl.OriginWidth || 
27645                     height < this.minHeight || 
27646                     height > this.imageEl.OriginHeight
27647                 )
27648         ){
27649             return false;
27650         }
27651         
27652         if(
27653                 !this.isDocument &&
27654                 (this.rotate == 90 || this.rotate == 270) && 
27655                 (
27656                     width < this.minHeight || 
27657                     width > this.imageEl.OriginWidth || 
27658                     height < this.minWidth || 
27659                     height > this.imageEl.OriginHeight
27660                 )
27661         ){
27662             return false;
27663         }
27664         
27665         return true;
27666         
27667     },
27668     
27669     onRotateLeft : function(e)
27670     {   
27671         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27672             
27673             var minScale = this.thumbEl.getWidth() / this.minWidth;
27674             
27675             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27676             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27677             
27678             this.startScale = this.scale;
27679             
27680             while (this.getScaleLevel() < minScale){
27681             
27682                 this.scale = this.scale + 1;
27683                 
27684                 if(!this.zoomable()){
27685                     break;
27686                 }
27687                 
27688                 if(
27689                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27690                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27691                 ){
27692                     continue;
27693                 }
27694                 
27695                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27696
27697                 this.draw();
27698                 
27699                 return;
27700             }
27701             
27702             this.scale = this.startScale;
27703             
27704             this.onRotateFail();
27705             
27706             return false;
27707         }
27708         
27709         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27710
27711         if(this.isDocument){
27712             this.setThumbBoxSize();
27713             this.setThumbBoxPosition();
27714             this.setCanvasPosition();
27715         }
27716         
27717         this.draw();
27718         
27719         this.fireEvent('rotate', this, 'left');
27720         
27721     },
27722     
27723     onRotateRight : function(e)
27724     {
27725         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27726             
27727             var minScale = this.thumbEl.getWidth() / this.minWidth;
27728         
27729             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27730             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27731             
27732             this.startScale = this.scale;
27733             
27734             while (this.getScaleLevel() < minScale){
27735             
27736                 this.scale = this.scale + 1;
27737                 
27738                 if(!this.zoomable()){
27739                     break;
27740                 }
27741                 
27742                 if(
27743                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27744                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27745                 ){
27746                     continue;
27747                 }
27748                 
27749                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27750
27751                 this.draw();
27752                 
27753                 return;
27754             }
27755             
27756             this.scale = this.startScale;
27757             
27758             this.onRotateFail();
27759             
27760             return false;
27761         }
27762         
27763         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27764
27765         if(this.isDocument){
27766             this.setThumbBoxSize();
27767             this.setThumbBoxPosition();
27768             this.setCanvasPosition();
27769         }
27770         
27771         this.draw();
27772         
27773         this.fireEvent('rotate', this, 'right');
27774     },
27775     
27776     onRotateFail : function()
27777     {
27778         this.errorEl.show(true);
27779         
27780         var _this = this;
27781         
27782         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27783     },
27784     
27785     draw : function()
27786     {
27787         this.previewEl.dom.innerHTML = '';
27788         
27789         var canvasEl = document.createElement("canvas");
27790         
27791         var contextEl = canvasEl.getContext("2d");
27792         
27793         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27794         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27795         var center = this.imageEl.OriginWidth / 2;
27796         
27797         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27798             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27799             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27800             center = this.imageEl.OriginHeight / 2;
27801         }
27802         
27803         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27804         
27805         contextEl.translate(center, center);
27806         contextEl.rotate(this.rotate * Math.PI / 180);
27807
27808         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27809         
27810         this.canvasEl = document.createElement("canvas");
27811         
27812         this.contextEl = this.canvasEl.getContext("2d");
27813         
27814         switch (this.rotate) {
27815             case 0 :
27816                 
27817                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27818                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27819                 
27820                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27821                 
27822                 break;
27823             case 90 : 
27824                 
27825                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27826                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27827                 
27828                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27829                     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);
27830                     break;
27831                 }
27832                 
27833                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27834                 
27835                 break;
27836             case 180 :
27837                 
27838                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27839                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27840                 
27841                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27842                     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);
27843                     break;
27844                 }
27845                 
27846                 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);
27847                 
27848                 break;
27849             case 270 :
27850                 
27851                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27852                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27853         
27854                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27855                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27856                     break;
27857                 }
27858                 
27859                 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);
27860                 
27861                 break;
27862             default : 
27863                 break;
27864         }
27865         
27866         this.previewEl.appendChild(this.canvasEl);
27867         
27868         this.setCanvasPosition();
27869     },
27870     
27871     crop : function()
27872     {
27873         if(!this.canvasLoaded){
27874             return;
27875         }
27876         
27877         var imageCanvas = document.createElement("canvas");
27878         
27879         var imageContext = imageCanvas.getContext("2d");
27880         
27881         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27882         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27883         
27884         var center = imageCanvas.width / 2;
27885         
27886         imageContext.translate(center, center);
27887         
27888         imageContext.rotate(this.rotate * Math.PI / 180);
27889         
27890         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27891         
27892         var canvas = document.createElement("canvas");
27893         
27894         var context = canvas.getContext("2d");
27895                 
27896         canvas.width = this.minWidth;
27897         canvas.height = this.minHeight;
27898
27899         switch (this.rotate) {
27900             case 0 :
27901                 
27902                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27903                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27904                 
27905                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27906                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27907                 
27908                 var targetWidth = this.minWidth - 2 * x;
27909                 var targetHeight = this.minHeight - 2 * y;
27910                 
27911                 var scale = 1;
27912                 
27913                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27914                     scale = targetWidth / width;
27915                 }
27916                 
27917                 if(x > 0 && y == 0){
27918                     scale = targetHeight / height;
27919                 }
27920                 
27921                 if(x > 0 && y > 0){
27922                     scale = targetWidth / width;
27923                     
27924                     if(width < height){
27925                         scale = targetHeight / height;
27926                     }
27927                 }
27928                 
27929                 context.scale(scale, scale);
27930                 
27931                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27932                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27933
27934                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27935                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27936
27937                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27938                 
27939                 break;
27940             case 90 : 
27941                 
27942                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27943                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27944                 
27945                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27946                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27947                 
27948                 var targetWidth = this.minWidth - 2 * x;
27949                 var targetHeight = this.minHeight - 2 * y;
27950                 
27951                 var scale = 1;
27952                 
27953                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27954                     scale = targetWidth / width;
27955                 }
27956                 
27957                 if(x > 0 && y == 0){
27958                     scale = targetHeight / height;
27959                 }
27960                 
27961                 if(x > 0 && y > 0){
27962                     scale = targetWidth / width;
27963                     
27964                     if(width < height){
27965                         scale = targetHeight / height;
27966                     }
27967                 }
27968                 
27969                 context.scale(scale, scale);
27970                 
27971                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27972                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27973
27974                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27975                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27976                 
27977                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27978                 
27979                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27980                 
27981                 break;
27982             case 180 :
27983                 
27984                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27985                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27986                 
27987                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27988                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27989                 
27990                 var targetWidth = this.minWidth - 2 * x;
27991                 var targetHeight = this.minHeight - 2 * y;
27992                 
27993                 var scale = 1;
27994                 
27995                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27996                     scale = targetWidth / width;
27997                 }
27998                 
27999                 if(x > 0 && y == 0){
28000                     scale = targetHeight / height;
28001                 }
28002                 
28003                 if(x > 0 && y > 0){
28004                     scale = targetWidth / width;
28005                     
28006                     if(width < height){
28007                         scale = targetHeight / height;
28008                     }
28009                 }
28010                 
28011                 context.scale(scale, scale);
28012                 
28013                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28014                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28015
28016                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28017                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28018
28019                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28020                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28021                 
28022                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28023                 
28024                 break;
28025             case 270 :
28026                 
28027                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28028                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28029                 
28030                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28031                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28032                 
28033                 var targetWidth = this.minWidth - 2 * x;
28034                 var targetHeight = this.minHeight - 2 * y;
28035                 
28036                 var scale = 1;
28037                 
28038                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28039                     scale = targetWidth / width;
28040                 }
28041                 
28042                 if(x > 0 && y == 0){
28043                     scale = targetHeight / height;
28044                 }
28045                 
28046                 if(x > 0 && y > 0){
28047                     scale = targetWidth / width;
28048                     
28049                     if(width < height){
28050                         scale = targetHeight / height;
28051                     }
28052                 }
28053                 
28054                 context.scale(scale, scale);
28055                 
28056                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28057                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28058
28059                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28060                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28061                 
28062                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28063                 
28064                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28065                 
28066                 break;
28067             default : 
28068                 break;
28069         }
28070         
28071         this.cropData = canvas.toDataURL(this.cropType);
28072         
28073         if(this.fireEvent('crop', this, this.cropData) !== false){
28074             this.process(this.file, this.cropData);
28075         }
28076         
28077         return;
28078         
28079     },
28080     
28081     setThumbBoxSize : function()
28082     {
28083         var width, height;
28084         
28085         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28086             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28087             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28088             
28089             this.minWidth = width;
28090             this.minHeight = height;
28091             
28092             if(this.rotate == 90 || this.rotate == 270){
28093                 this.minWidth = height;
28094                 this.minHeight = width;
28095             }
28096         }
28097         
28098         height = 300;
28099         width = Math.ceil(this.minWidth * height / this.minHeight);
28100         
28101         if(this.minWidth > this.minHeight){
28102             width = 300;
28103             height = Math.ceil(this.minHeight * width / this.minWidth);
28104         }
28105         
28106         this.thumbEl.setStyle({
28107             width : width + 'px',
28108             height : height + 'px'
28109         });
28110
28111         return;
28112             
28113     },
28114     
28115     setThumbBoxPosition : function()
28116     {
28117         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28118         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28119         
28120         this.thumbEl.setLeft(x);
28121         this.thumbEl.setTop(y);
28122         
28123     },
28124     
28125     baseRotateLevel : function()
28126     {
28127         this.baseRotate = 1;
28128         
28129         if(
28130                 typeof(this.exif) != 'undefined' &&
28131                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28132                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28133         ){
28134             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28135         }
28136         
28137         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28138         
28139     },
28140     
28141     baseScaleLevel : function()
28142     {
28143         var width, height;
28144         
28145         if(this.isDocument){
28146             
28147             if(this.baseRotate == 6 || this.baseRotate == 8){
28148             
28149                 height = this.thumbEl.getHeight();
28150                 this.baseScale = height / this.imageEl.OriginWidth;
28151
28152                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28153                     width = this.thumbEl.getWidth();
28154                     this.baseScale = width / this.imageEl.OriginHeight;
28155                 }
28156
28157                 return;
28158             }
28159
28160             height = this.thumbEl.getHeight();
28161             this.baseScale = height / this.imageEl.OriginHeight;
28162
28163             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28164                 width = this.thumbEl.getWidth();
28165                 this.baseScale = width / this.imageEl.OriginWidth;
28166             }
28167
28168             return;
28169         }
28170         
28171         if(this.baseRotate == 6 || this.baseRotate == 8){
28172             
28173             width = this.thumbEl.getHeight();
28174             this.baseScale = width / this.imageEl.OriginHeight;
28175             
28176             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28177                 height = this.thumbEl.getWidth();
28178                 this.baseScale = height / this.imageEl.OriginHeight;
28179             }
28180             
28181             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28182                 height = this.thumbEl.getWidth();
28183                 this.baseScale = height / this.imageEl.OriginHeight;
28184                 
28185                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28186                     width = this.thumbEl.getHeight();
28187                     this.baseScale = width / this.imageEl.OriginWidth;
28188                 }
28189             }
28190             
28191             return;
28192         }
28193         
28194         width = this.thumbEl.getWidth();
28195         this.baseScale = width / this.imageEl.OriginWidth;
28196         
28197         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28198             height = this.thumbEl.getHeight();
28199             this.baseScale = height / this.imageEl.OriginHeight;
28200         }
28201         
28202         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28203             
28204             height = this.thumbEl.getHeight();
28205             this.baseScale = height / this.imageEl.OriginHeight;
28206             
28207             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28208                 width = this.thumbEl.getWidth();
28209                 this.baseScale = width / this.imageEl.OriginWidth;
28210             }
28211             
28212         }
28213         
28214         return;
28215     },
28216     
28217     getScaleLevel : function()
28218     {
28219         return this.baseScale * Math.pow(1.1, this.scale);
28220     },
28221     
28222     onTouchStart : function(e)
28223     {
28224         if(!this.canvasLoaded){
28225             this.beforeSelectFile(e);
28226             return;
28227         }
28228         
28229         var touches = e.browserEvent.touches;
28230         
28231         if(!touches){
28232             return;
28233         }
28234         
28235         if(touches.length == 1){
28236             this.onMouseDown(e);
28237             return;
28238         }
28239         
28240         if(touches.length != 2){
28241             return;
28242         }
28243         
28244         var coords = [];
28245         
28246         for(var i = 0, finger; finger = touches[i]; i++){
28247             coords.push(finger.pageX, finger.pageY);
28248         }
28249         
28250         var x = Math.pow(coords[0] - coords[2], 2);
28251         var y = Math.pow(coords[1] - coords[3], 2);
28252         
28253         this.startDistance = Math.sqrt(x + y);
28254         
28255         this.startScale = this.scale;
28256         
28257         this.pinching = true;
28258         this.dragable = false;
28259         
28260     },
28261     
28262     onTouchMove : function(e)
28263     {
28264         if(!this.pinching && !this.dragable){
28265             return;
28266         }
28267         
28268         var touches = e.browserEvent.touches;
28269         
28270         if(!touches){
28271             return;
28272         }
28273         
28274         if(this.dragable){
28275             this.onMouseMove(e);
28276             return;
28277         }
28278         
28279         var coords = [];
28280         
28281         for(var i = 0, finger; finger = touches[i]; i++){
28282             coords.push(finger.pageX, finger.pageY);
28283         }
28284         
28285         var x = Math.pow(coords[0] - coords[2], 2);
28286         var y = Math.pow(coords[1] - coords[3], 2);
28287         
28288         this.endDistance = Math.sqrt(x + y);
28289         
28290         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28291         
28292         if(!this.zoomable()){
28293             this.scale = this.startScale;
28294             return;
28295         }
28296         
28297         this.draw();
28298         
28299     },
28300     
28301     onTouchEnd : function(e)
28302     {
28303         this.pinching = false;
28304         this.dragable = false;
28305         
28306     },
28307     
28308     process : function(file, crop)
28309     {
28310         if(this.loadMask){
28311             this.maskEl.mask(this.loadingText);
28312         }
28313         
28314         this.xhr = new XMLHttpRequest();
28315         
28316         file.xhr = this.xhr;
28317
28318         this.xhr.open(this.method, this.url, true);
28319         
28320         var headers = {
28321             "Accept": "application/json",
28322             "Cache-Control": "no-cache",
28323             "X-Requested-With": "XMLHttpRequest"
28324         };
28325         
28326         for (var headerName in headers) {
28327             var headerValue = headers[headerName];
28328             if (headerValue) {
28329                 this.xhr.setRequestHeader(headerName, headerValue);
28330             }
28331         }
28332         
28333         var _this = this;
28334         
28335         this.xhr.onload = function()
28336         {
28337             _this.xhrOnLoad(_this.xhr);
28338         }
28339         
28340         this.xhr.onerror = function()
28341         {
28342             _this.xhrOnError(_this.xhr);
28343         }
28344         
28345         var formData = new FormData();
28346
28347         formData.append('returnHTML', 'NO');
28348         
28349         if(crop){
28350             formData.append('crop', crop);
28351         }
28352         
28353         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28354             formData.append(this.paramName, file, file.name);
28355         }
28356         
28357         if(typeof(file.filename) != 'undefined'){
28358             formData.append('filename', file.filename);
28359         }
28360         
28361         if(typeof(file.mimetype) != 'undefined'){
28362             formData.append('mimetype', file.mimetype);
28363         }
28364         
28365         if(this.fireEvent('arrange', this, formData) != false){
28366             this.xhr.send(formData);
28367         };
28368     },
28369     
28370     xhrOnLoad : function(xhr)
28371     {
28372         if(this.loadMask){
28373             this.maskEl.unmask();
28374         }
28375         
28376         if (xhr.readyState !== 4) {
28377             this.fireEvent('exception', this, xhr);
28378             return;
28379         }
28380
28381         var response = Roo.decode(xhr.responseText);
28382         
28383         if(!response.success){
28384             this.fireEvent('exception', this, xhr);
28385             return;
28386         }
28387         
28388         var response = Roo.decode(xhr.responseText);
28389         
28390         this.fireEvent('upload', this, response);
28391         
28392     },
28393     
28394     xhrOnError : function()
28395     {
28396         if(this.loadMask){
28397             this.maskEl.unmask();
28398         }
28399         
28400         Roo.log('xhr on error');
28401         
28402         var response = Roo.decode(xhr.responseText);
28403           
28404         Roo.log(response);
28405         
28406     },
28407     
28408     prepare : function(file)
28409     {   
28410         if(this.loadMask){
28411             this.maskEl.mask(this.loadingText);
28412         }
28413         
28414         this.file = false;
28415         this.exif = {};
28416         
28417         if(typeof(file) === 'string'){
28418             this.loadCanvas(file);
28419             return;
28420         }
28421         
28422         if(!file || !this.urlAPI){
28423             return;
28424         }
28425         
28426         this.file = file;
28427         this.cropType = file.type;
28428         
28429         var _this = this;
28430         
28431         if(this.fireEvent('prepare', this, this.file) != false){
28432             
28433             var reader = new FileReader();
28434             
28435             reader.onload = function (e) {
28436                 if (e.target.error) {
28437                     Roo.log(e.target.error);
28438                     return;
28439                 }
28440                 
28441                 var buffer = e.target.result,
28442                     dataView = new DataView(buffer),
28443                     offset = 2,
28444                     maxOffset = dataView.byteLength - 4,
28445                     markerBytes,
28446                     markerLength;
28447                 
28448                 if (dataView.getUint16(0) === 0xffd8) {
28449                     while (offset < maxOffset) {
28450                         markerBytes = dataView.getUint16(offset);
28451                         
28452                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28453                             markerLength = dataView.getUint16(offset + 2) + 2;
28454                             if (offset + markerLength > dataView.byteLength) {
28455                                 Roo.log('Invalid meta data: Invalid segment size.');
28456                                 break;
28457                             }
28458                             
28459                             if(markerBytes == 0xffe1){
28460                                 _this.parseExifData(
28461                                     dataView,
28462                                     offset,
28463                                     markerLength
28464                                 );
28465                             }
28466                             
28467                             offset += markerLength;
28468                             
28469                             continue;
28470                         }
28471                         
28472                         break;
28473                     }
28474                     
28475                 }
28476                 
28477                 var url = _this.urlAPI.createObjectURL(_this.file);
28478                 
28479                 _this.loadCanvas(url);
28480                 
28481                 return;
28482             }
28483             
28484             reader.readAsArrayBuffer(this.file);
28485             
28486         }
28487         
28488     },
28489     
28490     parseExifData : function(dataView, offset, length)
28491     {
28492         var tiffOffset = offset + 10,
28493             littleEndian,
28494             dirOffset;
28495     
28496         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28497             // No Exif data, might be XMP data instead
28498             return;
28499         }
28500         
28501         // Check for the ASCII code for "Exif" (0x45786966):
28502         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28503             // No Exif data, might be XMP data instead
28504             return;
28505         }
28506         if (tiffOffset + 8 > dataView.byteLength) {
28507             Roo.log('Invalid Exif data: Invalid segment size.');
28508             return;
28509         }
28510         // Check for the two null bytes:
28511         if (dataView.getUint16(offset + 8) !== 0x0000) {
28512             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28513             return;
28514         }
28515         // Check the byte alignment:
28516         switch (dataView.getUint16(tiffOffset)) {
28517         case 0x4949:
28518             littleEndian = true;
28519             break;
28520         case 0x4D4D:
28521             littleEndian = false;
28522             break;
28523         default:
28524             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28525             return;
28526         }
28527         // Check for the TIFF tag marker (0x002A):
28528         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28529             Roo.log('Invalid Exif data: Missing TIFF marker.');
28530             return;
28531         }
28532         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28533         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28534         
28535         this.parseExifTags(
28536             dataView,
28537             tiffOffset,
28538             tiffOffset + dirOffset,
28539             littleEndian
28540         );
28541     },
28542     
28543     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28544     {
28545         var tagsNumber,
28546             dirEndOffset,
28547             i;
28548         if (dirOffset + 6 > dataView.byteLength) {
28549             Roo.log('Invalid Exif data: Invalid directory offset.');
28550             return;
28551         }
28552         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28553         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28554         if (dirEndOffset + 4 > dataView.byteLength) {
28555             Roo.log('Invalid Exif data: Invalid directory size.');
28556             return;
28557         }
28558         for (i = 0; i < tagsNumber; i += 1) {
28559             this.parseExifTag(
28560                 dataView,
28561                 tiffOffset,
28562                 dirOffset + 2 + 12 * i, // tag offset
28563                 littleEndian
28564             );
28565         }
28566         // Return the offset to the next directory:
28567         return dataView.getUint32(dirEndOffset, littleEndian);
28568     },
28569     
28570     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28571     {
28572         var tag = dataView.getUint16(offset, littleEndian);
28573         
28574         this.exif[tag] = this.getExifValue(
28575             dataView,
28576             tiffOffset,
28577             offset,
28578             dataView.getUint16(offset + 2, littleEndian), // tag type
28579             dataView.getUint32(offset + 4, littleEndian), // tag length
28580             littleEndian
28581         );
28582     },
28583     
28584     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28585     {
28586         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28587             tagSize,
28588             dataOffset,
28589             values,
28590             i,
28591             str,
28592             c;
28593     
28594         if (!tagType) {
28595             Roo.log('Invalid Exif data: Invalid tag type.');
28596             return;
28597         }
28598         
28599         tagSize = tagType.size * length;
28600         // Determine if the value is contained in the dataOffset bytes,
28601         // or if the value at the dataOffset is a pointer to the actual data:
28602         dataOffset = tagSize > 4 ?
28603                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28604         if (dataOffset + tagSize > dataView.byteLength) {
28605             Roo.log('Invalid Exif data: Invalid data offset.');
28606             return;
28607         }
28608         if (length === 1) {
28609             return tagType.getValue(dataView, dataOffset, littleEndian);
28610         }
28611         values = [];
28612         for (i = 0; i < length; i += 1) {
28613             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28614         }
28615         
28616         if (tagType.ascii) {
28617             str = '';
28618             // Concatenate the chars:
28619             for (i = 0; i < values.length; i += 1) {
28620                 c = values[i];
28621                 // Ignore the terminating NULL byte(s):
28622                 if (c === '\u0000') {
28623                     break;
28624                 }
28625                 str += c;
28626             }
28627             return str;
28628         }
28629         return values;
28630     }
28631     
28632 });
28633
28634 Roo.apply(Roo.bootstrap.UploadCropbox, {
28635     tags : {
28636         'Orientation': 0x0112
28637     },
28638     
28639     Orientation: {
28640             1: 0, //'top-left',
28641 //            2: 'top-right',
28642             3: 180, //'bottom-right',
28643 //            4: 'bottom-left',
28644 //            5: 'left-top',
28645             6: 90, //'right-top',
28646 //            7: 'right-bottom',
28647             8: 270 //'left-bottom'
28648     },
28649     
28650     exifTagTypes : {
28651         // byte, 8-bit unsigned int:
28652         1: {
28653             getValue: function (dataView, dataOffset) {
28654                 return dataView.getUint8(dataOffset);
28655             },
28656             size: 1
28657         },
28658         // ascii, 8-bit byte:
28659         2: {
28660             getValue: function (dataView, dataOffset) {
28661                 return String.fromCharCode(dataView.getUint8(dataOffset));
28662             },
28663             size: 1,
28664             ascii: true
28665         },
28666         // short, 16 bit int:
28667         3: {
28668             getValue: function (dataView, dataOffset, littleEndian) {
28669                 return dataView.getUint16(dataOffset, littleEndian);
28670             },
28671             size: 2
28672         },
28673         // long, 32 bit int:
28674         4: {
28675             getValue: function (dataView, dataOffset, littleEndian) {
28676                 return dataView.getUint32(dataOffset, littleEndian);
28677             },
28678             size: 4
28679         },
28680         // rational = two long values, first is numerator, second is denominator:
28681         5: {
28682             getValue: function (dataView, dataOffset, littleEndian) {
28683                 return dataView.getUint32(dataOffset, littleEndian) /
28684                     dataView.getUint32(dataOffset + 4, littleEndian);
28685             },
28686             size: 8
28687         },
28688         // slong, 32 bit signed int:
28689         9: {
28690             getValue: function (dataView, dataOffset, littleEndian) {
28691                 return dataView.getInt32(dataOffset, littleEndian);
28692             },
28693             size: 4
28694         },
28695         // srational, two slongs, first is numerator, second is denominator:
28696         10: {
28697             getValue: function (dataView, dataOffset, littleEndian) {
28698                 return dataView.getInt32(dataOffset, littleEndian) /
28699                     dataView.getInt32(dataOffset + 4, littleEndian);
28700             },
28701             size: 8
28702         }
28703     },
28704     
28705     footer : {
28706         STANDARD : [
28707             {
28708                 tag : 'div',
28709                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28710                 action : 'rotate-left',
28711                 cn : [
28712                     {
28713                         tag : 'button',
28714                         cls : 'btn btn-default',
28715                         html : '<i class="fa fa-undo"></i>'
28716                     }
28717                 ]
28718             },
28719             {
28720                 tag : 'div',
28721                 cls : 'btn-group roo-upload-cropbox-picture',
28722                 action : 'picture',
28723                 cn : [
28724                     {
28725                         tag : 'button',
28726                         cls : 'btn btn-default',
28727                         html : '<i class="fa fa-picture-o"></i>'
28728                     }
28729                 ]
28730             },
28731             {
28732                 tag : 'div',
28733                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28734                 action : 'rotate-right',
28735                 cn : [
28736                     {
28737                         tag : 'button',
28738                         cls : 'btn btn-default',
28739                         html : '<i class="fa fa-repeat"></i>'
28740                     }
28741                 ]
28742             }
28743         ],
28744         DOCUMENT : [
28745             {
28746                 tag : 'div',
28747                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28748                 action : 'rotate-left',
28749                 cn : [
28750                     {
28751                         tag : 'button',
28752                         cls : 'btn btn-default',
28753                         html : '<i class="fa fa-undo"></i>'
28754                     }
28755                 ]
28756             },
28757             {
28758                 tag : 'div',
28759                 cls : 'btn-group roo-upload-cropbox-download',
28760                 action : 'download',
28761                 cn : [
28762                     {
28763                         tag : 'button',
28764                         cls : 'btn btn-default',
28765                         html : '<i class="fa fa-download"></i>'
28766                     }
28767                 ]
28768             },
28769             {
28770                 tag : 'div',
28771                 cls : 'btn-group roo-upload-cropbox-crop',
28772                 action : 'crop',
28773                 cn : [
28774                     {
28775                         tag : 'button',
28776                         cls : 'btn btn-default',
28777                         html : '<i class="fa fa-crop"></i>'
28778                     }
28779                 ]
28780             },
28781             {
28782                 tag : 'div',
28783                 cls : 'btn-group roo-upload-cropbox-trash',
28784                 action : 'trash',
28785                 cn : [
28786                     {
28787                         tag : 'button',
28788                         cls : 'btn btn-default',
28789                         html : '<i class="fa fa-trash"></i>'
28790                     }
28791                 ]
28792             },
28793             {
28794                 tag : 'div',
28795                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28796                 action : 'rotate-right',
28797                 cn : [
28798                     {
28799                         tag : 'button',
28800                         cls : 'btn btn-default',
28801                         html : '<i class="fa fa-repeat"></i>'
28802                     }
28803                 ]
28804             }
28805         ],
28806         ROTATOR : [
28807             {
28808                 tag : 'div',
28809                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28810                 action : 'rotate-left',
28811                 cn : [
28812                     {
28813                         tag : 'button',
28814                         cls : 'btn btn-default',
28815                         html : '<i class="fa fa-undo"></i>'
28816                     }
28817                 ]
28818             },
28819             {
28820                 tag : 'div',
28821                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28822                 action : 'rotate-right',
28823                 cn : [
28824                     {
28825                         tag : 'button',
28826                         cls : 'btn btn-default',
28827                         html : '<i class="fa fa-repeat"></i>'
28828                     }
28829                 ]
28830             }
28831         ]
28832     }
28833 });
28834
28835 /*
28836 * Licence: LGPL
28837 */
28838
28839 /**
28840  * @class Roo.bootstrap.DocumentManager
28841  * @extends Roo.bootstrap.Component
28842  * Bootstrap DocumentManager class
28843  * @cfg {String} paramName default 'imageUpload'
28844  * @cfg {String} toolTipName default 'filename'
28845  * @cfg {String} method default POST
28846  * @cfg {String} url action url
28847  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28848  * @cfg {Boolean} multiple multiple upload default true
28849  * @cfg {Number} thumbSize default 300
28850  * @cfg {String} fieldLabel
28851  * @cfg {Number} labelWidth default 4
28852  * @cfg {String} labelAlign (left|top) default left
28853  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28854 * @cfg {Number} labellg set the width of label (1-12)
28855  * @cfg {Number} labelmd set the width of label (1-12)
28856  * @cfg {Number} labelsm set the width of label (1-12)
28857  * @cfg {Number} labelxs set the width of label (1-12)
28858  * 
28859  * @constructor
28860  * Create a new DocumentManager
28861  * @param {Object} config The config object
28862  */
28863
28864 Roo.bootstrap.DocumentManager = function(config){
28865     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28866     
28867     this.files = [];
28868     this.delegates = [];
28869     
28870     this.addEvents({
28871         /**
28872          * @event initial
28873          * Fire when initial the DocumentManager
28874          * @param {Roo.bootstrap.DocumentManager} this
28875          */
28876         "initial" : true,
28877         /**
28878          * @event inspect
28879          * inspect selected file
28880          * @param {Roo.bootstrap.DocumentManager} this
28881          * @param {File} file
28882          */
28883         "inspect" : true,
28884         /**
28885          * @event exception
28886          * Fire when xhr load exception
28887          * @param {Roo.bootstrap.DocumentManager} this
28888          * @param {XMLHttpRequest} xhr
28889          */
28890         "exception" : true,
28891         /**
28892          * @event afterupload
28893          * Fire when xhr load exception
28894          * @param {Roo.bootstrap.DocumentManager} this
28895          * @param {XMLHttpRequest} xhr
28896          */
28897         "afterupload" : true,
28898         /**
28899          * @event prepare
28900          * prepare the form data
28901          * @param {Roo.bootstrap.DocumentManager} this
28902          * @param {Object} formData
28903          */
28904         "prepare" : true,
28905         /**
28906          * @event remove
28907          * Fire when remove the file
28908          * @param {Roo.bootstrap.DocumentManager} this
28909          * @param {Object} file
28910          */
28911         "remove" : true,
28912         /**
28913          * @event refresh
28914          * Fire after refresh the file
28915          * @param {Roo.bootstrap.DocumentManager} this
28916          */
28917         "refresh" : true,
28918         /**
28919          * @event click
28920          * Fire after click the image
28921          * @param {Roo.bootstrap.DocumentManager} this
28922          * @param {Object} file
28923          */
28924         "click" : true,
28925         /**
28926          * @event edit
28927          * Fire when upload a image and editable set to true
28928          * @param {Roo.bootstrap.DocumentManager} this
28929          * @param {Object} file
28930          */
28931         "edit" : true,
28932         /**
28933          * @event beforeselectfile
28934          * Fire before select file
28935          * @param {Roo.bootstrap.DocumentManager} this
28936          */
28937         "beforeselectfile" : true,
28938         /**
28939          * @event process
28940          * Fire before process file
28941          * @param {Roo.bootstrap.DocumentManager} this
28942          * @param {Object} file
28943          */
28944         "process" : true,
28945         /**
28946          * @event previewrendered
28947          * Fire when preview rendered
28948          * @param {Roo.bootstrap.DocumentManager} this
28949          * @param {Object} file
28950          */
28951         "previewrendered" : true,
28952         /**
28953          */
28954         "previewResize" : true
28955         
28956     });
28957 };
28958
28959 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28960     
28961     boxes : 0,
28962     inputName : '',
28963     thumbSize : 300,
28964     multiple : true,
28965     files : false,
28966     method : 'POST',
28967     url : '',
28968     paramName : 'imageUpload',
28969     toolTipName : 'filename',
28970     fieldLabel : '',
28971     labelWidth : 4,
28972     labelAlign : 'left',
28973     editable : true,
28974     delegates : false,
28975     xhr : false, 
28976     
28977     labellg : 0,
28978     labelmd : 0,
28979     labelsm : 0,
28980     labelxs : 0,
28981     
28982     getAutoCreate : function()
28983     {   
28984         var managerWidget = {
28985             tag : 'div',
28986             cls : 'roo-document-manager',
28987             cn : [
28988                 {
28989                     tag : 'input',
28990                     cls : 'roo-document-manager-selector',
28991                     type : 'file'
28992                 },
28993                 {
28994                     tag : 'div',
28995                     cls : 'roo-document-manager-uploader',
28996                     cn : [
28997                         {
28998                             tag : 'div',
28999                             cls : 'roo-document-manager-upload-btn',
29000                             html : '<i class="fa fa-plus"></i>'
29001                         }
29002                     ]
29003                     
29004                 }
29005             ]
29006         };
29007         
29008         var content = [
29009             {
29010                 tag : 'div',
29011                 cls : 'column col-md-12',
29012                 cn : managerWidget
29013             }
29014         ];
29015         
29016         if(this.fieldLabel.length){
29017             
29018             content = [
29019                 {
29020                     tag : 'div',
29021                     cls : 'column col-md-12',
29022                     html : this.fieldLabel
29023                 },
29024                 {
29025                     tag : 'div',
29026                     cls : 'column col-md-12',
29027                     cn : managerWidget
29028                 }
29029             ];
29030
29031             if(this.labelAlign == 'left'){
29032                 content = [
29033                     {
29034                         tag : 'div',
29035                         cls : 'column',
29036                         html : this.fieldLabel
29037                     },
29038                     {
29039                         tag : 'div',
29040                         cls : 'column',
29041                         cn : managerWidget
29042                     }
29043                 ];
29044                 
29045                 if(this.labelWidth > 12){
29046                     content[0].style = "width: " + this.labelWidth + 'px';
29047                 }
29048
29049                 if(this.labelWidth < 13 && this.labelmd == 0){
29050                     this.labelmd = this.labelWidth;
29051                 }
29052
29053                 if(this.labellg > 0){
29054                     content[0].cls += ' col-lg-' + this.labellg;
29055                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29056                 }
29057
29058                 if(this.labelmd > 0){
29059                     content[0].cls += ' col-md-' + this.labelmd;
29060                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29061                 }
29062
29063                 if(this.labelsm > 0){
29064                     content[0].cls += ' col-sm-' + this.labelsm;
29065                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29066                 }
29067
29068                 if(this.labelxs > 0){
29069                     content[0].cls += ' col-xs-' + this.labelxs;
29070                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29071                 }
29072                 
29073             }
29074         }
29075         
29076         var cfg = {
29077             tag : 'div',
29078             cls : 'row clearfix',
29079             cn : content
29080         };
29081         
29082         return cfg;
29083         
29084     },
29085     
29086     initEvents : function()
29087     {
29088         this.managerEl = this.el.select('.roo-document-manager', true).first();
29089         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29090         
29091         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29092         this.selectorEl.hide();
29093         
29094         if(this.multiple){
29095             this.selectorEl.attr('multiple', 'multiple');
29096         }
29097         
29098         this.selectorEl.on('change', this.onFileSelected, this);
29099         
29100         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29101         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29102         
29103         this.uploader.on('click', this.onUploaderClick, this);
29104         
29105         this.renderProgressDialog();
29106         
29107         var _this = this;
29108         
29109         window.addEventListener("resize", function() { _this.refresh(); } );
29110         
29111         this.fireEvent('initial', this);
29112     },
29113     
29114     renderProgressDialog : function()
29115     {
29116         var _this = this;
29117         
29118         this.progressDialog = new Roo.bootstrap.Modal({
29119             cls : 'roo-document-manager-progress-dialog',
29120             allow_close : false,
29121             title : '',
29122             buttons : [
29123                 {
29124                     name  :'cancel',
29125                     weight : 'danger',
29126                     html : 'Cancel'
29127                 }
29128             ], 
29129             listeners : { 
29130                 btnclick : function() {
29131                     _this.uploadCancel();
29132                     this.hide();
29133                 }
29134             }
29135         });
29136          
29137         this.progressDialog.render(Roo.get(document.body));
29138          
29139         this.progress = new Roo.bootstrap.Progress({
29140             cls : 'roo-document-manager-progress',
29141             active : true,
29142             striped : true
29143         });
29144         
29145         this.progress.render(this.progressDialog.getChildContainer());
29146         
29147         this.progressBar = new Roo.bootstrap.ProgressBar({
29148             cls : 'roo-document-manager-progress-bar',
29149             aria_valuenow : 0,
29150             aria_valuemin : 0,
29151             aria_valuemax : 12,
29152             panel : 'success'
29153         });
29154         
29155         this.progressBar.render(this.progress.getChildContainer());
29156     },
29157     
29158     onUploaderClick : function(e)
29159     {
29160         e.preventDefault();
29161      
29162         if(this.fireEvent('beforeselectfile', this) != false){
29163             this.selectorEl.dom.click();
29164         }
29165         
29166     },
29167     
29168     onFileSelected : function(e)
29169     {
29170         e.preventDefault();
29171         
29172         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29173             return;
29174         }
29175         
29176         Roo.each(this.selectorEl.dom.files, function(file){
29177             if(this.fireEvent('inspect', this, file) != false){
29178                 this.files.push(file);
29179             }
29180         }, this);
29181         
29182         this.queue();
29183         
29184     },
29185     
29186     queue : function()
29187     {
29188         this.selectorEl.dom.value = '';
29189         
29190         if(!this.files || !this.files.length){
29191             return;
29192         }
29193         
29194         if(this.boxes > 0 && this.files.length > this.boxes){
29195             this.files = this.files.slice(0, this.boxes);
29196         }
29197         
29198         this.uploader.show();
29199         
29200         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29201             this.uploader.hide();
29202         }
29203         
29204         var _this = this;
29205         
29206         var files = [];
29207         
29208         var docs = [];
29209         
29210         Roo.each(this.files, function(file){
29211             
29212             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29213                 var f = this.renderPreview(file);
29214                 files.push(f);
29215                 return;
29216             }
29217             
29218             if(file.type.indexOf('image') != -1){
29219                 this.delegates.push(
29220                     (function(){
29221                         _this.process(file);
29222                     }).createDelegate(this)
29223                 );
29224         
29225                 return;
29226             }
29227             
29228             docs.push(
29229                 (function(){
29230                     _this.process(file);
29231                 }).createDelegate(this)
29232             );
29233             
29234         }, this);
29235         
29236         this.files = files;
29237         
29238         this.delegates = this.delegates.concat(docs);
29239         
29240         if(!this.delegates.length){
29241             this.refresh();
29242             return;
29243         }
29244         
29245         this.progressBar.aria_valuemax = this.delegates.length;
29246         
29247         this.arrange();
29248         
29249         return;
29250     },
29251     
29252     arrange : function()
29253     {
29254         if(!this.delegates.length){
29255             this.progressDialog.hide();
29256             this.refresh();
29257             return;
29258         }
29259         
29260         var delegate = this.delegates.shift();
29261         
29262         this.progressDialog.show();
29263         
29264         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29265         
29266         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29267         
29268         delegate();
29269     },
29270     
29271     refresh : function()
29272     {
29273         this.uploader.show();
29274         
29275         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29276             this.uploader.hide();
29277         }
29278         
29279         Roo.isTouch ? this.closable(false) : this.closable(true);
29280         
29281         this.fireEvent('refresh', this);
29282     },
29283     
29284     onRemove : function(e, el, o)
29285     {
29286         e.preventDefault();
29287         
29288         this.fireEvent('remove', this, o);
29289         
29290     },
29291     
29292     remove : function(o)
29293     {
29294         var files = [];
29295         
29296         Roo.each(this.files, function(file){
29297             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29298                 files.push(file);
29299                 return;
29300             }
29301
29302             o.target.remove();
29303
29304         }, this);
29305         
29306         this.files = files;
29307         
29308         this.refresh();
29309     },
29310     
29311     clear : function()
29312     {
29313         Roo.each(this.files, function(file){
29314             if(!file.target){
29315                 return;
29316             }
29317             
29318             file.target.remove();
29319
29320         }, this);
29321         
29322         this.files = [];
29323         
29324         this.refresh();
29325     },
29326     
29327     onClick : function(e, el, o)
29328     {
29329         e.preventDefault();
29330         
29331         this.fireEvent('click', this, o);
29332         
29333     },
29334     
29335     closable : function(closable)
29336     {
29337         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29338             
29339             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29340             
29341             if(closable){
29342                 el.show();
29343                 return;
29344             }
29345             
29346             el.hide();
29347             
29348         }, this);
29349     },
29350     
29351     xhrOnLoad : function(xhr)
29352     {
29353         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29354             el.remove();
29355         }, this);
29356         
29357         if (xhr.readyState !== 4) {
29358             this.arrange();
29359             this.fireEvent('exception', this, xhr);
29360             return;
29361         }
29362
29363         var response = Roo.decode(xhr.responseText);
29364         
29365         if(!response.success){
29366             this.arrange();
29367             this.fireEvent('exception', this, xhr);
29368             return;
29369         }
29370         
29371         var file = this.renderPreview(response.data);
29372         
29373         this.files.push(file);
29374         
29375         this.arrange();
29376         
29377         this.fireEvent('afterupload', this, xhr);
29378         
29379     },
29380     
29381     xhrOnError : function(xhr)
29382     {
29383         Roo.log('xhr on error');
29384         
29385         var response = Roo.decode(xhr.responseText);
29386           
29387         Roo.log(response);
29388         
29389         this.arrange();
29390     },
29391     
29392     process : function(file)
29393     {
29394         if(this.fireEvent('process', this, file) !== false){
29395             if(this.editable && file.type.indexOf('image') != -1){
29396                 this.fireEvent('edit', this, file);
29397                 return;
29398             }
29399
29400             this.uploadStart(file, false);
29401
29402             return;
29403         }
29404         
29405     },
29406     
29407     uploadStart : function(file, crop)
29408     {
29409         this.xhr = new XMLHttpRequest();
29410         
29411         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29412             this.arrange();
29413             return;
29414         }
29415         
29416         file.xhr = this.xhr;
29417             
29418         this.managerEl.createChild({
29419             tag : 'div',
29420             cls : 'roo-document-manager-loading',
29421             cn : [
29422                 {
29423                     tag : 'div',
29424                     tooltip : file.name,
29425                     cls : 'roo-document-manager-thumb',
29426                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29427                 }
29428             ]
29429
29430         });
29431
29432         this.xhr.open(this.method, this.url, true);
29433         
29434         var headers = {
29435             "Accept": "application/json",
29436             "Cache-Control": "no-cache",
29437             "X-Requested-With": "XMLHttpRequest"
29438         };
29439         
29440         for (var headerName in headers) {
29441             var headerValue = headers[headerName];
29442             if (headerValue) {
29443                 this.xhr.setRequestHeader(headerName, headerValue);
29444             }
29445         }
29446         
29447         var _this = this;
29448         
29449         this.xhr.onload = function()
29450         {
29451             _this.xhrOnLoad(_this.xhr);
29452         }
29453         
29454         this.xhr.onerror = function()
29455         {
29456             _this.xhrOnError(_this.xhr);
29457         }
29458         
29459         var formData = new FormData();
29460
29461         formData.append('returnHTML', 'NO');
29462         
29463         if(crop){
29464             formData.append('crop', crop);
29465         }
29466         
29467         formData.append(this.paramName, file, file.name);
29468         
29469         var options = {
29470             file : file, 
29471             manually : false
29472         };
29473         
29474         if(this.fireEvent('prepare', this, formData, options) != false){
29475             
29476             if(options.manually){
29477                 return;
29478             }
29479             
29480             this.xhr.send(formData);
29481             return;
29482         };
29483         
29484         this.uploadCancel();
29485     },
29486     
29487     uploadCancel : function()
29488     {
29489         if (this.xhr) {
29490             this.xhr.abort();
29491         }
29492         
29493         this.delegates = [];
29494         
29495         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29496             el.remove();
29497         }, this);
29498         
29499         this.arrange();
29500     },
29501     
29502     renderPreview : function(file)
29503     {
29504         if(typeof(file.target) != 'undefined' && file.target){
29505             return file;
29506         }
29507         
29508         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29509         
29510         var previewEl = this.managerEl.createChild({
29511             tag : 'div',
29512             cls : 'roo-document-manager-preview',
29513             cn : [
29514                 {
29515                     tag : 'div',
29516                     tooltip : file[this.toolTipName],
29517                     cls : 'roo-document-manager-thumb',
29518                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29519                 },
29520                 {
29521                     tag : 'button',
29522                     cls : 'close',
29523                     html : '<i class="fa fa-times-circle"></i>'
29524                 }
29525             ]
29526         });
29527
29528         var close = previewEl.select('button.close', true).first();
29529
29530         close.on('click', this.onRemove, this, file);
29531
29532         file.target = previewEl;
29533
29534         var image = previewEl.select('img', true).first();
29535         
29536         var _this = this;
29537         
29538         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29539         
29540         image.on('click', this.onClick, this, file);
29541         
29542         this.fireEvent('previewrendered', this, file);
29543         
29544         return file;
29545         
29546     },
29547     
29548     onPreviewLoad : function(file, image)
29549     {
29550         if(typeof(file.target) == 'undefined' || !file.target){
29551             return;
29552         }
29553         
29554         var width = image.dom.naturalWidth || image.dom.width;
29555         var height = image.dom.naturalHeight || image.dom.height;
29556         
29557         if(!this.previewResize) {
29558             return;
29559         }
29560         
29561         if(width > height){
29562             file.target.addClass('wide');
29563             return;
29564         }
29565         
29566         file.target.addClass('tall');
29567         return;
29568         
29569     },
29570     
29571     uploadFromSource : function(file, crop)
29572     {
29573         this.xhr = new XMLHttpRequest();
29574         
29575         this.managerEl.createChild({
29576             tag : 'div',
29577             cls : 'roo-document-manager-loading',
29578             cn : [
29579                 {
29580                     tag : 'div',
29581                     tooltip : file.name,
29582                     cls : 'roo-document-manager-thumb',
29583                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29584                 }
29585             ]
29586
29587         });
29588
29589         this.xhr.open(this.method, this.url, true);
29590         
29591         var headers = {
29592             "Accept": "application/json",
29593             "Cache-Control": "no-cache",
29594             "X-Requested-With": "XMLHttpRequest"
29595         };
29596         
29597         for (var headerName in headers) {
29598             var headerValue = headers[headerName];
29599             if (headerValue) {
29600                 this.xhr.setRequestHeader(headerName, headerValue);
29601             }
29602         }
29603         
29604         var _this = this;
29605         
29606         this.xhr.onload = function()
29607         {
29608             _this.xhrOnLoad(_this.xhr);
29609         }
29610         
29611         this.xhr.onerror = function()
29612         {
29613             _this.xhrOnError(_this.xhr);
29614         }
29615         
29616         var formData = new FormData();
29617
29618         formData.append('returnHTML', 'NO');
29619         
29620         formData.append('crop', crop);
29621         
29622         if(typeof(file.filename) != 'undefined'){
29623             formData.append('filename', file.filename);
29624         }
29625         
29626         if(typeof(file.mimetype) != 'undefined'){
29627             formData.append('mimetype', file.mimetype);
29628         }
29629         
29630         Roo.log(formData);
29631         
29632         if(this.fireEvent('prepare', this, formData) != false){
29633             this.xhr.send(formData);
29634         };
29635     }
29636 });
29637
29638 /*
29639 * Licence: LGPL
29640 */
29641
29642 /**
29643  * @class Roo.bootstrap.DocumentViewer
29644  * @extends Roo.bootstrap.Component
29645  * Bootstrap DocumentViewer class
29646  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29647  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29648  * 
29649  * @constructor
29650  * Create a new DocumentViewer
29651  * @param {Object} config The config object
29652  */
29653
29654 Roo.bootstrap.DocumentViewer = function(config){
29655     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29656     
29657     this.addEvents({
29658         /**
29659          * @event initial
29660          * Fire after initEvent
29661          * @param {Roo.bootstrap.DocumentViewer} this
29662          */
29663         "initial" : true,
29664         /**
29665          * @event click
29666          * Fire after click
29667          * @param {Roo.bootstrap.DocumentViewer} this
29668          */
29669         "click" : true,
29670         /**
29671          * @event download
29672          * Fire after download button
29673          * @param {Roo.bootstrap.DocumentViewer} this
29674          */
29675         "download" : true,
29676         /**
29677          * @event trash
29678          * Fire after trash button
29679          * @param {Roo.bootstrap.DocumentViewer} this
29680          */
29681         "trash" : true
29682         
29683     });
29684 };
29685
29686 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29687     
29688     showDownload : true,
29689     
29690     showTrash : true,
29691     
29692     getAutoCreate : function()
29693     {
29694         var cfg = {
29695             tag : 'div',
29696             cls : 'roo-document-viewer',
29697             cn : [
29698                 {
29699                     tag : 'div',
29700                     cls : 'roo-document-viewer-body',
29701                     cn : [
29702                         {
29703                             tag : 'div',
29704                             cls : 'roo-document-viewer-thumb',
29705                             cn : [
29706                                 {
29707                                     tag : 'img',
29708                                     cls : 'roo-document-viewer-image'
29709                                 }
29710                             ]
29711                         }
29712                     ]
29713                 },
29714                 {
29715                     tag : 'div',
29716                     cls : 'roo-document-viewer-footer',
29717                     cn : {
29718                         tag : 'div',
29719                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29720                         cn : [
29721                             {
29722                                 tag : 'div',
29723                                 cls : 'btn-group roo-document-viewer-download',
29724                                 cn : [
29725                                     {
29726                                         tag : 'button',
29727                                         cls : 'btn btn-default',
29728                                         html : '<i class="fa fa-download"></i>'
29729                                     }
29730                                 ]
29731                             },
29732                             {
29733                                 tag : 'div',
29734                                 cls : 'btn-group roo-document-viewer-trash',
29735                                 cn : [
29736                                     {
29737                                         tag : 'button',
29738                                         cls : 'btn btn-default',
29739                                         html : '<i class="fa fa-trash"></i>'
29740                                     }
29741                                 ]
29742                             }
29743                         ]
29744                     }
29745                 }
29746             ]
29747         };
29748         
29749         return cfg;
29750     },
29751     
29752     initEvents : function()
29753     {
29754         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29755         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29756         
29757         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29758         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29759         
29760         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29761         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29762         
29763         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29764         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29765         
29766         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29767         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29768         
29769         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29770         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29771         
29772         this.bodyEl.on('click', this.onClick, this);
29773         this.downloadBtn.on('click', this.onDownload, this);
29774         this.trashBtn.on('click', this.onTrash, this);
29775         
29776         this.downloadBtn.hide();
29777         this.trashBtn.hide();
29778         
29779         if(this.showDownload){
29780             this.downloadBtn.show();
29781         }
29782         
29783         if(this.showTrash){
29784             this.trashBtn.show();
29785         }
29786         
29787         if(!this.showDownload && !this.showTrash) {
29788             this.footerEl.hide();
29789         }
29790         
29791     },
29792     
29793     initial : function()
29794     {
29795         this.fireEvent('initial', this);
29796         
29797     },
29798     
29799     onClick : function(e)
29800     {
29801         e.preventDefault();
29802         
29803         this.fireEvent('click', this);
29804     },
29805     
29806     onDownload : function(e)
29807     {
29808         e.preventDefault();
29809         
29810         this.fireEvent('download', this);
29811     },
29812     
29813     onTrash : function(e)
29814     {
29815         e.preventDefault();
29816         
29817         this.fireEvent('trash', this);
29818     }
29819     
29820 });
29821 /*
29822  * - LGPL
29823  *
29824  * nav progress bar
29825  * 
29826  */
29827
29828 /**
29829  * @class Roo.bootstrap.NavProgressBar
29830  * @extends Roo.bootstrap.Component
29831  * Bootstrap NavProgressBar class
29832  * 
29833  * @constructor
29834  * Create a new nav progress bar
29835  * @param {Object} config The config object
29836  */
29837
29838 Roo.bootstrap.NavProgressBar = function(config){
29839     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29840
29841     this.bullets = this.bullets || [];
29842    
29843 //    Roo.bootstrap.NavProgressBar.register(this);
29844      this.addEvents({
29845         /**
29846              * @event changed
29847              * Fires when the active item changes
29848              * @param {Roo.bootstrap.NavProgressBar} this
29849              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29850              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29851          */
29852         'changed': true
29853      });
29854     
29855 };
29856
29857 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29858     
29859     bullets : [],
29860     barItems : [],
29861     
29862     getAutoCreate : function()
29863     {
29864         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29865         
29866         cfg = {
29867             tag : 'div',
29868             cls : 'roo-navigation-bar-group',
29869             cn : [
29870                 {
29871                     tag : 'div',
29872                     cls : 'roo-navigation-top-bar'
29873                 },
29874                 {
29875                     tag : 'div',
29876                     cls : 'roo-navigation-bullets-bar',
29877                     cn : [
29878                         {
29879                             tag : 'ul',
29880                             cls : 'roo-navigation-bar'
29881                         }
29882                     ]
29883                 },
29884                 
29885                 {
29886                     tag : 'div',
29887                     cls : 'roo-navigation-bottom-bar'
29888                 }
29889             ]
29890             
29891         };
29892         
29893         return cfg;
29894         
29895     },
29896     
29897     initEvents: function() 
29898     {
29899         
29900     },
29901     
29902     onRender : function(ct, position) 
29903     {
29904         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29905         
29906         if(this.bullets.length){
29907             Roo.each(this.bullets, function(b){
29908                this.addItem(b);
29909             }, this);
29910         }
29911         
29912         this.format();
29913         
29914     },
29915     
29916     addItem : function(cfg)
29917     {
29918         var item = new Roo.bootstrap.NavProgressItem(cfg);
29919         
29920         item.parentId = this.id;
29921         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29922         
29923         if(cfg.html){
29924             var top = new Roo.bootstrap.Element({
29925                 tag : 'div',
29926                 cls : 'roo-navigation-bar-text'
29927             });
29928             
29929             var bottom = new Roo.bootstrap.Element({
29930                 tag : 'div',
29931                 cls : 'roo-navigation-bar-text'
29932             });
29933             
29934             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29935             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29936             
29937             var topText = new Roo.bootstrap.Element({
29938                 tag : 'span',
29939                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29940             });
29941             
29942             var bottomText = new Roo.bootstrap.Element({
29943                 tag : 'span',
29944                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29945             });
29946             
29947             topText.onRender(top.el, null);
29948             bottomText.onRender(bottom.el, null);
29949             
29950             item.topEl = top;
29951             item.bottomEl = bottom;
29952         }
29953         
29954         this.barItems.push(item);
29955         
29956         return item;
29957     },
29958     
29959     getActive : function()
29960     {
29961         var active = false;
29962         
29963         Roo.each(this.barItems, function(v){
29964             
29965             if (!v.isActive()) {
29966                 return;
29967             }
29968             
29969             active = v;
29970             return false;
29971             
29972         });
29973         
29974         return active;
29975     },
29976     
29977     setActiveItem : function(item)
29978     {
29979         var prev = false;
29980         
29981         Roo.each(this.barItems, function(v){
29982             if (v.rid == item.rid) {
29983                 return ;
29984             }
29985             
29986             if (v.isActive()) {
29987                 v.setActive(false);
29988                 prev = v;
29989             }
29990         });
29991
29992         item.setActive(true);
29993         
29994         this.fireEvent('changed', this, item, prev);
29995     },
29996     
29997     getBarItem: function(rid)
29998     {
29999         var ret = false;
30000         
30001         Roo.each(this.barItems, function(e) {
30002             if (e.rid != rid) {
30003                 return;
30004             }
30005             
30006             ret =  e;
30007             return false;
30008         });
30009         
30010         return ret;
30011     },
30012     
30013     indexOfItem : function(item)
30014     {
30015         var index = false;
30016         
30017         Roo.each(this.barItems, function(v, i){
30018             
30019             if (v.rid != item.rid) {
30020                 return;
30021             }
30022             
30023             index = i;
30024             return false
30025         });
30026         
30027         return index;
30028     },
30029     
30030     setActiveNext : function()
30031     {
30032         var i = this.indexOfItem(this.getActive());
30033         
30034         if (i > this.barItems.length) {
30035             return;
30036         }
30037         
30038         this.setActiveItem(this.barItems[i+1]);
30039     },
30040     
30041     setActivePrev : function()
30042     {
30043         var i = this.indexOfItem(this.getActive());
30044         
30045         if (i  < 1) {
30046             return;
30047         }
30048         
30049         this.setActiveItem(this.barItems[i-1]);
30050     },
30051     
30052     format : function()
30053     {
30054         if(!this.barItems.length){
30055             return;
30056         }
30057      
30058         var width = 100 / this.barItems.length;
30059         
30060         Roo.each(this.barItems, function(i){
30061             i.el.setStyle('width', width + '%');
30062             i.topEl.el.setStyle('width', width + '%');
30063             i.bottomEl.el.setStyle('width', width + '%');
30064         }, this);
30065         
30066     }
30067     
30068 });
30069 /*
30070  * - LGPL
30071  *
30072  * Nav Progress Item
30073  * 
30074  */
30075
30076 /**
30077  * @class Roo.bootstrap.NavProgressItem
30078  * @extends Roo.bootstrap.Component
30079  * Bootstrap NavProgressItem class
30080  * @cfg {String} rid the reference id
30081  * @cfg {Boolean} active (true|false) Is item active default false
30082  * @cfg {Boolean} disabled (true|false) Is item active default false
30083  * @cfg {String} html
30084  * @cfg {String} position (top|bottom) text position default bottom
30085  * @cfg {String} icon show icon instead of number
30086  * 
30087  * @constructor
30088  * Create a new NavProgressItem
30089  * @param {Object} config The config object
30090  */
30091 Roo.bootstrap.NavProgressItem = function(config){
30092     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30093     this.addEvents({
30094         // raw events
30095         /**
30096          * @event click
30097          * The raw click event for the entire grid.
30098          * @param {Roo.bootstrap.NavProgressItem} this
30099          * @param {Roo.EventObject} e
30100          */
30101         "click" : true
30102     });
30103    
30104 };
30105
30106 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30107     
30108     rid : '',
30109     active : false,
30110     disabled : false,
30111     html : '',
30112     position : 'bottom',
30113     icon : false,
30114     
30115     getAutoCreate : function()
30116     {
30117         var iconCls = 'roo-navigation-bar-item-icon';
30118         
30119         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30120         
30121         var cfg = {
30122             tag: 'li',
30123             cls: 'roo-navigation-bar-item',
30124             cn : [
30125                 {
30126                     tag : 'i',
30127                     cls : iconCls
30128                 }
30129             ]
30130         };
30131         
30132         if(this.active){
30133             cfg.cls += ' active';
30134         }
30135         if(this.disabled){
30136             cfg.cls += ' disabled';
30137         }
30138         
30139         return cfg;
30140     },
30141     
30142     disable : function()
30143     {
30144         this.setDisabled(true);
30145     },
30146     
30147     enable : function()
30148     {
30149         this.setDisabled(false);
30150     },
30151     
30152     initEvents: function() 
30153     {
30154         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30155         
30156         this.iconEl.on('click', this.onClick, this);
30157     },
30158     
30159     onClick : function(e)
30160     {
30161         e.preventDefault();
30162         
30163         if(this.disabled){
30164             return;
30165         }
30166         
30167         if(this.fireEvent('click', this, e) === false){
30168             return;
30169         };
30170         
30171         this.parent().setActiveItem(this);
30172     },
30173     
30174     isActive: function () 
30175     {
30176         return this.active;
30177     },
30178     
30179     setActive : function(state)
30180     {
30181         if(this.active == state){
30182             return;
30183         }
30184         
30185         this.active = state;
30186         
30187         if (state) {
30188             this.el.addClass('active');
30189             return;
30190         }
30191         
30192         this.el.removeClass('active');
30193         
30194         return;
30195     },
30196     
30197     setDisabled : function(state)
30198     {
30199         if(this.disabled == state){
30200             return;
30201         }
30202         
30203         this.disabled = state;
30204         
30205         if (state) {
30206             this.el.addClass('disabled');
30207             return;
30208         }
30209         
30210         this.el.removeClass('disabled');
30211     },
30212     
30213     tooltipEl : function()
30214     {
30215         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30216     }
30217 });
30218  
30219
30220  /*
30221  * - LGPL
30222  *
30223  * FieldLabel
30224  * 
30225  */
30226
30227 /**
30228  * @class Roo.bootstrap.FieldLabel
30229  * @extends Roo.bootstrap.Component
30230  * Bootstrap FieldLabel class
30231  * @cfg {String} html contents of the element
30232  * @cfg {String} tag tag of the element default label
30233  * @cfg {String} cls class of the element
30234  * @cfg {String} target label target 
30235  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30236  * @cfg {String} invalidClass default "text-warning"
30237  * @cfg {String} validClass default "text-success"
30238  * @cfg {String} iconTooltip default "This field is required"
30239  * @cfg {String} indicatorpos (left|right) default left
30240  * 
30241  * @constructor
30242  * Create a new FieldLabel
30243  * @param {Object} config The config object
30244  */
30245
30246 Roo.bootstrap.FieldLabel = function(config){
30247     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30248     
30249     this.addEvents({
30250             /**
30251              * @event invalid
30252              * Fires after the field has been marked as invalid.
30253              * @param {Roo.form.FieldLabel} this
30254              * @param {String} msg The validation message
30255              */
30256             invalid : true,
30257             /**
30258              * @event valid
30259              * Fires after the field has been validated with no errors.
30260              * @param {Roo.form.FieldLabel} this
30261              */
30262             valid : true
30263         });
30264 };
30265
30266 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30267     
30268     tag: 'label',
30269     cls: '',
30270     html: '',
30271     target: '',
30272     allowBlank : true,
30273     invalidClass : 'has-warning',
30274     validClass : 'has-success',
30275     iconTooltip : 'This field is required',
30276     indicatorpos : 'left',
30277     
30278     getAutoCreate : function(){
30279         
30280         var cls = "";
30281         if (!this.allowBlank) {
30282             cls  = "visible";
30283         }
30284         
30285         var cfg = {
30286             tag : this.tag,
30287             cls : 'roo-bootstrap-field-label ' + this.cls,
30288             for : this.target,
30289             cn : [
30290                 {
30291                     tag : 'i',
30292                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30293                     tooltip : this.iconTooltip
30294                 },
30295                 {
30296                     tag : 'span',
30297                     html : this.html
30298                 }
30299             ] 
30300         };
30301         
30302         if(this.indicatorpos == 'right'){
30303             var cfg = {
30304                 tag : this.tag,
30305                 cls : 'roo-bootstrap-field-label ' + this.cls,
30306                 for : this.target,
30307                 cn : [
30308                     {
30309                         tag : 'span',
30310                         html : this.html
30311                     },
30312                     {
30313                         tag : 'i',
30314                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30315                         tooltip : this.iconTooltip
30316                     }
30317                 ] 
30318             };
30319         }
30320         
30321         return cfg;
30322     },
30323     
30324     initEvents: function() 
30325     {
30326         Roo.bootstrap.Element.superclass.initEvents.call(this);
30327         
30328         this.indicator = this.indicatorEl();
30329         
30330         if(this.indicator){
30331             this.indicator.removeClass('visible');
30332             this.indicator.addClass('invisible');
30333         }
30334         
30335         Roo.bootstrap.FieldLabel.register(this);
30336     },
30337     
30338     indicatorEl : function()
30339     {
30340         var indicator = this.el.select('i.roo-required-indicator',true).first();
30341         
30342         if(!indicator){
30343             return false;
30344         }
30345         
30346         return indicator;
30347         
30348     },
30349     
30350     /**
30351      * Mark this field as valid
30352      */
30353     markValid : function()
30354     {
30355         if(this.indicator){
30356             this.indicator.removeClass('visible');
30357             this.indicator.addClass('invisible');
30358         }
30359         
30360         this.el.removeClass(this.invalidClass);
30361         
30362         this.el.addClass(this.validClass);
30363         
30364         this.fireEvent('valid', this);
30365     },
30366     
30367     /**
30368      * Mark this field as invalid
30369      * @param {String} msg The validation message
30370      */
30371     markInvalid : function(msg)
30372     {
30373         if(this.indicator){
30374             this.indicator.removeClass('invisible');
30375             this.indicator.addClass('visible');
30376         }
30377         
30378         this.el.removeClass(this.validClass);
30379         
30380         this.el.addClass(this.invalidClass);
30381         
30382         this.fireEvent('invalid', this, msg);
30383     }
30384     
30385    
30386 });
30387
30388 Roo.apply(Roo.bootstrap.FieldLabel, {
30389     
30390     groups: {},
30391     
30392      /**
30393     * register a FieldLabel Group
30394     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30395     */
30396     register : function(label)
30397     {
30398         if(this.groups.hasOwnProperty(label.target)){
30399             return;
30400         }
30401      
30402         this.groups[label.target] = label;
30403         
30404     },
30405     /**
30406     * fetch a FieldLabel Group based on the target
30407     * @param {string} target
30408     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30409     */
30410     get: function(target) {
30411         if (typeof(this.groups[target]) == 'undefined') {
30412             return false;
30413         }
30414         
30415         return this.groups[target] ;
30416     }
30417 });
30418
30419  
30420
30421  /*
30422  * - LGPL
30423  *
30424  * page DateSplitField.
30425  * 
30426  */
30427
30428
30429 /**
30430  * @class Roo.bootstrap.DateSplitField
30431  * @extends Roo.bootstrap.Component
30432  * Bootstrap DateSplitField class
30433  * @cfg {string} fieldLabel - the label associated
30434  * @cfg {Number} labelWidth set the width of label (0-12)
30435  * @cfg {String} labelAlign (top|left)
30436  * @cfg {Boolean} dayAllowBlank (true|false) default false
30437  * @cfg {Boolean} monthAllowBlank (true|false) default false
30438  * @cfg {Boolean} yearAllowBlank (true|false) default false
30439  * @cfg {string} dayPlaceholder 
30440  * @cfg {string} monthPlaceholder
30441  * @cfg {string} yearPlaceholder
30442  * @cfg {string} dayFormat default 'd'
30443  * @cfg {string} monthFormat default 'm'
30444  * @cfg {string} yearFormat default 'Y'
30445  * @cfg {Number} labellg set the width of label (1-12)
30446  * @cfg {Number} labelmd set the width of label (1-12)
30447  * @cfg {Number} labelsm set the width of label (1-12)
30448  * @cfg {Number} labelxs set the width of label (1-12)
30449
30450  *     
30451  * @constructor
30452  * Create a new DateSplitField
30453  * @param {Object} config The config object
30454  */
30455
30456 Roo.bootstrap.DateSplitField = function(config){
30457     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30458     
30459     this.addEvents({
30460         // raw events
30461          /**
30462          * @event years
30463          * getting the data of years
30464          * @param {Roo.bootstrap.DateSplitField} this
30465          * @param {Object} years
30466          */
30467         "years" : true,
30468         /**
30469          * @event days
30470          * getting the data of days
30471          * @param {Roo.bootstrap.DateSplitField} this
30472          * @param {Object} days
30473          */
30474         "days" : true,
30475         /**
30476          * @event invalid
30477          * Fires after the field has been marked as invalid.
30478          * @param {Roo.form.Field} this
30479          * @param {String} msg The validation message
30480          */
30481         invalid : true,
30482        /**
30483          * @event valid
30484          * Fires after the field has been validated with no errors.
30485          * @param {Roo.form.Field} this
30486          */
30487         valid : true
30488     });
30489 };
30490
30491 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30492     
30493     fieldLabel : '',
30494     labelAlign : 'top',
30495     labelWidth : 3,
30496     dayAllowBlank : false,
30497     monthAllowBlank : false,
30498     yearAllowBlank : false,
30499     dayPlaceholder : '',
30500     monthPlaceholder : '',
30501     yearPlaceholder : '',
30502     dayFormat : 'd',
30503     monthFormat : 'm',
30504     yearFormat : 'Y',
30505     isFormField : true,
30506     labellg : 0,
30507     labelmd : 0,
30508     labelsm : 0,
30509     labelxs : 0,
30510     
30511     getAutoCreate : function()
30512     {
30513         var cfg = {
30514             tag : 'div',
30515             cls : 'row roo-date-split-field-group',
30516             cn : [
30517                 {
30518                     tag : 'input',
30519                     type : 'hidden',
30520                     cls : 'form-hidden-field roo-date-split-field-group-value',
30521                     name : this.name
30522                 }
30523             ]
30524         };
30525         
30526         var labelCls = 'col-md-12';
30527         var contentCls = 'col-md-4';
30528         
30529         if(this.fieldLabel){
30530             
30531             var label = {
30532                 tag : 'div',
30533                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30534                 cn : [
30535                     {
30536                         tag : 'label',
30537                         html : this.fieldLabel
30538                     }
30539                 ]
30540             };
30541             
30542             if(this.labelAlign == 'left'){
30543             
30544                 if(this.labelWidth > 12){
30545                     label.style = "width: " + this.labelWidth + 'px';
30546                 }
30547
30548                 if(this.labelWidth < 13 && this.labelmd == 0){
30549                     this.labelmd = this.labelWidth;
30550                 }
30551
30552                 if(this.labellg > 0){
30553                     labelCls = ' col-lg-' + this.labellg;
30554                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30555                 }
30556
30557                 if(this.labelmd > 0){
30558                     labelCls = ' col-md-' + this.labelmd;
30559                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30560                 }
30561
30562                 if(this.labelsm > 0){
30563                     labelCls = ' col-sm-' + this.labelsm;
30564                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30565                 }
30566
30567                 if(this.labelxs > 0){
30568                     labelCls = ' col-xs-' + this.labelxs;
30569                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30570                 }
30571             }
30572             
30573             label.cls += ' ' + labelCls;
30574             
30575             cfg.cn.push(label);
30576         }
30577         
30578         Roo.each(['day', 'month', 'year'], function(t){
30579             cfg.cn.push({
30580                 tag : 'div',
30581                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30582             });
30583         }, this);
30584         
30585         return cfg;
30586     },
30587     
30588     inputEl: function ()
30589     {
30590         return this.el.select('.roo-date-split-field-group-value', true).first();
30591     },
30592     
30593     onRender : function(ct, position) 
30594     {
30595         var _this = this;
30596         
30597         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30598         
30599         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30600         
30601         this.dayField = new Roo.bootstrap.ComboBox({
30602             allowBlank : this.dayAllowBlank,
30603             alwaysQuery : true,
30604             displayField : 'value',
30605             editable : false,
30606             fieldLabel : '',
30607             forceSelection : true,
30608             mode : 'local',
30609             placeholder : this.dayPlaceholder,
30610             selectOnFocus : true,
30611             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30612             triggerAction : 'all',
30613             typeAhead : true,
30614             valueField : 'value',
30615             store : new Roo.data.SimpleStore({
30616                 data : (function() {    
30617                     var days = [];
30618                     _this.fireEvent('days', _this, days);
30619                     return days;
30620                 })(),
30621                 fields : [ 'value' ]
30622             }),
30623             listeners : {
30624                 select : function (_self, record, index)
30625                 {
30626                     _this.setValue(_this.getValue());
30627                 }
30628             }
30629         });
30630
30631         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30632         
30633         this.monthField = new Roo.bootstrap.MonthField({
30634             after : '<i class=\"fa fa-calendar\"></i>',
30635             allowBlank : this.monthAllowBlank,
30636             placeholder : this.monthPlaceholder,
30637             readOnly : true,
30638             listeners : {
30639                 render : function (_self)
30640                 {
30641                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30642                         e.preventDefault();
30643                         _self.focus();
30644                     });
30645                 },
30646                 select : function (_self, oldvalue, newvalue)
30647                 {
30648                     _this.setValue(_this.getValue());
30649                 }
30650             }
30651         });
30652         
30653         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30654         
30655         this.yearField = new Roo.bootstrap.ComboBox({
30656             allowBlank : this.yearAllowBlank,
30657             alwaysQuery : true,
30658             displayField : 'value',
30659             editable : false,
30660             fieldLabel : '',
30661             forceSelection : true,
30662             mode : 'local',
30663             placeholder : this.yearPlaceholder,
30664             selectOnFocus : true,
30665             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30666             triggerAction : 'all',
30667             typeAhead : true,
30668             valueField : 'value',
30669             store : new Roo.data.SimpleStore({
30670                 data : (function() {
30671                     var years = [];
30672                     _this.fireEvent('years', _this, years);
30673                     return years;
30674                 })(),
30675                 fields : [ 'value' ]
30676             }),
30677             listeners : {
30678                 select : function (_self, record, index)
30679                 {
30680                     _this.setValue(_this.getValue());
30681                 }
30682             }
30683         });
30684
30685         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30686     },
30687     
30688     setValue : function(v, format)
30689     {
30690         this.inputEl.dom.value = v;
30691         
30692         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30693         
30694         var d = Date.parseDate(v, f);
30695         
30696         if(!d){
30697             this.validate();
30698             return;
30699         }
30700         
30701         this.setDay(d.format(this.dayFormat));
30702         this.setMonth(d.format(this.monthFormat));
30703         this.setYear(d.format(this.yearFormat));
30704         
30705         this.validate();
30706         
30707         return;
30708     },
30709     
30710     setDay : function(v)
30711     {
30712         this.dayField.setValue(v);
30713         this.inputEl.dom.value = this.getValue();
30714         this.validate();
30715         return;
30716     },
30717     
30718     setMonth : function(v)
30719     {
30720         this.monthField.setValue(v, true);
30721         this.inputEl.dom.value = this.getValue();
30722         this.validate();
30723         return;
30724     },
30725     
30726     setYear : function(v)
30727     {
30728         this.yearField.setValue(v);
30729         this.inputEl.dom.value = this.getValue();
30730         this.validate();
30731         return;
30732     },
30733     
30734     getDay : function()
30735     {
30736         return this.dayField.getValue();
30737     },
30738     
30739     getMonth : function()
30740     {
30741         return this.monthField.getValue();
30742     },
30743     
30744     getYear : function()
30745     {
30746         return this.yearField.getValue();
30747     },
30748     
30749     getValue : function()
30750     {
30751         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30752         
30753         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30754         
30755         return date;
30756     },
30757     
30758     reset : function()
30759     {
30760         this.setDay('');
30761         this.setMonth('');
30762         this.setYear('');
30763         this.inputEl.dom.value = '';
30764         this.validate();
30765         return;
30766     },
30767     
30768     validate : function()
30769     {
30770         var d = this.dayField.validate();
30771         var m = this.monthField.validate();
30772         var y = this.yearField.validate();
30773         
30774         var valid = true;
30775         
30776         if(
30777                 (!this.dayAllowBlank && !d) ||
30778                 (!this.monthAllowBlank && !m) ||
30779                 (!this.yearAllowBlank && !y)
30780         ){
30781             valid = false;
30782         }
30783         
30784         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30785             return valid;
30786         }
30787         
30788         if(valid){
30789             this.markValid();
30790             return valid;
30791         }
30792         
30793         this.markInvalid();
30794         
30795         return valid;
30796     },
30797     
30798     markValid : function()
30799     {
30800         
30801         var label = this.el.select('label', true).first();
30802         var icon = this.el.select('i.fa-star', true).first();
30803
30804         if(label && icon){
30805             icon.remove();
30806         }
30807         
30808         this.fireEvent('valid', this);
30809     },
30810     
30811      /**
30812      * Mark this field as invalid
30813      * @param {String} msg The validation message
30814      */
30815     markInvalid : function(msg)
30816     {
30817         
30818         var label = this.el.select('label', true).first();
30819         var icon = this.el.select('i.fa-star', true).first();
30820
30821         if(label && !icon){
30822             this.el.select('.roo-date-split-field-label', true).createChild({
30823                 tag : 'i',
30824                 cls : 'text-danger fa fa-lg fa-star',
30825                 tooltip : 'This field is required',
30826                 style : 'margin-right:5px;'
30827             }, label, true);
30828         }
30829         
30830         this.fireEvent('invalid', this, msg);
30831     },
30832     
30833     clearInvalid : function()
30834     {
30835         var label = this.el.select('label', true).first();
30836         var icon = this.el.select('i.fa-star', true).first();
30837
30838         if(label && icon){
30839             icon.remove();
30840         }
30841         
30842         this.fireEvent('valid', this);
30843     },
30844     
30845     getName: function()
30846     {
30847         return this.name;
30848     }
30849     
30850 });
30851
30852  /**
30853  *
30854  * This is based on 
30855  * http://masonry.desandro.com
30856  *
30857  * The idea is to render all the bricks based on vertical width...
30858  *
30859  * The original code extends 'outlayer' - we might need to use that....
30860  * 
30861  */
30862
30863
30864 /**
30865  * @class Roo.bootstrap.LayoutMasonry
30866  * @extends Roo.bootstrap.Component
30867  * Bootstrap Layout Masonry class
30868  * 
30869  * @constructor
30870  * Create a new Element
30871  * @param {Object} config The config object
30872  */
30873
30874 Roo.bootstrap.LayoutMasonry = function(config){
30875     
30876     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30877     
30878     this.bricks = [];
30879     
30880     Roo.bootstrap.LayoutMasonry.register(this);
30881     
30882     this.addEvents({
30883         // raw events
30884         /**
30885          * @event layout
30886          * Fire after layout the items
30887          * @param {Roo.bootstrap.LayoutMasonry} this
30888          * @param {Roo.EventObject} e
30889          */
30890         "layout" : true
30891     });
30892     
30893 };
30894
30895 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30896     
30897     /**
30898      * @cfg {Boolean} isLayoutInstant = no animation?
30899      */   
30900     isLayoutInstant : false, // needed?
30901    
30902     /**
30903      * @cfg {Number} boxWidth  width of the columns
30904      */   
30905     boxWidth : 450,
30906     
30907       /**
30908      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30909      */   
30910     boxHeight : 0,
30911     
30912     /**
30913      * @cfg {Number} padWidth padding below box..
30914      */   
30915     padWidth : 10, 
30916     
30917     /**
30918      * @cfg {Number} gutter gutter width..
30919      */   
30920     gutter : 10,
30921     
30922      /**
30923      * @cfg {Number} maxCols maximum number of columns
30924      */   
30925     
30926     maxCols: 0,
30927     
30928     /**
30929      * @cfg {Boolean} isAutoInitial defalut true
30930      */   
30931     isAutoInitial : true, 
30932     
30933     containerWidth: 0,
30934     
30935     /**
30936      * @cfg {Boolean} isHorizontal defalut false
30937      */   
30938     isHorizontal : false, 
30939
30940     currentSize : null,
30941     
30942     tag: 'div',
30943     
30944     cls: '',
30945     
30946     bricks: null, //CompositeElement
30947     
30948     cols : 1,
30949     
30950     _isLayoutInited : false,
30951     
30952 //    isAlternative : false, // only use for vertical layout...
30953     
30954     /**
30955      * @cfg {Number} alternativePadWidth padding below box..
30956      */   
30957     alternativePadWidth : 50,
30958     
30959     selectedBrick : [],
30960     
30961     getAutoCreate : function(){
30962         
30963         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30964         
30965         var cfg = {
30966             tag: this.tag,
30967             cls: 'blog-masonary-wrapper ' + this.cls,
30968             cn : {
30969                 cls : 'mas-boxes masonary'
30970             }
30971         };
30972         
30973         return cfg;
30974     },
30975     
30976     getChildContainer: function( )
30977     {
30978         if (this.boxesEl) {
30979             return this.boxesEl;
30980         }
30981         
30982         this.boxesEl = this.el.select('.mas-boxes').first();
30983         
30984         return this.boxesEl;
30985     },
30986     
30987     
30988     initEvents : function()
30989     {
30990         var _this = this;
30991         
30992         if(this.isAutoInitial){
30993             Roo.log('hook children rendered');
30994             this.on('childrenrendered', function() {
30995                 Roo.log('children rendered');
30996                 _this.initial();
30997             } ,this);
30998         }
30999     },
31000     
31001     initial : function()
31002     {
31003         this.selectedBrick = [];
31004         
31005         this.currentSize = this.el.getBox(true);
31006         
31007         Roo.EventManager.onWindowResize(this.resize, this); 
31008
31009         if(!this.isAutoInitial){
31010             this.layout();
31011             return;
31012         }
31013         
31014         this.layout();
31015         
31016         return;
31017         //this.layout.defer(500,this);
31018         
31019     },
31020     
31021     resize : function()
31022     {
31023         var cs = this.el.getBox(true);
31024         
31025         if (
31026                 this.currentSize.width == cs.width && 
31027                 this.currentSize.x == cs.x && 
31028                 this.currentSize.height == cs.height && 
31029                 this.currentSize.y == cs.y 
31030         ) {
31031             Roo.log("no change in with or X or Y");
31032             return;
31033         }
31034         
31035         this.currentSize = cs;
31036         
31037         this.layout();
31038         
31039     },
31040     
31041     layout : function()
31042     {   
31043         this._resetLayout();
31044         
31045         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31046         
31047         this.layoutItems( isInstant );
31048       
31049         this._isLayoutInited = true;
31050         
31051         this.fireEvent('layout', this);
31052         
31053     },
31054     
31055     _resetLayout : function()
31056     {
31057         if(this.isHorizontal){
31058             this.horizontalMeasureColumns();
31059             return;
31060         }
31061         
31062         this.verticalMeasureColumns();
31063         
31064     },
31065     
31066     verticalMeasureColumns : function()
31067     {
31068         this.getContainerWidth();
31069         
31070 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31071 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31072 //            return;
31073 //        }
31074         
31075         var boxWidth = this.boxWidth + this.padWidth;
31076         
31077         if(this.containerWidth < this.boxWidth){
31078             boxWidth = this.containerWidth
31079         }
31080         
31081         var containerWidth = this.containerWidth;
31082         
31083         var cols = Math.floor(containerWidth / boxWidth);
31084         
31085         this.cols = Math.max( cols, 1 );
31086         
31087         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31088         
31089         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31090         
31091         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31092         
31093         this.colWidth = boxWidth + avail - this.padWidth;
31094         
31095         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31096         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31097     },
31098     
31099     horizontalMeasureColumns : function()
31100     {
31101         this.getContainerWidth();
31102         
31103         var boxWidth = this.boxWidth;
31104         
31105         if(this.containerWidth < boxWidth){
31106             boxWidth = this.containerWidth;
31107         }
31108         
31109         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31110         
31111         this.el.setHeight(boxWidth);
31112         
31113     },
31114     
31115     getContainerWidth : function()
31116     {
31117         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31118     },
31119     
31120     layoutItems : function( isInstant )
31121     {
31122         Roo.log(this.bricks);
31123         
31124         var items = Roo.apply([], this.bricks);
31125         
31126         if(this.isHorizontal){
31127             this._horizontalLayoutItems( items , isInstant );
31128             return;
31129         }
31130         
31131 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31132 //            this._verticalAlternativeLayoutItems( items , isInstant );
31133 //            return;
31134 //        }
31135         
31136         this._verticalLayoutItems( items , isInstant );
31137         
31138     },
31139     
31140     _verticalLayoutItems : function ( items , isInstant)
31141     {
31142         if ( !items || !items.length ) {
31143             return;
31144         }
31145         
31146         var standard = [
31147             ['xs', 'xs', 'xs', 'tall'],
31148             ['xs', 'xs', 'tall'],
31149             ['xs', 'xs', 'sm'],
31150             ['xs', 'xs', 'xs'],
31151             ['xs', 'tall'],
31152             ['xs', 'sm'],
31153             ['xs', 'xs'],
31154             ['xs'],
31155             
31156             ['sm', 'xs', 'xs'],
31157             ['sm', 'xs'],
31158             ['sm'],
31159             
31160             ['tall', 'xs', 'xs', 'xs'],
31161             ['tall', 'xs', 'xs'],
31162             ['tall', 'xs'],
31163             ['tall']
31164             
31165         ];
31166         
31167         var queue = [];
31168         
31169         var boxes = [];
31170         
31171         var box = [];
31172         
31173         Roo.each(items, function(item, k){
31174             
31175             switch (item.size) {
31176                 // these layouts take up a full box,
31177                 case 'md' :
31178                 case 'md-left' :
31179                 case 'md-right' :
31180                 case 'wide' :
31181                     
31182                     if(box.length){
31183                         boxes.push(box);
31184                         box = [];
31185                     }
31186                     
31187                     boxes.push([item]);
31188                     
31189                     break;
31190                     
31191                 case 'xs' :
31192                 case 'sm' :
31193                 case 'tall' :
31194                     
31195                     box.push(item);
31196                     
31197                     break;
31198                 default :
31199                     break;
31200                     
31201             }
31202             
31203         }, this);
31204         
31205         if(box.length){
31206             boxes.push(box);
31207             box = [];
31208         }
31209         
31210         var filterPattern = function(box, length)
31211         {
31212             if(!box.length){
31213                 return;
31214             }
31215             
31216             var match = false;
31217             
31218             var pattern = box.slice(0, length);
31219             
31220             var format = [];
31221             
31222             Roo.each(pattern, function(i){
31223                 format.push(i.size);
31224             }, this);
31225             
31226             Roo.each(standard, function(s){
31227                 
31228                 if(String(s) != String(format)){
31229                     return;
31230                 }
31231                 
31232                 match = true;
31233                 return false;
31234                 
31235             }, this);
31236             
31237             if(!match && length == 1){
31238                 return;
31239             }
31240             
31241             if(!match){
31242                 filterPattern(box, length - 1);
31243                 return;
31244             }
31245                 
31246             queue.push(pattern);
31247
31248             box = box.slice(length, box.length);
31249
31250             filterPattern(box, 4);
31251
31252             return;
31253             
31254         }
31255         
31256         Roo.each(boxes, function(box, k){
31257             
31258             if(!box.length){
31259                 return;
31260             }
31261             
31262             if(box.length == 1){
31263                 queue.push(box);
31264                 return;
31265             }
31266             
31267             filterPattern(box, 4);
31268             
31269         }, this);
31270         
31271         this._processVerticalLayoutQueue( queue, isInstant );
31272         
31273     },
31274     
31275 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31276 //    {
31277 //        if ( !items || !items.length ) {
31278 //            return;
31279 //        }
31280 //
31281 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31282 //        
31283 //    },
31284     
31285     _horizontalLayoutItems : function ( items , isInstant)
31286     {
31287         if ( !items || !items.length || items.length < 3) {
31288             return;
31289         }
31290         
31291         items.reverse();
31292         
31293         var eItems = items.slice(0, 3);
31294         
31295         items = items.slice(3, items.length);
31296         
31297         var standard = [
31298             ['xs', 'xs', 'xs', 'wide'],
31299             ['xs', 'xs', 'wide'],
31300             ['xs', 'xs', 'sm'],
31301             ['xs', 'xs', 'xs'],
31302             ['xs', 'wide'],
31303             ['xs', 'sm'],
31304             ['xs', 'xs'],
31305             ['xs'],
31306             
31307             ['sm', 'xs', 'xs'],
31308             ['sm', 'xs'],
31309             ['sm'],
31310             
31311             ['wide', 'xs', 'xs', 'xs'],
31312             ['wide', 'xs', 'xs'],
31313             ['wide', 'xs'],
31314             ['wide'],
31315             
31316             ['wide-thin']
31317         ];
31318         
31319         var queue = [];
31320         
31321         var boxes = [];
31322         
31323         var box = [];
31324         
31325         Roo.each(items, function(item, k){
31326             
31327             switch (item.size) {
31328                 case 'md' :
31329                 case 'md-left' :
31330                 case 'md-right' :
31331                 case 'tall' :
31332                     
31333                     if(box.length){
31334                         boxes.push(box);
31335                         box = [];
31336                     }
31337                     
31338                     boxes.push([item]);
31339                     
31340                     break;
31341                     
31342                 case 'xs' :
31343                 case 'sm' :
31344                 case 'wide' :
31345                 case 'wide-thin' :
31346                     
31347                     box.push(item);
31348                     
31349                     break;
31350                 default :
31351                     break;
31352                     
31353             }
31354             
31355         }, this);
31356         
31357         if(box.length){
31358             boxes.push(box);
31359             box = [];
31360         }
31361         
31362         var filterPattern = function(box, length)
31363         {
31364             if(!box.length){
31365                 return;
31366             }
31367             
31368             var match = false;
31369             
31370             var pattern = box.slice(0, length);
31371             
31372             var format = [];
31373             
31374             Roo.each(pattern, function(i){
31375                 format.push(i.size);
31376             }, this);
31377             
31378             Roo.each(standard, function(s){
31379                 
31380                 if(String(s) != String(format)){
31381                     return;
31382                 }
31383                 
31384                 match = true;
31385                 return false;
31386                 
31387             }, this);
31388             
31389             if(!match && length == 1){
31390                 return;
31391             }
31392             
31393             if(!match){
31394                 filterPattern(box, length - 1);
31395                 return;
31396             }
31397                 
31398             queue.push(pattern);
31399
31400             box = box.slice(length, box.length);
31401
31402             filterPattern(box, 4);
31403
31404             return;
31405             
31406         }
31407         
31408         Roo.each(boxes, function(box, k){
31409             
31410             if(!box.length){
31411                 return;
31412             }
31413             
31414             if(box.length == 1){
31415                 queue.push(box);
31416                 return;
31417             }
31418             
31419             filterPattern(box, 4);
31420             
31421         }, this);
31422         
31423         
31424         var prune = [];
31425         
31426         var pos = this.el.getBox(true);
31427         
31428         var minX = pos.x;
31429         
31430         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31431         
31432         var hit_end = false;
31433         
31434         Roo.each(queue, function(box){
31435             
31436             if(hit_end){
31437                 
31438                 Roo.each(box, function(b){
31439                 
31440                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31441                     b.el.hide();
31442
31443                 }, this);
31444
31445                 return;
31446             }
31447             
31448             var mx = 0;
31449             
31450             Roo.each(box, function(b){
31451                 
31452                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31453                 b.el.show();
31454
31455                 mx = Math.max(mx, b.x);
31456                 
31457             }, this);
31458             
31459             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31460             
31461             if(maxX < minX){
31462                 
31463                 Roo.each(box, function(b){
31464                 
31465                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31466                     b.el.hide();
31467                     
31468                 }, this);
31469                 
31470                 hit_end = true;
31471                 
31472                 return;
31473             }
31474             
31475             prune.push(box);
31476             
31477         }, this);
31478         
31479         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31480     },
31481     
31482     /** Sets position of item in DOM
31483     * @param {Element} item
31484     * @param {Number} x - horizontal position
31485     * @param {Number} y - vertical position
31486     * @param {Boolean} isInstant - disables transitions
31487     */
31488     _processVerticalLayoutQueue : function( queue, isInstant )
31489     {
31490         var pos = this.el.getBox(true);
31491         var x = pos.x;
31492         var y = pos.y;
31493         var maxY = [];
31494         
31495         for (var i = 0; i < this.cols; i++){
31496             maxY[i] = pos.y;
31497         }
31498         
31499         Roo.each(queue, function(box, k){
31500             
31501             var col = k % this.cols;
31502             
31503             Roo.each(box, function(b,kk){
31504                 
31505                 b.el.position('absolute');
31506                 
31507                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31508                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31509                 
31510                 if(b.size == 'md-left' || b.size == 'md-right'){
31511                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31512                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31513                 }
31514                 
31515                 b.el.setWidth(width);
31516                 b.el.setHeight(height);
31517                 // iframe?
31518                 b.el.select('iframe',true).setSize(width,height);
31519                 
31520             }, this);
31521             
31522             for (var i = 0; i < this.cols; i++){
31523                 
31524                 if(maxY[i] < maxY[col]){
31525                     col = i;
31526                     continue;
31527                 }
31528                 
31529                 col = Math.min(col, i);
31530                 
31531             }
31532             
31533             x = pos.x + col * (this.colWidth + this.padWidth);
31534             
31535             y = maxY[col];
31536             
31537             var positions = [];
31538             
31539             switch (box.length){
31540                 case 1 :
31541                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31542                     break;
31543                 case 2 :
31544                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31545                     break;
31546                 case 3 :
31547                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31548                     break;
31549                 case 4 :
31550                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31551                     break;
31552                 default :
31553                     break;
31554             }
31555             
31556             Roo.each(box, function(b,kk){
31557                 
31558                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31559                 
31560                 var sz = b.el.getSize();
31561                 
31562                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31563                 
31564             }, this);
31565             
31566         }, this);
31567         
31568         var mY = 0;
31569         
31570         for (var i = 0; i < this.cols; i++){
31571             mY = Math.max(mY, maxY[i]);
31572         }
31573         
31574         this.el.setHeight(mY - pos.y);
31575         
31576     },
31577     
31578 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31579 //    {
31580 //        var pos = this.el.getBox(true);
31581 //        var x = pos.x;
31582 //        var y = pos.y;
31583 //        var maxX = pos.right;
31584 //        
31585 //        var maxHeight = 0;
31586 //        
31587 //        Roo.each(items, function(item, k){
31588 //            
31589 //            var c = k % 2;
31590 //            
31591 //            item.el.position('absolute');
31592 //                
31593 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31594 //
31595 //            item.el.setWidth(width);
31596 //
31597 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31598 //
31599 //            item.el.setHeight(height);
31600 //            
31601 //            if(c == 0){
31602 //                item.el.setXY([x, y], isInstant ? false : true);
31603 //            } else {
31604 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31605 //            }
31606 //            
31607 //            y = y + height + this.alternativePadWidth;
31608 //            
31609 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31610 //            
31611 //        }, this);
31612 //        
31613 //        this.el.setHeight(maxHeight);
31614 //        
31615 //    },
31616     
31617     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31618     {
31619         var pos = this.el.getBox(true);
31620         
31621         var minX = pos.x;
31622         var minY = pos.y;
31623         
31624         var maxX = pos.right;
31625         
31626         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31627         
31628         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31629         
31630         Roo.each(queue, function(box, k){
31631             
31632             Roo.each(box, function(b, kk){
31633                 
31634                 b.el.position('absolute');
31635                 
31636                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31637                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31638                 
31639                 if(b.size == 'md-left' || b.size == 'md-right'){
31640                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31641                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31642                 }
31643                 
31644                 b.el.setWidth(width);
31645                 b.el.setHeight(height);
31646                 
31647             }, this);
31648             
31649             if(!box.length){
31650                 return;
31651             }
31652             
31653             var positions = [];
31654             
31655             switch (box.length){
31656                 case 1 :
31657                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31658                     break;
31659                 case 2 :
31660                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31661                     break;
31662                 case 3 :
31663                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31664                     break;
31665                 case 4 :
31666                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31667                     break;
31668                 default :
31669                     break;
31670             }
31671             
31672             Roo.each(box, function(b,kk){
31673                 
31674                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31675                 
31676                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31677                 
31678             }, this);
31679             
31680         }, this);
31681         
31682     },
31683     
31684     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31685     {
31686         Roo.each(eItems, function(b,k){
31687             
31688             b.size = (k == 0) ? 'sm' : 'xs';
31689             b.x = (k == 0) ? 2 : 1;
31690             b.y = (k == 0) ? 2 : 1;
31691             
31692             b.el.position('absolute');
31693             
31694             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31695                 
31696             b.el.setWidth(width);
31697             
31698             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31699             
31700             b.el.setHeight(height);
31701             
31702         }, this);
31703
31704         var positions = [];
31705         
31706         positions.push({
31707             x : maxX - this.unitWidth * 2 - this.gutter,
31708             y : minY
31709         });
31710         
31711         positions.push({
31712             x : maxX - this.unitWidth,
31713             y : minY + (this.unitWidth + this.gutter) * 2
31714         });
31715         
31716         positions.push({
31717             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31718             y : minY
31719         });
31720         
31721         Roo.each(eItems, function(b,k){
31722             
31723             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31724
31725         }, this);
31726         
31727     },
31728     
31729     getVerticalOneBoxColPositions : function(x, y, box)
31730     {
31731         var pos = [];
31732         
31733         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31734         
31735         if(box[0].size == 'md-left'){
31736             rand = 0;
31737         }
31738         
31739         if(box[0].size == 'md-right'){
31740             rand = 1;
31741         }
31742         
31743         pos.push({
31744             x : x + (this.unitWidth + this.gutter) * rand,
31745             y : y
31746         });
31747         
31748         return pos;
31749     },
31750     
31751     getVerticalTwoBoxColPositions : function(x, y, box)
31752     {
31753         var pos = [];
31754         
31755         if(box[0].size == 'xs'){
31756             
31757             pos.push({
31758                 x : x,
31759                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31760             });
31761
31762             pos.push({
31763                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31764                 y : y
31765             });
31766             
31767             return pos;
31768             
31769         }
31770         
31771         pos.push({
31772             x : x,
31773             y : y
31774         });
31775
31776         pos.push({
31777             x : x + (this.unitWidth + this.gutter) * 2,
31778             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31779         });
31780         
31781         return pos;
31782         
31783     },
31784     
31785     getVerticalThreeBoxColPositions : function(x, y, box)
31786     {
31787         var pos = [];
31788         
31789         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31790             
31791             pos.push({
31792                 x : x,
31793                 y : y
31794             });
31795
31796             pos.push({
31797                 x : x + (this.unitWidth + this.gutter) * 1,
31798                 y : y
31799             });
31800             
31801             pos.push({
31802                 x : x + (this.unitWidth + this.gutter) * 2,
31803                 y : y
31804             });
31805             
31806             return pos;
31807             
31808         }
31809         
31810         if(box[0].size == 'xs' && box[1].size == 'xs'){
31811             
31812             pos.push({
31813                 x : x,
31814                 y : y
31815             });
31816
31817             pos.push({
31818                 x : x,
31819                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31820             });
31821             
31822             pos.push({
31823                 x : x + (this.unitWidth + this.gutter) * 1,
31824                 y : y
31825             });
31826             
31827             return pos;
31828             
31829         }
31830         
31831         pos.push({
31832             x : x,
31833             y : y
31834         });
31835
31836         pos.push({
31837             x : x + (this.unitWidth + this.gutter) * 2,
31838             y : y
31839         });
31840
31841         pos.push({
31842             x : x + (this.unitWidth + this.gutter) * 2,
31843             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31844         });
31845             
31846         return pos;
31847         
31848     },
31849     
31850     getVerticalFourBoxColPositions : function(x, y, box)
31851     {
31852         var pos = [];
31853         
31854         if(box[0].size == 'xs'){
31855             
31856             pos.push({
31857                 x : x,
31858                 y : y
31859             });
31860
31861             pos.push({
31862                 x : x,
31863                 y : y + (this.unitHeight + this.gutter) * 1
31864             });
31865             
31866             pos.push({
31867                 x : x,
31868                 y : y + (this.unitHeight + this.gutter) * 2
31869             });
31870             
31871             pos.push({
31872                 x : x + (this.unitWidth + this.gutter) * 1,
31873                 y : y
31874             });
31875             
31876             return pos;
31877             
31878         }
31879         
31880         pos.push({
31881             x : x,
31882             y : y
31883         });
31884
31885         pos.push({
31886             x : x + (this.unitWidth + this.gutter) * 2,
31887             y : y
31888         });
31889
31890         pos.push({
31891             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31892             y : y + (this.unitHeight + this.gutter) * 1
31893         });
31894
31895         pos.push({
31896             x : x + (this.unitWidth + this.gutter) * 2,
31897             y : y + (this.unitWidth + this.gutter) * 2
31898         });
31899
31900         return pos;
31901         
31902     },
31903     
31904     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31905     {
31906         var pos = [];
31907         
31908         if(box[0].size == 'md-left'){
31909             pos.push({
31910                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31911                 y : minY
31912             });
31913             
31914             return pos;
31915         }
31916         
31917         if(box[0].size == 'md-right'){
31918             pos.push({
31919                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31920                 y : minY + (this.unitWidth + this.gutter) * 1
31921             });
31922             
31923             return pos;
31924         }
31925         
31926         var rand = Math.floor(Math.random() * (4 - box[0].y));
31927         
31928         pos.push({
31929             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31930             y : minY + (this.unitWidth + this.gutter) * rand
31931         });
31932         
31933         return pos;
31934         
31935     },
31936     
31937     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31938     {
31939         var pos = [];
31940         
31941         if(box[0].size == 'xs'){
31942             
31943             pos.push({
31944                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31945                 y : minY
31946             });
31947
31948             pos.push({
31949                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31950                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31951             });
31952             
31953             return pos;
31954             
31955         }
31956         
31957         pos.push({
31958             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31959             y : minY
31960         });
31961
31962         pos.push({
31963             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31964             y : minY + (this.unitWidth + this.gutter) * 2
31965         });
31966         
31967         return pos;
31968         
31969     },
31970     
31971     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31972     {
31973         var pos = [];
31974         
31975         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31976             
31977             pos.push({
31978                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31979                 y : minY
31980             });
31981
31982             pos.push({
31983                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31984                 y : minY + (this.unitWidth + this.gutter) * 1
31985             });
31986             
31987             pos.push({
31988                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31989                 y : minY + (this.unitWidth + this.gutter) * 2
31990             });
31991             
31992             return pos;
31993             
31994         }
31995         
31996         if(box[0].size == 'xs' && box[1].size == 'xs'){
31997             
31998             pos.push({
31999                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32000                 y : minY
32001             });
32002
32003             pos.push({
32004                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32005                 y : minY
32006             });
32007             
32008             pos.push({
32009                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32010                 y : minY + (this.unitWidth + this.gutter) * 1
32011             });
32012             
32013             return pos;
32014             
32015         }
32016         
32017         pos.push({
32018             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32019             y : minY
32020         });
32021
32022         pos.push({
32023             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32024             y : minY + (this.unitWidth + this.gutter) * 2
32025         });
32026
32027         pos.push({
32028             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32029             y : minY + (this.unitWidth + this.gutter) * 2
32030         });
32031             
32032         return pos;
32033         
32034     },
32035     
32036     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32037     {
32038         var pos = [];
32039         
32040         if(box[0].size == 'xs'){
32041             
32042             pos.push({
32043                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32044                 y : minY
32045             });
32046
32047             pos.push({
32048                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32049                 y : minY
32050             });
32051             
32052             pos.push({
32053                 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),
32054                 y : minY
32055             });
32056             
32057             pos.push({
32058                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32059                 y : minY + (this.unitWidth + this.gutter) * 1
32060             });
32061             
32062             return pos;
32063             
32064         }
32065         
32066         pos.push({
32067             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32068             y : minY
32069         });
32070         
32071         pos.push({
32072             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32073             y : minY + (this.unitWidth + this.gutter) * 2
32074         });
32075         
32076         pos.push({
32077             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32078             y : minY + (this.unitWidth + this.gutter) * 2
32079         });
32080         
32081         pos.push({
32082             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),
32083             y : minY + (this.unitWidth + this.gutter) * 2
32084         });
32085
32086         return pos;
32087         
32088     },
32089     
32090     /**
32091     * remove a Masonry Brick
32092     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32093     */
32094     removeBrick : function(brick_id)
32095     {
32096         if (!brick_id) {
32097             return;
32098         }
32099         
32100         for (var i = 0; i<this.bricks.length; i++) {
32101             if (this.bricks[i].id == brick_id) {
32102                 this.bricks.splice(i,1);
32103                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32104                 this.initial();
32105             }
32106         }
32107     },
32108     
32109     /**
32110     * adds a Masonry Brick
32111     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32112     */
32113     addBrick : function(cfg)
32114     {
32115         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32116         //this.register(cn);
32117         cn.parentId = this.id;
32118         cn.render(this.el);
32119         return cn;
32120     },
32121     
32122     /**
32123     * register a Masonry Brick
32124     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32125     */
32126     
32127     register : function(brick)
32128     {
32129         this.bricks.push(brick);
32130         brick.masonryId = this.id;
32131     },
32132     
32133     /**
32134     * clear all the Masonry Brick
32135     */
32136     clearAll : function()
32137     {
32138         this.bricks = [];
32139         //this.getChildContainer().dom.innerHTML = "";
32140         this.el.dom.innerHTML = '';
32141     },
32142     
32143     getSelected : function()
32144     {
32145         if (!this.selectedBrick) {
32146             return false;
32147         }
32148         
32149         return this.selectedBrick;
32150     }
32151 });
32152
32153 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32154     
32155     groups: {},
32156      /**
32157     * register a Masonry Layout
32158     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32159     */
32160     
32161     register : function(layout)
32162     {
32163         this.groups[layout.id] = layout;
32164     },
32165     /**
32166     * fetch a  Masonry Layout based on the masonry layout ID
32167     * @param {string} the masonry layout to add
32168     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32169     */
32170     
32171     get: function(layout_id) {
32172         if (typeof(this.groups[layout_id]) == 'undefined') {
32173             return false;
32174         }
32175         return this.groups[layout_id] ;
32176     }
32177     
32178     
32179     
32180 });
32181
32182  
32183
32184  /**
32185  *
32186  * This is based on 
32187  * http://masonry.desandro.com
32188  *
32189  * The idea is to render all the bricks based on vertical width...
32190  *
32191  * The original code extends 'outlayer' - we might need to use that....
32192  * 
32193  */
32194
32195
32196 /**
32197  * @class Roo.bootstrap.LayoutMasonryAuto
32198  * @extends Roo.bootstrap.Component
32199  * Bootstrap Layout Masonry class
32200  * 
32201  * @constructor
32202  * Create a new Element
32203  * @param {Object} config The config object
32204  */
32205
32206 Roo.bootstrap.LayoutMasonryAuto = function(config){
32207     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32208 };
32209
32210 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32211     
32212       /**
32213      * @cfg {Boolean} isFitWidth  - resize the width..
32214      */   
32215     isFitWidth : false,  // options..
32216     /**
32217      * @cfg {Boolean} isOriginLeft = left align?
32218      */   
32219     isOriginLeft : true,
32220     /**
32221      * @cfg {Boolean} isOriginTop = top align?
32222      */   
32223     isOriginTop : false,
32224     /**
32225      * @cfg {Boolean} isLayoutInstant = no animation?
32226      */   
32227     isLayoutInstant : false, // needed?
32228     /**
32229      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32230      */   
32231     isResizingContainer : true,
32232     /**
32233      * @cfg {Number} columnWidth  width of the columns 
32234      */   
32235     
32236     columnWidth : 0,
32237     
32238     /**
32239      * @cfg {Number} maxCols maximum number of columns
32240      */   
32241     
32242     maxCols: 0,
32243     /**
32244      * @cfg {Number} padHeight padding below box..
32245      */   
32246     
32247     padHeight : 10, 
32248     
32249     /**
32250      * @cfg {Boolean} isAutoInitial defalut true
32251      */   
32252     
32253     isAutoInitial : true, 
32254     
32255     // private?
32256     gutter : 0,
32257     
32258     containerWidth: 0,
32259     initialColumnWidth : 0,
32260     currentSize : null,
32261     
32262     colYs : null, // array.
32263     maxY : 0,
32264     padWidth: 10,
32265     
32266     
32267     tag: 'div',
32268     cls: '',
32269     bricks: null, //CompositeElement
32270     cols : 0, // array?
32271     // element : null, // wrapped now this.el
32272     _isLayoutInited : null, 
32273     
32274     
32275     getAutoCreate : function(){
32276         
32277         var cfg = {
32278             tag: this.tag,
32279             cls: 'blog-masonary-wrapper ' + this.cls,
32280             cn : {
32281                 cls : 'mas-boxes masonary'
32282             }
32283         };
32284         
32285         return cfg;
32286     },
32287     
32288     getChildContainer: function( )
32289     {
32290         if (this.boxesEl) {
32291             return this.boxesEl;
32292         }
32293         
32294         this.boxesEl = this.el.select('.mas-boxes').first();
32295         
32296         return this.boxesEl;
32297     },
32298     
32299     
32300     initEvents : function()
32301     {
32302         var _this = this;
32303         
32304         if(this.isAutoInitial){
32305             Roo.log('hook children rendered');
32306             this.on('childrenrendered', function() {
32307                 Roo.log('children rendered');
32308                 _this.initial();
32309             } ,this);
32310         }
32311         
32312     },
32313     
32314     initial : function()
32315     {
32316         this.reloadItems();
32317
32318         this.currentSize = this.el.getBox(true);
32319
32320         /// was window resize... - let's see if this works..
32321         Roo.EventManager.onWindowResize(this.resize, this); 
32322
32323         if(!this.isAutoInitial){
32324             this.layout();
32325             return;
32326         }
32327         
32328         this.layout.defer(500,this);
32329     },
32330     
32331     reloadItems: function()
32332     {
32333         this.bricks = this.el.select('.masonry-brick', true);
32334         
32335         this.bricks.each(function(b) {
32336             //Roo.log(b.getSize());
32337             if (!b.attr('originalwidth')) {
32338                 b.attr('originalwidth',  b.getSize().width);
32339             }
32340             
32341         });
32342         
32343         Roo.log(this.bricks.elements.length);
32344     },
32345     
32346     resize : function()
32347     {
32348         Roo.log('resize');
32349         var cs = this.el.getBox(true);
32350         
32351         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32352             Roo.log("no change in with or X");
32353             return;
32354         }
32355         this.currentSize = cs;
32356         this.layout();
32357     },
32358     
32359     layout : function()
32360     {
32361          Roo.log('layout');
32362         this._resetLayout();
32363         //this._manageStamps();
32364       
32365         // don't animate first layout
32366         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32367         this.layoutItems( isInstant );
32368       
32369         // flag for initalized
32370         this._isLayoutInited = true;
32371     },
32372     
32373     layoutItems : function( isInstant )
32374     {
32375         //var items = this._getItemsForLayout( this.items );
32376         // original code supports filtering layout items.. we just ignore it..
32377         
32378         this._layoutItems( this.bricks , isInstant );
32379       
32380         this._postLayout();
32381     },
32382     _layoutItems : function ( items , isInstant)
32383     {
32384        //this.fireEvent( 'layout', this, items );
32385     
32386
32387         if ( !items || !items.elements.length ) {
32388           // no items, emit event with empty array
32389             return;
32390         }
32391
32392         var queue = [];
32393         items.each(function(item) {
32394             Roo.log("layout item");
32395             Roo.log(item);
32396             // get x/y object from method
32397             var position = this._getItemLayoutPosition( item );
32398             // enqueue
32399             position.item = item;
32400             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32401             queue.push( position );
32402         }, this);
32403       
32404         this._processLayoutQueue( queue );
32405     },
32406     /** Sets position of item in DOM
32407     * @param {Element} item
32408     * @param {Number} x - horizontal position
32409     * @param {Number} y - vertical position
32410     * @param {Boolean} isInstant - disables transitions
32411     */
32412     _processLayoutQueue : function( queue )
32413     {
32414         for ( var i=0, len = queue.length; i < len; i++ ) {
32415             var obj = queue[i];
32416             obj.item.position('absolute');
32417             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32418         }
32419     },
32420       
32421     
32422     /**
32423     * Any logic you want to do after each layout,
32424     * i.e. size the container
32425     */
32426     _postLayout : function()
32427     {
32428         this.resizeContainer();
32429     },
32430     
32431     resizeContainer : function()
32432     {
32433         if ( !this.isResizingContainer ) {
32434             return;
32435         }
32436         var size = this._getContainerSize();
32437         if ( size ) {
32438             this.el.setSize(size.width,size.height);
32439             this.boxesEl.setSize(size.width,size.height);
32440         }
32441     },
32442     
32443     
32444     
32445     _resetLayout : function()
32446     {
32447         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32448         this.colWidth = this.el.getWidth();
32449         //this.gutter = this.el.getWidth(); 
32450         
32451         this.measureColumns();
32452
32453         // reset column Y
32454         var i = this.cols;
32455         this.colYs = [];
32456         while (i--) {
32457             this.colYs.push( 0 );
32458         }
32459     
32460         this.maxY = 0;
32461     },
32462
32463     measureColumns : function()
32464     {
32465         this.getContainerWidth();
32466       // if columnWidth is 0, default to outerWidth of first item
32467         if ( !this.columnWidth ) {
32468             var firstItem = this.bricks.first();
32469             Roo.log(firstItem);
32470             this.columnWidth  = this.containerWidth;
32471             if (firstItem && firstItem.attr('originalwidth') ) {
32472                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32473             }
32474             // columnWidth fall back to item of first element
32475             Roo.log("set column width?");
32476                         this.initialColumnWidth = this.columnWidth  ;
32477
32478             // if first elem has no width, default to size of container
32479             
32480         }
32481         
32482         
32483         if (this.initialColumnWidth) {
32484             this.columnWidth = this.initialColumnWidth;
32485         }
32486         
32487         
32488             
32489         // column width is fixed at the top - however if container width get's smaller we should
32490         // reduce it...
32491         
32492         // this bit calcs how man columns..
32493             
32494         var columnWidth = this.columnWidth += this.gutter;
32495       
32496         // calculate columns
32497         var containerWidth = this.containerWidth + this.gutter;
32498         
32499         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32500         // fix rounding errors, typically with gutters
32501         var excess = columnWidth - containerWidth % columnWidth;
32502         
32503         
32504         // if overshoot is less than a pixel, round up, otherwise floor it
32505         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32506         cols = Math[ mathMethod ]( cols );
32507         this.cols = Math.max( cols, 1 );
32508         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32509         
32510          // padding positioning..
32511         var totalColWidth = this.cols * this.columnWidth;
32512         var padavail = this.containerWidth - totalColWidth;
32513         // so for 2 columns - we need 3 'pads'
32514         
32515         var padNeeded = (1+this.cols) * this.padWidth;
32516         
32517         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32518         
32519         this.columnWidth += padExtra
32520         //this.padWidth = Math.floor(padavail /  ( this.cols));
32521         
32522         // adjust colum width so that padding is fixed??
32523         
32524         // we have 3 columns ... total = width * 3
32525         // we have X left over... that should be used by 
32526         
32527         //if (this.expandC) {
32528             
32529         //}
32530         
32531         
32532         
32533     },
32534     
32535     getContainerWidth : function()
32536     {
32537        /* // container is parent if fit width
32538         var container = this.isFitWidth ? this.element.parentNode : this.element;
32539         // check that this.size and size are there
32540         // IE8 triggers resize on body size change, so they might not be
32541         
32542         var size = getSize( container );  //FIXME
32543         this.containerWidth = size && size.innerWidth; //FIXME
32544         */
32545          
32546         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32547         
32548     },
32549     
32550     _getItemLayoutPosition : function( item )  // what is item?
32551     {
32552         // we resize the item to our columnWidth..
32553       
32554         item.setWidth(this.columnWidth);
32555         item.autoBoxAdjust  = false;
32556         
32557         var sz = item.getSize();
32558  
32559         // how many columns does this brick span
32560         var remainder = this.containerWidth % this.columnWidth;
32561         
32562         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32563         // round if off by 1 pixel, otherwise use ceil
32564         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32565         colSpan = Math.min( colSpan, this.cols );
32566         
32567         // normally this should be '1' as we dont' currently allow multi width columns..
32568         
32569         var colGroup = this._getColGroup( colSpan );
32570         // get the minimum Y value from the columns
32571         var minimumY = Math.min.apply( Math, colGroup );
32572         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32573         
32574         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32575          
32576         // position the brick
32577         var position = {
32578             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32579             y: this.currentSize.y + minimumY + this.padHeight
32580         };
32581         
32582         Roo.log(position);
32583         // apply setHeight to necessary columns
32584         var setHeight = minimumY + sz.height + this.padHeight;
32585         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32586         
32587         var setSpan = this.cols + 1 - colGroup.length;
32588         for ( var i = 0; i < setSpan; i++ ) {
32589           this.colYs[ shortColIndex + i ] = setHeight ;
32590         }
32591       
32592         return position;
32593     },
32594     
32595     /**
32596      * @param {Number} colSpan - number of columns the element spans
32597      * @returns {Array} colGroup
32598      */
32599     _getColGroup : function( colSpan )
32600     {
32601         if ( colSpan < 2 ) {
32602           // if brick spans only one column, use all the column Ys
32603           return this.colYs;
32604         }
32605       
32606         var colGroup = [];
32607         // how many different places could this brick fit horizontally
32608         var groupCount = this.cols + 1 - colSpan;
32609         // for each group potential horizontal position
32610         for ( var i = 0; i < groupCount; i++ ) {
32611           // make an array of colY values for that one group
32612           var groupColYs = this.colYs.slice( i, i + colSpan );
32613           // and get the max value of the array
32614           colGroup[i] = Math.max.apply( Math, groupColYs );
32615         }
32616         return colGroup;
32617     },
32618     /*
32619     _manageStamp : function( stamp )
32620     {
32621         var stampSize =  stamp.getSize();
32622         var offset = stamp.getBox();
32623         // get the columns that this stamp affects
32624         var firstX = this.isOriginLeft ? offset.x : offset.right;
32625         var lastX = firstX + stampSize.width;
32626         var firstCol = Math.floor( firstX / this.columnWidth );
32627         firstCol = Math.max( 0, firstCol );
32628         
32629         var lastCol = Math.floor( lastX / this.columnWidth );
32630         // lastCol should not go over if multiple of columnWidth #425
32631         lastCol -= lastX % this.columnWidth ? 0 : 1;
32632         lastCol = Math.min( this.cols - 1, lastCol );
32633         
32634         // set colYs to bottom of the stamp
32635         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32636             stampSize.height;
32637             
32638         for ( var i = firstCol; i <= lastCol; i++ ) {
32639           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32640         }
32641     },
32642     */
32643     
32644     _getContainerSize : function()
32645     {
32646         this.maxY = Math.max.apply( Math, this.colYs );
32647         var size = {
32648             height: this.maxY
32649         };
32650       
32651         if ( this.isFitWidth ) {
32652             size.width = this._getContainerFitWidth();
32653         }
32654       
32655         return size;
32656     },
32657     
32658     _getContainerFitWidth : function()
32659     {
32660         var unusedCols = 0;
32661         // count unused columns
32662         var i = this.cols;
32663         while ( --i ) {
32664           if ( this.colYs[i] !== 0 ) {
32665             break;
32666           }
32667           unusedCols++;
32668         }
32669         // fit container to columns that have been used
32670         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32671     },
32672     
32673     needsResizeLayout : function()
32674     {
32675         var previousWidth = this.containerWidth;
32676         this.getContainerWidth();
32677         return previousWidth !== this.containerWidth;
32678     }
32679  
32680 });
32681
32682  
32683
32684  /*
32685  * - LGPL
32686  *
32687  * element
32688  * 
32689  */
32690
32691 /**
32692  * @class Roo.bootstrap.MasonryBrick
32693  * @extends Roo.bootstrap.Component
32694  * Bootstrap MasonryBrick class
32695  * 
32696  * @constructor
32697  * Create a new MasonryBrick
32698  * @param {Object} config The config object
32699  */
32700
32701 Roo.bootstrap.MasonryBrick = function(config){
32702     
32703     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32704     
32705     Roo.bootstrap.MasonryBrick.register(this);
32706     
32707     this.addEvents({
32708         // raw events
32709         /**
32710          * @event click
32711          * When a MasonryBrick is clcik
32712          * @param {Roo.bootstrap.MasonryBrick} this
32713          * @param {Roo.EventObject} e
32714          */
32715         "click" : true
32716     });
32717 };
32718
32719 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32720     
32721     /**
32722      * @cfg {String} title
32723      */   
32724     title : '',
32725     /**
32726      * @cfg {String} html
32727      */   
32728     html : '',
32729     /**
32730      * @cfg {String} bgimage
32731      */   
32732     bgimage : '',
32733     /**
32734      * @cfg {String} videourl
32735      */   
32736     videourl : '',
32737     /**
32738      * @cfg {String} cls
32739      */   
32740     cls : '',
32741     /**
32742      * @cfg {String} href
32743      */   
32744     href : '',
32745     /**
32746      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32747      */   
32748     size : 'xs',
32749     
32750     /**
32751      * @cfg {String} placetitle (center|bottom)
32752      */   
32753     placetitle : '',
32754     
32755     /**
32756      * @cfg {Boolean} isFitContainer defalut true
32757      */   
32758     isFitContainer : true, 
32759     
32760     /**
32761      * @cfg {Boolean} preventDefault defalut false
32762      */   
32763     preventDefault : false, 
32764     
32765     /**
32766      * @cfg {Boolean} inverse defalut false
32767      */   
32768     maskInverse : false, 
32769     
32770     getAutoCreate : function()
32771     {
32772         if(!this.isFitContainer){
32773             return this.getSplitAutoCreate();
32774         }
32775         
32776         var cls = 'masonry-brick masonry-brick-full';
32777         
32778         if(this.href.length){
32779             cls += ' masonry-brick-link';
32780         }
32781         
32782         if(this.bgimage.length){
32783             cls += ' masonry-brick-image';
32784         }
32785         
32786         if(this.maskInverse){
32787             cls += ' mask-inverse';
32788         }
32789         
32790         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32791             cls += ' enable-mask';
32792         }
32793         
32794         if(this.size){
32795             cls += ' masonry-' + this.size + '-brick';
32796         }
32797         
32798         if(this.placetitle.length){
32799             
32800             switch (this.placetitle) {
32801                 case 'center' :
32802                     cls += ' masonry-center-title';
32803                     break;
32804                 case 'bottom' :
32805                     cls += ' masonry-bottom-title';
32806                     break;
32807                 default:
32808                     break;
32809             }
32810             
32811         } else {
32812             if(!this.html.length && !this.bgimage.length){
32813                 cls += ' masonry-center-title';
32814             }
32815
32816             if(!this.html.length && this.bgimage.length){
32817                 cls += ' masonry-bottom-title';
32818             }
32819         }
32820         
32821         if(this.cls){
32822             cls += ' ' + this.cls;
32823         }
32824         
32825         var cfg = {
32826             tag: (this.href.length) ? 'a' : 'div',
32827             cls: cls,
32828             cn: [
32829                 {
32830                     tag: 'div',
32831                     cls: 'masonry-brick-mask'
32832                 },
32833                 {
32834                     tag: 'div',
32835                     cls: 'masonry-brick-paragraph',
32836                     cn: []
32837                 }
32838             ]
32839         };
32840         
32841         if(this.href.length){
32842             cfg.href = this.href;
32843         }
32844         
32845         var cn = cfg.cn[1].cn;
32846         
32847         if(this.title.length){
32848             cn.push({
32849                 tag: 'h4',
32850                 cls: 'masonry-brick-title',
32851                 html: this.title
32852             });
32853         }
32854         
32855         if(this.html.length){
32856             cn.push({
32857                 tag: 'p',
32858                 cls: 'masonry-brick-text',
32859                 html: this.html
32860             });
32861         }
32862         
32863         if (!this.title.length && !this.html.length) {
32864             cfg.cn[1].cls += ' hide';
32865         }
32866         
32867         if(this.bgimage.length){
32868             cfg.cn.push({
32869                 tag: 'img',
32870                 cls: 'masonry-brick-image-view',
32871                 src: this.bgimage
32872             });
32873         }
32874         
32875         if(this.videourl.length){
32876             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32877             // youtube support only?
32878             cfg.cn.push({
32879                 tag: 'iframe',
32880                 cls: 'masonry-brick-image-view',
32881                 src: vurl,
32882                 frameborder : 0,
32883                 allowfullscreen : true
32884             });
32885         }
32886         
32887         return cfg;
32888         
32889     },
32890     
32891     getSplitAutoCreate : function()
32892     {
32893         var cls = 'masonry-brick masonry-brick-split';
32894         
32895         if(this.href.length){
32896             cls += ' masonry-brick-link';
32897         }
32898         
32899         if(this.bgimage.length){
32900             cls += ' masonry-brick-image';
32901         }
32902         
32903         if(this.size){
32904             cls += ' masonry-' + this.size + '-brick';
32905         }
32906         
32907         switch (this.placetitle) {
32908             case 'center' :
32909                 cls += ' masonry-center-title';
32910                 break;
32911             case 'bottom' :
32912                 cls += ' masonry-bottom-title';
32913                 break;
32914             default:
32915                 if(!this.bgimage.length){
32916                     cls += ' masonry-center-title';
32917                 }
32918
32919                 if(this.bgimage.length){
32920                     cls += ' masonry-bottom-title';
32921                 }
32922                 break;
32923         }
32924         
32925         if(this.cls){
32926             cls += ' ' + this.cls;
32927         }
32928         
32929         var cfg = {
32930             tag: (this.href.length) ? 'a' : 'div',
32931             cls: cls,
32932             cn: [
32933                 {
32934                     tag: 'div',
32935                     cls: 'masonry-brick-split-head',
32936                     cn: [
32937                         {
32938                             tag: 'div',
32939                             cls: 'masonry-brick-paragraph',
32940                             cn: []
32941                         }
32942                     ]
32943                 },
32944                 {
32945                     tag: 'div',
32946                     cls: 'masonry-brick-split-body',
32947                     cn: []
32948                 }
32949             ]
32950         };
32951         
32952         if(this.href.length){
32953             cfg.href = this.href;
32954         }
32955         
32956         if(this.title.length){
32957             cfg.cn[0].cn[0].cn.push({
32958                 tag: 'h4',
32959                 cls: 'masonry-brick-title',
32960                 html: this.title
32961             });
32962         }
32963         
32964         if(this.html.length){
32965             cfg.cn[1].cn.push({
32966                 tag: 'p',
32967                 cls: 'masonry-brick-text',
32968                 html: this.html
32969             });
32970         }
32971
32972         if(this.bgimage.length){
32973             cfg.cn[0].cn.push({
32974                 tag: 'img',
32975                 cls: 'masonry-brick-image-view',
32976                 src: this.bgimage
32977             });
32978         }
32979         
32980         if(this.videourl.length){
32981             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32982             // youtube support only?
32983             cfg.cn[0].cn.cn.push({
32984                 tag: 'iframe',
32985                 cls: 'masonry-brick-image-view',
32986                 src: vurl,
32987                 frameborder : 0,
32988                 allowfullscreen : true
32989             });
32990         }
32991         
32992         return cfg;
32993     },
32994     
32995     initEvents: function() 
32996     {
32997         switch (this.size) {
32998             case 'xs' :
32999                 this.x = 1;
33000                 this.y = 1;
33001                 break;
33002             case 'sm' :
33003                 this.x = 2;
33004                 this.y = 2;
33005                 break;
33006             case 'md' :
33007             case 'md-left' :
33008             case 'md-right' :
33009                 this.x = 3;
33010                 this.y = 3;
33011                 break;
33012             case 'tall' :
33013                 this.x = 2;
33014                 this.y = 3;
33015                 break;
33016             case 'wide' :
33017                 this.x = 3;
33018                 this.y = 2;
33019                 break;
33020             case 'wide-thin' :
33021                 this.x = 3;
33022                 this.y = 1;
33023                 break;
33024                         
33025             default :
33026                 break;
33027         }
33028         
33029         if(Roo.isTouch){
33030             this.el.on('touchstart', this.onTouchStart, this);
33031             this.el.on('touchmove', this.onTouchMove, this);
33032             this.el.on('touchend', this.onTouchEnd, this);
33033             this.el.on('contextmenu', this.onContextMenu, this);
33034         } else {
33035             this.el.on('mouseenter'  ,this.enter, this);
33036             this.el.on('mouseleave', this.leave, this);
33037             this.el.on('click', this.onClick, this);
33038         }
33039         
33040         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33041             this.parent().bricks.push(this);   
33042         }
33043         
33044     },
33045     
33046     onClick: function(e, el)
33047     {
33048         var time = this.endTimer - this.startTimer;
33049         // Roo.log(e.preventDefault());
33050         if(Roo.isTouch){
33051             if(time > 1000){
33052                 e.preventDefault();
33053                 return;
33054             }
33055         }
33056         
33057         if(!this.preventDefault){
33058             return;
33059         }
33060         
33061         e.preventDefault();
33062         
33063         if (this.activeClass != '') {
33064             this.selectBrick();
33065         }
33066         
33067         this.fireEvent('click', this, e);
33068     },
33069     
33070     enter: function(e, el)
33071     {
33072         e.preventDefault();
33073         
33074         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33075             return;
33076         }
33077         
33078         if(this.bgimage.length && this.html.length){
33079             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33080         }
33081     },
33082     
33083     leave: function(e, el)
33084     {
33085         e.preventDefault();
33086         
33087         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33088             return;
33089         }
33090         
33091         if(this.bgimage.length && this.html.length){
33092             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33093         }
33094     },
33095     
33096     onTouchStart: function(e, el)
33097     {
33098 //        e.preventDefault();
33099         
33100         this.touchmoved = false;
33101         
33102         if(!this.isFitContainer){
33103             return;
33104         }
33105         
33106         if(!this.bgimage.length || !this.html.length){
33107             return;
33108         }
33109         
33110         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33111         
33112         this.timer = new Date().getTime();
33113         
33114     },
33115     
33116     onTouchMove: function(e, el)
33117     {
33118         this.touchmoved = true;
33119     },
33120     
33121     onContextMenu : function(e,el)
33122     {
33123         e.preventDefault();
33124         e.stopPropagation();
33125         return false;
33126     },
33127     
33128     onTouchEnd: function(e, el)
33129     {
33130 //        e.preventDefault();
33131         
33132         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33133         
33134             this.leave(e,el);
33135             
33136             return;
33137         }
33138         
33139         if(!this.bgimage.length || !this.html.length){
33140             
33141             if(this.href.length){
33142                 window.location.href = this.href;
33143             }
33144             
33145             return;
33146         }
33147         
33148         if(!this.isFitContainer){
33149             return;
33150         }
33151         
33152         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33153         
33154         window.location.href = this.href;
33155     },
33156     
33157     //selection on single brick only
33158     selectBrick : function() {
33159         
33160         if (!this.parentId) {
33161             return;
33162         }
33163         
33164         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33165         var index = m.selectedBrick.indexOf(this.id);
33166         
33167         if ( index > -1) {
33168             m.selectedBrick.splice(index,1);
33169             this.el.removeClass(this.activeClass);
33170             return;
33171         }
33172         
33173         for(var i = 0; i < m.selectedBrick.length; i++) {
33174             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33175             b.el.removeClass(b.activeClass);
33176         }
33177         
33178         m.selectedBrick = [];
33179         
33180         m.selectedBrick.push(this.id);
33181         this.el.addClass(this.activeClass);
33182         return;
33183     },
33184     
33185     isSelected : function(){
33186         return this.el.hasClass(this.activeClass);
33187         
33188     }
33189 });
33190
33191 Roo.apply(Roo.bootstrap.MasonryBrick, {
33192     
33193     //groups: {},
33194     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33195      /**
33196     * register a Masonry Brick
33197     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33198     */
33199     
33200     register : function(brick)
33201     {
33202         //this.groups[brick.id] = brick;
33203         this.groups.add(brick.id, brick);
33204     },
33205     /**
33206     * fetch a  masonry brick based on the masonry brick ID
33207     * @param {string} the masonry brick to add
33208     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33209     */
33210     
33211     get: function(brick_id) 
33212     {
33213         // if (typeof(this.groups[brick_id]) == 'undefined') {
33214         //     return false;
33215         // }
33216         // return this.groups[brick_id] ;
33217         
33218         if(this.groups.key(brick_id)) {
33219             return this.groups.key(brick_id);
33220         }
33221         
33222         return false;
33223     }
33224     
33225     
33226     
33227 });
33228
33229  /*
33230  * - LGPL
33231  *
33232  * element
33233  * 
33234  */
33235
33236 /**
33237  * @class Roo.bootstrap.Brick
33238  * @extends Roo.bootstrap.Component
33239  * Bootstrap Brick class
33240  * 
33241  * @constructor
33242  * Create a new Brick
33243  * @param {Object} config The config object
33244  */
33245
33246 Roo.bootstrap.Brick = function(config){
33247     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33248     
33249     this.addEvents({
33250         // raw events
33251         /**
33252          * @event click
33253          * When a Brick is click
33254          * @param {Roo.bootstrap.Brick} this
33255          * @param {Roo.EventObject} e
33256          */
33257         "click" : true
33258     });
33259 };
33260
33261 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33262     
33263     /**
33264      * @cfg {String} title
33265      */   
33266     title : '',
33267     /**
33268      * @cfg {String} html
33269      */   
33270     html : '',
33271     /**
33272      * @cfg {String} bgimage
33273      */   
33274     bgimage : '',
33275     /**
33276      * @cfg {String} cls
33277      */   
33278     cls : '',
33279     /**
33280      * @cfg {String} href
33281      */   
33282     href : '',
33283     /**
33284      * @cfg {String} video
33285      */   
33286     video : '',
33287     /**
33288      * @cfg {Boolean} square
33289      */   
33290     square : true,
33291     
33292     getAutoCreate : function()
33293     {
33294         var cls = 'roo-brick';
33295         
33296         if(this.href.length){
33297             cls += ' roo-brick-link';
33298         }
33299         
33300         if(this.bgimage.length){
33301             cls += ' roo-brick-image';
33302         }
33303         
33304         if(!this.html.length && !this.bgimage.length){
33305             cls += ' roo-brick-center-title';
33306         }
33307         
33308         if(!this.html.length && this.bgimage.length){
33309             cls += ' roo-brick-bottom-title';
33310         }
33311         
33312         if(this.cls){
33313             cls += ' ' + this.cls;
33314         }
33315         
33316         var cfg = {
33317             tag: (this.href.length) ? 'a' : 'div',
33318             cls: cls,
33319             cn: [
33320                 {
33321                     tag: 'div',
33322                     cls: 'roo-brick-paragraph',
33323                     cn: []
33324                 }
33325             ]
33326         };
33327         
33328         if(this.href.length){
33329             cfg.href = this.href;
33330         }
33331         
33332         var cn = cfg.cn[0].cn;
33333         
33334         if(this.title.length){
33335             cn.push({
33336                 tag: 'h4',
33337                 cls: 'roo-brick-title',
33338                 html: this.title
33339             });
33340         }
33341         
33342         if(this.html.length){
33343             cn.push({
33344                 tag: 'p',
33345                 cls: 'roo-brick-text',
33346                 html: this.html
33347             });
33348         } else {
33349             cn.cls += ' hide';
33350         }
33351         
33352         if(this.bgimage.length){
33353             cfg.cn.push({
33354                 tag: 'img',
33355                 cls: 'roo-brick-image-view',
33356                 src: this.bgimage
33357             });
33358         }
33359         
33360         return cfg;
33361     },
33362     
33363     initEvents: function() 
33364     {
33365         if(this.title.length || this.html.length){
33366             this.el.on('mouseenter'  ,this.enter, this);
33367             this.el.on('mouseleave', this.leave, this);
33368         }
33369         
33370         Roo.EventManager.onWindowResize(this.resize, this); 
33371         
33372         if(this.bgimage.length){
33373             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33374             this.imageEl.on('load', this.onImageLoad, this);
33375             return;
33376         }
33377         
33378         this.resize();
33379     },
33380     
33381     onImageLoad : function()
33382     {
33383         this.resize();
33384     },
33385     
33386     resize : function()
33387     {
33388         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33389         
33390         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33391         
33392         if(this.bgimage.length){
33393             var image = this.el.select('.roo-brick-image-view', true).first();
33394             
33395             image.setWidth(paragraph.getWidth());
33396             
33397             if(this.square){
33398                 image.setHeight(paragraph.getWidth());
33399             }
33400             
33401             this.el.setHeight(image.getHeight());
33402             paragraph.setHeight(image.getHeight());
33403             
33404         }
33405         
33406     },
33407     
33408     enter: function(e, el)
33409     {
33410         e.preventDefault();
33411         
33412         if(this.bgimage.length){
33413             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33414             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33415         }
33416     },
33417     
33418     leave: function(e, el)
33419     {
33420         e.preventDefault();
33421         
33422         if(this.bgimage.length){
33423             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33424             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33425         }
33426     }
33427     
33428 });
33429
33430  
33431
33432  /*
33433  * - LGPL
33434  *
33435  * Number field 
33436  */
33437
33438 /**
33439  * @class Roo.bootstrap.NumberField
33440  * @extends Roo.bootstrap.Input
33441  * Bootstrap NumberField class
33442  * 
33443  * 
33444  * 
33445  * 
33446  * @constructor
33447  * Create a new NumberField
33448  * @param {Object} config The config object
33449  */
33450
33451 Roo.bootstrap.NumberField = function(config){
33452     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33453 };
33454
33455 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33456     
33457     /**
33458      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33459      */
33460     allowDecimals : true,
33461     /**
33462      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33463      */
33464     decimalSeparator : ".",
33465     /**
33466      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33467      */
33468     decimalPrecision : 2,
33469     /**
33470      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33471      */
33472     allowNegative : true,
33473     
33474     /**
33475      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33476      */
33477     allowZero: true,
33478     /**
33479      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33480      */
33481     minValue : Number.NEGATIVE_INFINITY,
33482     /**
33483      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33484      */
33485     maxValue : Number.MAX_VALUE,
33486     /**
33487      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33488      */
33489     minText : "The minimum value for this field is {0}",
33490     /**
33491      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33492      */
33493     maxText : "The maximum value for this field is {0}",
33494     /**
33495      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33496      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33497      */
33498     nanText : "{0} is not a valid number",
33499     /**
33500      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33501      */
33502     thousandsDelimiter : false,
33503     /**
33504      * @cfg {String} valueAlign alignment of value
33505      */
33506     valueAlign : "left",
33507
33508     getAutoCreate : function()
33509     {
33510         var hiddenInput = {
33511             tag: 'input',
33512             type: 'hidden',
33513             id: Roo.id(),
33514             cls: 'hidden-number-input'
33515         };
33516         
33517         if (this.name) {
33518             hiddenInput.name = this.name;
33519         }
33520         
33521         this.name = '';
33522         
33523         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33524         
33525         this.name = hiddenInput.name;
33526         
33527         if(cfg.cn.length > 0) {
33528             cfg.cn.push(hiddenInput);
33529         }
33530         
33531         return cfg;
33532     },
33533
33534     // private
33535     initEvents : function()
33536     {   
33537         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33538         
33539         var allowed = "0123456789";
33540         
33541         if(this.allowDecimals){
33542             allowed += this.decimalSeparator;
33543         }
33544         
33545         if(this.allowNegative){
33546             allowed += "-";
33547         }
33548         
33549         if(this.thousandsDelimiter) {
33550             allowed += ",";
33551         }
33552         
33553         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33554         
33555         var keyPress = function(e){
33556             
33557             var k = e.getKey();
33558             
33559             var c = e.getCharCode();
33560             
33561             if(
33562                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33563                     allowed.indexOf(String.fromCharCode(c)) === -1
33564             ){
33565                 e.stopEvent();
33566                 return;
33567             }
33568             
33569             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33570                 return;
33571             }
33572             
33573             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33574                 e.stopEvent();
33575             }
33576         };
33577         
33578         this.el.on("keypress", keyPress, this);
33579     },
33580     
33581     validateValue : function(value)
33582     {
33583         
33584         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33585             return false;
33586         }
33587         
33588         var num = this.parseValue(value);
33589         
33590         if(isNaN(num)){
33591             this.markInvalid(String.format(this.nanText, value));
33592             return false;
33593         }
33594         
33595         if(num < this.minValue){
33596             this.markInvalid(String.format(this.minText, this.minValue));
33597             return false;
33598         }
33599         
33600         if(num > this.maxValue){
33601             this.markInvalid(String.format(this.maxText, this.maxValue));
33602             return false;
33603         }
33604         
33605         return true;
33606     },
33607
33608     getValue : function()
33609     {
33610         var v = this.hiddenEl().getValue();
33611         
33612         return this.fixPrecision(this.parseValue(v));
33613     },
33614
33615     parseValue : function(value)
33616     {
33617         if(this.thousandsDelimiter) {
33618             value += "";
33619             r = new RegExp(",", "g");
33620             value = value.replace(r, "");
33621         }
33622         
33623         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33624         return isNaN(value) ? '' : value;
33625     },
33626
33627     fixPrecision : function(value)
33628     {
33629         if(this.thousandsDelimiter) {
33630             value += "";
33631             r = new RegExp(",", "g");
33632             value = value.replace(r, "");
33633         }
33634         
33635         var nan = isNaN(value);
33636         
33637         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33638             return nan ? '' : value;
33639         }
33640         return parseFloat(value).toFixed(this.decimalPrecision);
33641     },
33642
33643     setValue : function(v)
33644     {
33645         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33646         
33647         this.value = v;
33648         
33649         if(this.rendered){
33650             
33651             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33652             
33653             this.inputEl().dom.value = (v == '') ? '' :
33654                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33655             
33656             if(!this.allowZero && v === '0') {
33657                 this.hiddenEl().dom.value = '';
33658                 this.inputEl().dom.value = '';
33659             }
33660             
33661             this.validate();
33662         }
33663     },
33664
33665     decimalPrecisionFcn : function(v)
33666     {
33667         return Math.floor(v);
33668     },
33669
33670     beforeBlur : function()
33671     {
33672         var v = this.parseValue(this.getRawValue());
33673         
33674         if(v || v === 0 || v === ''){
33675             this.setValue(v);
33676         }
33677     },
33678     
33679     hiddenEl : function()
33680     {
33681         return this.el.select('input.hidden-number-input',true).first();
33682     }
33683     
33684 });
33685
33686  
33687
33688 /*
33689 * Licence: LGPL
33690 */
33691
33692 /**
33693  * @class Roo.bootstrap.DocumentSlider
33694  * @extends Roo.bootstrap.Component
33695  * Bootstrap DocumentSlider class
33696  * 
33697  * @constructor
33698  * Create a new DocumentViewer
33699  * @param {Object} config The config object
33700  */
33701
33702 Roo.bootstrap.DocumentSlider = function(config){
33703     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33704     
33705     this.files = [];
33706     
33707     this.addEvents({
33708         /**
33709          * @event initial
33710          * Fire after initEvent
33711          * @param {Roo.bootstrap.DocumentSlider} this
33712          */
33713         "initial" : true,
33714         /**
33715          * @event update
33716          * Fire after update
33717          * @param {Roo.bootstrap.DocumentSlider} this
33718          */
33719         "update" : true,
33720         /**
33721          * @event click
33722          * Fire after click
33723          * @param {Roo.bootstrap.DocumentSlider} this
33724          */
33725         "click" : true
33726     });
33727 };
33728
33729 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33730     
33731     files : false,
33732     
33733     indicator : 0,
33734     
33735     getAutoCreate : function()
33736     {
33737         var cfg = {
33738             tag : 'div',
33739             cls : 'roo-document-slider',
33740             cn : [
33741                 {
33742                     tag : 'div',
33743                     cls : 'roo-document-slider-header',
33744                     cn : [
33745                         {
33746                             tag : 'div',
33747                             cls : 'roo-document-slider-header-title'
33748                         }
33749                     ]
33750                 },
33751                 {
33752                     tag : 'div',
33753                     cls : 'roo-document-slider-body',
33754                     cn : [
33755                         {
33756                             tag : 'div',
33757                             cls : 'roo-document-slider-prev',
33758                             cn : [
33759                                 {
33760                                     tag : 'i',
33761                                     cls : 'fa fa-chevron-left'
33762                                 }
33763                             ]
33764                         },
33765                         {
33766                             tag : 'div',
33767                             cls : 'roo-document-slider-thumb',
33768                             cn : [
33769                                 {
33770                                     tag : 'img',
33771                                     cls : 'roo-document-slider-image'
33772                                 }
33773                             ]
33774                         },
33775                         {
33776                             tag : 'div',
33777                             cls : 'roo-document-slider-next',
33778                             cn : [
33779                                 {
33780                                     tag : 'i',
33781                                     cls : 'fa fa-chevron-right'
33782                                 }
33783                             ]
33784                         }
33785                     ]
33786                 }
33787             ]
33788         };
33789         
33790         return cfg;
33791     },
33792     
33793     initEvents : function()
33794     {
33795         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33796         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33797         
33798         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33799         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33800         
33801         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33802         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33803         
33804         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33805         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33806         
33807         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33808         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33809         
33810         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33811         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33812         
33813         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33814         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33815         
33816         this.thumbEl.on('click', this.onClick, this);
33817         
33818         this.prevIndicator.on('click', this.prev, this);
33819         
33820         this.nextIndicator.on('click', this.next, this);
33821         
33822     },
33823     
33824     initial : function()
33825     {
33826         if(this.files.length){
33827             this.indicator = 1;
33828             this.update()
33829         }
33830         
33831         this.fireEvent('initial', this);
33832     },
33833     
33834     update : function()
33835     {
33836         this.imageEl.attr('src', this.files[this.indicator - 1]);
33837         
33838         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33839         
33840         this.prevIndicator.show();
33841         
33842         if(this.indicator == 1){
33843             this.prevIndicator.hide();
33844         }
33845         
33846         this.nextIndicator.show();
33847         
33848         if(this.indicator == this.files.length){
33849             this.nextIndicator.hide();
33850         }
33851         
33852         this.thumbEl.scrollTo('top');
33853         
33854         this.fireEvent('update', this);
33855     },
33856     
33857     onClick : function(e)
33858     {
33859         e.preventDefault();
33860         
33861         this.fireEvent('click', this);
33862     },
33863     
33864     prev : function(e)
33865     {
33866         e.preventDefault();
33867         
33868         this.indicator = Math.max(1, this.indicator - 1);
33869         
33870         this.update();
33871     },
33872     
33873     next : function(e)
33874     {
33875         e.preventDefault();
33876         
33877         this.indicator = Math.min(this.files.length, this.indicator + 1);
33878         
33879         this.update();
33880     }
33881 });
33882 /*
33883  * - LGPL
33884  *
33885  * RadioSet
33886  *
33887  *
33888  */
33889
33890 /**
33891  * @class Roo.bootstrap.RadioSet
33892  * @extends Roo.bootstrap.Input
33893  * Bootstrap RadioSet class
33894  * @cfg {String} indicatorpos (left|right) default left
33895  * @cfg {Boolean} inline (true|false) inline the element (default true)
33896  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33897  * @constructor
33898  * Create a new RadioSet
33899  * @param {Object} config The config object
33900  */
33901
33902 Roo.bootstrap.RadioSet = function(config){
33903     
33904     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33905     
33906     this.radioes = [];
33907     
33908     Roo.bootstrap.RadioSet.register(this);
33909     
33910     this.addEvents({
33911         /**
33912         * @event check
33913         * Fires when the element is checked or unchecked.
33914         * @param {Roo.bootstrap.RadioSet} this This radio
33915         * @param {Roo.bootstrap.Radio} item The checked item
33916         */
33917        check : true,
33918        /**
33919         * @event click
33920         * Fires when the element is click.
33921         * @param {Roo.bootstrap.RadioSet} this This radio set
33922         * @param {Roo.bootstrap.Radio} item The checked item
33923         * @param {Roo.EventObject} e The event object
33924         */
33925        click : true
33926     });
33927     
33928 };
33929
33930 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33931
33932     radioes : false,
33933     
33934     inline : true,
33935     
33936     weight : '',
33937     
33938     indicatorpos : 'left',
33939     
33940     getAutoCreate : function()
33941     {
33942         var label = {
33943             tag : 'label',
33944             cls : 'roo-radio-set-label',
33945             cn : [
33946                 {
33947                     tag : 'span',
33948                     html : this.fieldLabel
33949                 }
33950             ]
33951         };
33952         
33953         if(this.indicatorpos == 'left'){
33954             label.cn.unshift({
33955                 tag : 'i',
33956                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33957                 tooltip : 'This field is required'
33958             });
33959         } else {
33960             label.cn.push({
33961                 tag : 'i',
33962                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33963                 tooltip : 'This field is required'
33964             });
33965         }
33966         
33967         var items = {
33968             tag : 'div',
33969             cls : 'roo-radio-set-items'
33970         };
33971         
33972         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33973         
33974         if (align === 'left' && this.fieldLabel.length) {
33975             
33976             items = {
33977                 cls : "roo-radio-set-right", 
33978                 cn: [
33979                     items
33980                 ]
33981             };
33982             
33983             if(this.labelWidth > 12){
33984                 label.style = "width: " + this.labelWidth + 'px';
33985             }
33986             
33987             if(this.labelWidth < 13 && this.labelmd == 0){
33988                 this.labelmd = this.labelWidth;
33989             }
33990             
33991             if(this.labellg > 0){
33992                 label.cls += ' col-lg-' + this.labellg;
33993                 items.cls += ' col-lg-' + (12 - this.labellg);
33994             }
33995             
33996             if(this.labelmd > 0){
33997                 label.cls += ' col-md-' + this.labelmd;
33998                 items.cls += ' col-md-' + (12 - this.labelmd);
33999             }
34000             
34001             if(this.labelsm > 0){
34002                 label.cls += ' col-sm-' + this.labelsm;
34003                 items.cls += ' col-sm-' + (12 - this.labelsm);
34004             }
34005             
34006             if(this.labelxs > 0){
34007                 label.cls += ' col-xs-' + this.labelxs;
34008                 items.cls += ' col-xs-' + (12 - this.labelxs);
34009             }
34010         }
34011         
34012         var cfg = {
34013             tag : 'div',
34014             cls : 'roo-radio-set',
34015             cn : [
34016                 {
34017                     tag : 'input',
34018                     cls : 'roo-radio-set-input',
34019                     type : 'hidden',
34020                     name : this.name,
34021                     value : this.value ? this.value :  ''
34022                 },
34023                 label,
34024                 items
34025             ]
34026         };
34027         
34028         if(this.weight.length){
34029             cfg.cls += ' roo-radio-' + this.weight;
34030         }
34031         
34032         if(this.inline) {
34033             cfg.cls += ' roo-radio-set-inline';
34034         }
34035         
34036         var settings=this;
34037         ['xs','sm','md','lg'].map(function(size){
34038             if (settings[size]) {
34039                 cfg.cls += ' col-' + size + '-' + settings[size];
34040             }
34041         });
34042         
34043         return cfg;
34044         
34045     },
34046
34047     initEvents : function()
34048     {
34049         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34050         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34051         
34052         if(!this.fieldLabel.length){
34053             this.labelEl.hide();
34054         }
34055         
34056         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34057         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34058         
34059         this.indicator = this.indicatorEl();
34060         
34061         if(this.indicator){
34062             this.indicator.addClass('invisible');
34063         }
34064         
34065         this.originalValue = this.getValue();
34066         
34067     },
34068     
34069     inputEl: function ()
34070     {
34071         return this.el.select('.roo-radio-set-input', true).first();
34072     },
34073     
34074     getChildContainer : function()
34075     {
34076         return this.itemsEl;
34077     },
34078     
34079     register : function(item)
34080     {
34081         this.radioes.push(item);
34082         
34083     },
34084     
34085     validate : function()
34086     {   
34087         if(this.getVisibilityEl().hasClass('hidden')){
34088             return true;
34089         }
34090         
34091         var valid = false;
34092         
34093         Roo.each(this.radioes, function(i){
34094             if(!i.checked){
34095                 return;
34096             }
34097             
34098             valid = true;
34099             return false;
34100         });
34101         
34102         if(this.allowBlank) {
34103             return true;
34104         }
34105         
34106         if(this.disabled || valid){
34107             this.markValid();
34108             return true;
34109         }
34110         
34111         this.markInvalid();
34112         return false;
34113         
34114     },
34115     
34116     markValid : function()
34117     {
34118         if(this.labelEl.isVisible(true)){
34119             this.indicatorEl().removeClass('visible');
34120             this.indicatorEl().addClass('invisible');
34121         }
34122         
34123         this.el.removeClass([this.invalidClass, this.validClass]);
34124         this.el.addClass(this.validClass);
34125         
34126         this.fireEvent('valid', this);
34127     },
34128     
34129     markInvalid : function(msg)
34130     {
34131         if(this.allowBlank || this.disabled){
34132             return;
34133         }
34134         
34135         if(this.labelEl.isVisible(true)){
34136             this.indicatorEl().removeClass('invisible');
34137             this.indicatorEl().addClass('visible');
34138         }
34139         
34140         this.el.removeClass([this.invalidClass, this.validClass]);
34141         this.el.addClass(this.invalidClass);
34142         
34143         this.fireEvent('invalid', this, msg);
34144         
34145     },
34146     
34147     setValue : function(v, suppressEvent)
34148     {   
34149         if(this.value === v){
34150             return;
34151         }
34152         
34153         this.value = v;
34154         
34155         if(this.rendered){
34156             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34157         }
34158         
34159         Roo.each(this.radioes, function(i){
34160             i.checked = false;
34161             i.el.removeClass('checked');
34162         });
34163         
34164         Roo.each(this.radioes, function(i){
34165             
34166             if(i.value === v || i.value.toString() === v.toString()){
34167                 i.checked = true;
34168                 i.el.addClass('checked');
34169                 
34170                 if(suppressEvent !== true){
34171                     this.fireEvent('check', this, i);
34172                 }
34173                 
34174                 return false;
34175             }
34176             
34177         }, this);
34178         
34179         this.validate();
34180     },
34181     
34182     clearInvalid : function(){
34183         
34184         if(!this.el || this.preventMark){
34185             return;
34186         }
34187         
34188         this.el.removeClass([this.invalidClass]);
34189         
34190         this.fireEvent('valid', this);
34191     }
34192     
34193 });
34194
34195 Roo.apply(Roo.bootstrap.RadioSet, {
34196     
34197     groups: {},
34198     
34199     register : function(set)
34200     {
34201         this.groups[set.name] = set;
34202     },
34203     
34204     get: function(name) 
34205     {
34206         if (typeof(this.groups[name]) == 'undefined') {
34207             return false;
34208         }
34209         
34210         return this.groups[name] ;
34211     }
34212     
34213 });
34214 /*
34215  * Based on:
34216  * Ext JS Library 1.1.1
34217  * Copyright(c) 2006-2007, Ext JS, LLC.
34218  *
34219  * Originally Released Under LGPL - original licence link has changed is not relivant.
34220  *
34221  * Fork - LGPL
34222  * <script type="text/javascript">
34223  */
34224
34225
34226 /**
34227  * @class Roo.bootstrap.SplitBar
34228  * @extends Roo.util.Observable
34229  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34230  * <br><br>
34231  * Usage:
34232  * <pre><code>
34233 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34234                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34235 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34236 split.minSize = 100;
34237 split.maxSize = 600;
34238 split.animate = true;
34239 split.on('moved', splitterMoved);
34240 </code></pre>
34241  * @constructor
34242  * Create a new SplitBar
34243  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34244  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34245  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34246  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34247                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34248                         position of the SplitBar).
34249  */
34250 Roo.bootstrap.SplitBar = function(cfg){
34251     
34252     /** @private */
34253     
34254     //{
34255     //  dragElement : elm
34256     //  resizingElement: el,
34257         // optional..
34258     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34259     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34260         // existingProxy ???
34261     //}
34262     
34263     this.el = Roo.get(cfg.dragElement, true);
34264     this.el.dom.unselectable = "on";
34265     /** @private */
34266     this.resizingEl = Roo.get(cfg.resizingElement, true);
34267
34268     /**
34269      * @private
34270      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34271      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34272      * @type Number
34273      */
34274     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34275     
34276     /**
34277      * The minimum size of the resizing element. (Defaults to 0)
34278      * @type Number
34279      */
34280     this.minSize = 0;
34281     
34282     /**
34283      * The maximum size of the resizing element. (Defaults to 2000)
34284      * @type Number
34285      */
34286     this.maxSize = 2000;
34287     
34288     /**
34289      * Whether to animate the transition to the new size
34290      * @type Boolean
34291      */
34292     this.animate = false;
34293     
34294     /**
34295      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34296      * @type Boolean
34297      */
34298     this.useShim = false;
34299     
34300     /** @private */
34301     this.shim = null;
34302     
34303     if(!cfg.existingProxy){
34304         /** @private */
34305         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34306     }else{
34307         this.proxy = Roo.get(cfg.existingProxy).dom;
34308     }
34309     /** @private */
34310     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34311     
34312     /** @private */
34313     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34314     
34315     /** @private */
34316     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34317     
34318     /** @private */
34319     this.dragSpecs = {};
34320     
34321     /**
34322      * @private The adapter to use to positon and resize elements
34323      */
34324     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34325     this.adapter.init(this);
34326     
34327     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34328         /** @private */
34329         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34330         this.el.addClass("roo-splitbar-h");
34331     }else{
34332         /** @private */
34333         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34334         this.el.addClass("roo-splitbar-v");
34335     }
34336     
34337     this.addEvents({
34338         /**
34339          * @event resize
34340          * Fires when the splitter is moved (alias for {@link #event-moved})
34341          * @param {Roo.bootstrap.SplitBar} this
34342          * @param {Number} newSize the new width or height
34343          */
34344         "resize" : true,
34345         /**
34346          * @event moved
34347          * Fires when the splitter is moved
34348          * @param {Roo.bootstrap.SplitBar} this
34349          * @param {Number} newSize the new width or height
34350          */
34351         "moved" : true,
34352         /**
34353          * @event beforeresize
34354          * Fires before the splitter is dragged
34355          * @param {Roo.bootstrap.SplitBar} this
34356          */
34357         "beforeresize" : true,
34358
34359         "beforeapply" : true
34360     });
34361
34362     Roo.util.Observable.call(this);
34363 };
34364
34365 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34366     onStartProxyDrag : function(x, y){
34367         this.fireEvent("beforeresize", this);
34368         if(!this.overlay){
34369             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34370             o.unselectable();
34371             o.enableDisplayMode("block");
34372             // all splitbars share the same overlay
34373             Roo.bootstrap.SplitBar.prototype.overlay = o;
34374         }
34375         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34376         this.overlay.show();
34377         Roo.get(this.proxy).setDisplayed("block");
34378         var size = this.adapter.getElementSize(this);
34379         this.activeMinSize = this.getMinimumSize();;
34380         this.activeMaxSize = this.getMaximumSize();;
34381         var c1 = size - this.activeMinSize;
34382         var c2 = Math.max(this.activeMaxSize - size, 0);
34383         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34384             this.dd.resetConstraints();
34385             this.dd.setXConstraint(
34386                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34387                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34388             );
34389             this.dd.setYConstraint(0, 0);
34390         }else{
34391             this.dd.resetConstraints();
34392             this.dd.setXConstraint(0, 0);
34393             this.dd.setYConstraint(
34394                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34395                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34396             );
34397          }
34398         this.dragSpecs.startSize = size;
34399         this.dragSpecs.startPoint = [x, y];
34400         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34401     },
34402     
34403     /** 
34404      * @private Called after the drag operation by the DDProxy
34405      */
34406     onEndProxyDrag : function(e){
34407         Roo.get(this.proxy).setDisplayed(false);
34408         var endPoint = Roo.lib.Event.getXY(e);
34409         if(this.overlay){
34410             this.overlay.hide();
34411         }
34412         var newSize;
34413         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34414             newSize = this.dragSpecs.startSize + 
34415                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34416                     endPoint[0] - this.dragSpecs.startPoint[0] :
34417                     this.dragSpecs.startPoint[0] - endPoint[0]
34418                 );
34419         }else{
34420             newSize = this.dragSpecs.startSize + 
34421                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34422                     endPoint[1] - this.dragSpecs.startPoint[1] :
34423                     this.dragSpecs.startPoint[1] - endPoint[1]
34424                 );
34425         }
34426         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34427         if(newSize != this.dragSpecs.startSize){
34428             if(this.fireEvent('beforeapply', this, newSize) !== false){
34429                 this.adapter.setElementSize(this, newSize);
34430                 this.fireEvent("moved", this, newSize);
34431                 this.fireEvent("resize", this, newSize);
34432             }
34433         }
34434     },
34435     
34436     /**
34437      * Get the adapter this SplitBar uses
34438      * @return The adapter object
34439      */
34440     getAdapter : function(){
34441         return this.adapter;
34442     },
34443     
34444     /**
34445      * Set the adapter this SplitBar uses
34446      * @param {Object} adapter A SplitBar adapter object
34447      */
34448     setAdapter : function(adapter){
34449         this.adapter = adapter;
34450         this.adapter.init(this);
34451     },
34452     
34453     /**
34454      * Gets the minimum size for the resizing element
34455      * @return {Number} The minimum size
34456      */
34457     getMinimumSize : function(){
34458         return this.minSize;
34459     },
34460     
34461     /**
34462      * Sets the minimum size for the resizing element
34463      * @param {Number} minSize The minimum size
34464      */
34465     setMinimumSize : function(minSize){
34466         this.minSize = minSize;
34467     },
34468     
34469     /**
34470      * Gets the maximum size for the resizing element
34471      * @return {Number} The maximum size
34472      */
34473     getMaximumSize : function(){
34474         return this.maxSize;
34475     },
34476     
34477     /**
34478      * Sets the maximum size for the resizing element
34479      * @param {Number} maxSize The maximum size
34480      */
34481     setMaximumSize : function(maxSize){
34482         this.maxSize = maxSize;
34483     },
34484     
34485     /**
34486      * Sets the initialize size for the resizing element
34487      * @param {Number} size The initial size
34488      */
34489     setCurrentSize : function(size){
34490         var oldAnimate = this.animate;
34491         this.animate = false;
34492         this.adapter.setElementSize(this, size);
34493         this.animate = oldAnimate;
34494     },
34495     
34496     /**
34497      * Destroy this splitbar. 
34498      * @param {Boolean} removeEl True to remove the element
34499      */
34500     destroy : function(removeEl){
34501         if(this.shim){
34502             this.shim.remove();
34503         }
34504         this.dd.unreg();
34505         this.proxy.parentNode.removeChild(this.proxy);
34506         if(removeEl){
34507             this.el.remove();
34508         }
34509     }
34510 });
34511
34512 /**
34513  * @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.
34514  */
34515 Roo.bootstrap.SplitBar.createProxy = function(dir){
34516     var proxy = new Roo.Element(document.createElement("div"));
34517     proxy.unselectable();
34518     var cls = 'roo-splitbar-proxy';
34519     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34520     document.body.appendChild(proxy.dom);
34521     return proxy.dom;
34522 };
34523
34524 /** 
34525  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34526  * Default Adapter. It assumes the splitter and resizing element are not positioned
34527  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34528  */
34529 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34530 };
34531
34532 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34533     // do nothing for now
34534     init : function(s){
34535     
34536     },
34537     /**
34538      * Called before drag operations to get the current size of the resizing element. 
34539      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34540      */
34541      getElementSize : function(s){
34542         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34543             return s.resizingEl.getWidth();
34544         }else{
34545             return s.resizingEl.getHeight();
34546         }
34547     },
34548     
34549     /**
34550      * Called after drag operations to set the size of the resizing element.
34551      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34552      * @param {Number} newSize The new size to set
34553      * @param {Function} onComplete A function to be invoked when resizing is complete
34554      */
34555     setElementSize : function(s, newSize, onComplete){
34556         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34557             if(!s.animate){
34558                 s.resizingEl.setWidth(newSize);
34559                 if(onComplete){
34560                     onComplete(s, newSize);
34561                 }
34562             }else{
34563                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34564             }
34565         }else{
34566             
34567             if(!s.animate){
34568                 s.resizingEl.setHeight(newSize);
34569                 if(onComplete){
34570                     onComplete(s, newSize);
34571                 }
34572             }else{
34573                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34574             }
34575         }
34576     }
34577 };
34578
34579 /** 
34580  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34581  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34582  * Adapter that  moves the splitter element to align with the resized sizing element. 
34583  * Used with an absolute positioned SplitBar.
34584  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34585  * document.body, make sure you assign an id to the body element.
34586  */
34587 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34588     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34589     this.container = Roo.get(container);
34590 };
34591
34592 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34593     init : function(s){
34594         this.basic.init(s);
34595     },
34596     
34597     getElementSize : function(s){
34598         return this.basic.getElementSize(s);
34599     },
34600     
34601     setElementSize : function(s, newSize, onComplete){
34602         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34603     },
34604     
34605     moveSplitter : function(s){
34606         var yes = Roo.bootstrap.SplitBar;
34607         switch(s.placement){
34608             case yes.LEFT:
34609                 s.el.setX(s.resizingEl.getRight());
34610                 break;
34611             case yes.RIGHT:
34612                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34613                 break;
34614             case yes.TOP:
34615                 s.el.setY(s.resizingEl.getBottom());
34616                 break;
34617             case yes.BOTTOM:
34618                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34619                 break;
34620         }
34621     }
34622 };
34623
34624 /**
34625  * Orientation constant - Create a vertical SplitBar
34626  * @static
34627  * @type Number
34628  */
34629 Roo.bootstrap.SplitBar.VERTICAL = 1;
34630
34631 /**
34632  * Orientation constant - Create a horizontal SplitBar
34633  * @static
34634  * @type Number
34635  */
34636 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34637
34638 /**
34639  * Placement constant - The resizing element is to the left of the splitter element
34640  * @static
34641  * @type Number
34642  */
34643 Roo.bootstrap.SplitBar.LEFT = 1;
34644
34645 /**
34646  * Placement constant - The resizing element is to the right of the splitter element
34647  * @static
34648  * @type Number
34649  */
34650 Roo.bootstrap.SplitBar.RIGHT = 2;
34651
34652 /**
34653  * Placement constant - The resizing element is positioned above the splitter element
34654  * @static
34655  * @type Number
34656  */
34657 Roo.bootstrap.SplitBar.TOP = 3;
34658
34659 /**
34660  * Placement constant - The resizing element is positioned under splitter element
34661  * @static
34662  * @type Number
34663  */
34664 Roo.bootstrap.SplitBar.BOTTOM = 4;
34665 Roo.namespace("Roo.bootstrap.layout");/*
34666  * Based on:
34667  * Ext JS Library 1.1.1
34668  * Copyright(c) 2006-2007, Ext JS, LLC.
34669  *
34670  * Originally Released Under LGPL - original licence link has changed is not relivant.
34671  *
34672  * Fork - LGPL
34673  * <script type="text/javascript">
34674  */
34675
34676 /**
34677  * @class Roo.bootstrap.layout.Manager
34678  * @extends Roo.bootstrap.Component
34679  * Base class for layout managers.
34680  */
34681 Roo.bootstrap.layout.Manager = function(config)
34682 {
34683     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34684
34685
34686
34687
34688
34689     /** false to disable window resize monitoring @type Boolean */
34690     this.monitorWindowResize = true;
34691     this.regions = {};
34692     this.addEvents({
34693         /**
34694          * @event layout
34695          * Fires when a layout is performed.
34696          * @param {Roo.LayoutManager} this
34697          */
34698         "layout" : true,
34699         /**
34700          * @event regionresized
34701          * Fires when the user resizes a region.
34702          * @param {Roo.LayoutRegion} region The resized region
34703          * @param {Number} newSize The new size (width for east/west, height for north/south)
34704          */
34705         "regionresized" : true,
34706         /**
34707          * @event regioncollapsed
34708          * Fires when a region is collapsed.
34709          * @param {Roo.LayoutRegion} region The collapsed region
34710          */
34711         "regioncollapsed" : true,
34712         /**
34713          * @event regionexpanded
34714          * Fires when a region is expanded.
34715          * @param {Roo.LayoutRegion} region The expanded region
34716          */
34717         "regionexpanded" : true
34718     });
34719     this.updating = false;
34720
34721     if (config.el) {
34722         this.el = Roo.get(config.el);
34723         this.initEvents();
34724     }
34725
34726 };
34727
34728 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34729
34730
34731     regions : null,
34732
34733     monitorWindowResize : true,
34734
34735
34736     updating : false,
34737
34738
34739     onRender : function(ct, position)
34740     {
34741         if(!this.el){
34742             this.el = Roo.get(ct);
34743             this.initEvents();
34744         }
34745         //this.fireEvent('render',this);
34746     },
34747
34748
34749     initEvents: function()
34750     {
34751
34752
34753         // ie scrollbar fix
34754         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34755             document.body.scroll = "no";
34756         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34757             this.el.position('relative');
34758         }
34759         this.id = this.el.id;
34760         this.el.addClass("roo-layout-container");
34761         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34762         if(this.el.dom != document.body ) {
34763             this.el.on('resize', this.layout,this);
34764             this.el.on('show', this.layout,this);
34765         }
34766
34767     },
34768
34769     /**
34770      * Returns true if this layout is currently being updated
34771      * @return {Boolean}
34772      */
34773     isUpdating : function(){
34774         return this.updating;
34775     },
34776
34777     /**
34778      * Suspend the LayoutManager from doing auto-layouts while
34779      * making multiple add or remove calls
34780      */
34781     beginUpdate : function(){
34782         this.updating = true;
34783     },
34784
34785     /**
34786      * Restore auto-layouts and optionally disable the manager from performing a layout
34787      * @param {Boolean} noLayout true to disable a layout update
34788      */
34789     endUpdate : function(noLayout){
34790         this.updating = false;
34791         if(!noLayout){
34792             this.layout();
34793         }
34794     },
34795
34796     layout: function(){
34797         // abstract...
34798     },
34799
34800     onRegionResized : function(region, newSize){
34801         this.fireEvent("regionresized", region, newSize);
34802         this.layout();
34803     },
34804
34805     onRegionCollapsed : function(region){
34806         this.fireEvent("regioncollapsed", region);
34807     },
34808
34809     onRegionExpanded : function(region){
34810         this.fireEvent("regionexpanded", region);
34811     },
34812
34813     /**
34814      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34815      * performs box-model adjustments.
34816      * @return {Object} The size as an object {width: (the width), height: (the height)}
34817      */
34818     getViewSize : function()
34819     {
34820         var size;
34821         if(this.el.dom != document.body){
34822             size = this.el.getSize();
34823         }else{
34824             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34825         }
34826         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34827         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34828         return size;
34829     },
34830
34831     /**
34832      * Returns the Element this layout is bound to.
34833      * @return {Roo.Element}
34834      */
34835     getEl : function(){
34836         return this.el;
34837     },
34838
34839     /**
34840      * Returns the specified region.
34841      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34842      * @return {Roo.LayoutRegion}
34843      */
34844     getRegion : function(target){
34845         return this.regions[target.toLowerCase()];
34846     },
34847
34848     onWindowResize : function(){
34849         if(this.monitorWindowResize){
34850             this.layout();
34851         }
34852     }
34853 });
34854 /*
34855  * Based on:
34856  * Ext JS Library 1.1.1
34857  * Copyright(c) 2006-2007, Ext JS, LLC.
34858  *
34859  * Originally Released Under LGPL - original licence link has changed is not relivant.
34860  *
34861  * Fork - LGPL
34862  * <script type="text/javascript">
34863  */
34864 /**
34865  * @class Roo.bootstrap.layout.Border
34866  * @extends Roo.bootstrap.layout.Manager
34867  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34868  * please see: examples/bootstrap/nested.html<br><br>
34869  
34870 <b>The container the layout is rendered into can be either the body element or any other element.
34871 If it is not the body element, the container needs to either be an absolute positioned element,
34872 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34873 the container size if it is not the body element.</b>
34874
34875 * @constructor
34876 * Create a new Border
34877 * @param {Object} config Configuration options
34878  */
34879 Roo.bootstrap.layout.Border = function(config){
34880     config = config || {};
34881     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34882     
34883     
34884     
34885     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34886         if(config[region]){
34887             config[region].region = region;
34888             this.addRegion(config[region]);
34889         }
34890     },this);
34891     
34892 };
34893
34894 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34895
34896 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34897     /**
34898      * Creates and adds a new region if it doesn't already exist.
34899      * @param {String} target The target region key (north, south, east, west or center).
34900      * @param {Object} config The regions config object
34901      * @return {BorderLayoutRegion} The new region
34902      */
34903     addRegion : function(config)
34904     {
34905         if(!this.regions[config.region]){
34906             var r = this.factory(config);
34907             this.bindRegion(r);
34908         }
34909         return this.regions[config.region];
34910     },
34911
34912     // private (kinda)
34913     bindRegion : function(r){
34914         this.regions[r.config.region] = r;
34915         
34916         r.on("visibilitychange",    this.layout, this);
34917         r.on("paneladded",          this.layout, this);
34918         r.on("panelremoved",        this.layout, this);
34919         r.on("invalidated",         this.layout, this);
34920         r.on("resized",             this.onRegionResized, this);
34921         r.on("collapsed",           this.onRegionCollapsed, this);
34922         r.on("expanded",            this.onRegionExpanded, this);
34923     },
34924
34925     /**
34926      * Performs a layout update.
34927      */
34928     layout : function()
34929     {
34930         if(this.updating) {
34931             return;
34932         }
34933         
34934         // render all the rebions if they have not been done alreayd?
34935         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34936             if(this.regions[region] && !this.regions[region].bodyEl){
34937                 this.regions[region].onRender(this.el)
34938             }
34939         },this);
34940         
34941         var size = this.getViewSize();
34942         var w = size.width;
34943         var h = size.height;
34944         var centerW = w;
34945         var centerH = h;
34946         var centerY = 0;
34947         var centerX = 0;
34948         //var x = 0, y = 0;
34949
34950         var rs = this.regions;
34951         var north = rs["north"];
34952         var south = rs["south"]; 
34953         var west = rs["west"];
34954         var east = rs["east"];
34955         var center = rs["center"];
34956         //if(this.hideOnLayout){ // not supported anymore
34957             //c.el.setStyle("display", "none");
34958         //}
34959         if(north && north.isVisible()){
34960             var b = north.getBox();
34961             var m = north.getMargins();
34962             b.width = w - (m.left+m.right);
34963             b.x = m.left;
34964             b.y = m.top;
34965             centerY = b.height + b.y + m.bottom;
34966             centerH -= centerY;
34967             north.updateBox(this.safeBox(b));
34968         }
34969         if(south && south.isVisible()){
34970             var b = south.getBox();
34971             var m = south.getMargins();
34972             b.width = w - (m.left+m.right);
34973             b.x = m.left;
34974             var totalHeight = (b.height + m.top + m.bottom);
34975             b.y = h - totalHeight + m.top;
34976             centerH -= totalHeight;
34977             south.updateBox(this.safeBox(b));
34978         }
34979         if(west && west.isVisible()){
34980             var b = west.getBox();
34981             var m = west.getMargins();
34982             b.height = centerH - (m.top+m.bottom);
34983             b.x = m.left;
34984             b.y = centerY + m.top;
34985             var totalWidth = (b.width + m.left + m.right);
34986             centerX += totalWidth;
34987             centerW -= totalWidth;
34988             west.updateBox(this.safeBox(b));
34989         }
34990         if(east && east.isVisible()){
34991             var b = east.getBox();
34992             var m = east.getMargins();
34993             b.height = centerH - (m.top+m.bottom);
34994             var totalWidth = (b.width + m.left + m.right);
34995             b.x = w - totalWidth + m.left;
34996             b.y = centerY + m.top;
34997             centerW -= totalWidth;
34998             east.updateBox(this.safeBox(b));
34999         }
35000         if(center){
35001             var m = center.getMargins();
35002             var centerBox = {
35003                 x: centerX + m.left,
35004                 y: centerY + m.top,
35005                 width: centerW - (m.left+m.right),
35006                 height: centerH - (m.top+m.bottom)
35007             };
35008             //if(this.hideOnLayout){
35009                 //center.el.setStyle("display", "block");
35010             //}
35011             center.updateBox(this.safeBox(centerBox));
35012         }
35013         this.el.repaint();
35014         this.fireEvent("layout", this);
35015     },
35016
35017     // private
35018     safeBox : function(box){
35019         box.width = Math.max(0, box.width);
35020         box.height = Math.max(0, box.height);
35021         return box;
35022     },
35023
35024     /**
35025      * Adds a ContentPanel (or subclass) to this layout.
35026      * @param {String} target The target region key (north, south, east, west or center).
35027      * @param {Roo.ContentPanel} panel The panel to add
35028      * @return {Roo.ContentPanel} The added panel
35029      */
35030     add : function(target, panel){
35031          
35032         target = target.toLowerCase();
35033         return this.regions[target].add(panel);
35034     },
35035
35036     /**
35037      * Remove a ContentPanel (or subclass) to this layout.
35038      * @param {String} target The target region key (north, south, east, west or center).
35039      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35040      * @return {Roo.ContentPanel} The removed panel
35041      */
35042     remove : function(target, panel){
35043         target = target.toLowerCase();
35044         return this.regions[target].remove(panel);
35045     },
35046
35047     /**
35048      * Searches all regions for a panel with the specified id
35049      * @param {String} panelId
35050      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35051      */
35052     findPanel : function(panelId){
35053         var rs = this.regions;
35054         for(var target in rs){
35055             if(typeof rs[target] != "function"){
35056                 var p = rs[target].getPanel(panelId);
35057                 if(p){
35058                     return p;
35059                 }
35060             }
35061         }
35062         return null;
35063     },
35064
35065     /**
35066      * Searches all regions for a panel with the specified id and activates (shows) it.
35067      * @param {String/ContentPanel} panelId The panels id or the panel itself
35068      * @return {Roo.ContentPanel} The shown panel or null
35069      */
35070     showPanel : function(panelId) {
35071       var rs = this.regions;
35072       for(var target in rs){
35073          var r = rs[target];
35074          if(typeof r != "function"){
35075             if(r.hasPanel(panelId)){
35076                return r.showPanel(panelId);
35077             }
35078          }
35079       }
35080       return null;
35081    },
35082
35083    /**
35084      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35085      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35086      */
35087    /*
35088     restoreState : function(provider){
35089         if(!provider){
35090             provider = Roo.state.Manager;
35091         }
35092         var sm = new Roo.LayoutStateManager();
35093         sm.init(this, provider);
35094     },
35095 */
35096  
35097  
35098     /**
35099      * Adds a xtype elements to the layout.
35100      * <pre><code>
35101
35102 layout.addxtype({
35103        xtype : 'ContentPanel',
35104        region: 'west',
35105        items: [ .... ]
35106    }
35107 );
35108
35109 layout.addxtype({
35110         xtype : 'NestedLayoutPanel',
35111         region: 'west',
35112         layout: {
35113            center: { },
35114            west: { }   
35115         },
35116         items : [ ... list of content panels or nested layout panels.. ]
35117    }
35118 );
35119 </code></pre>
35120      * @param {Object} cfg Xtype definition of item to add.
35121      */
35122     addxtype : function(cfg)
35123     {
35124         // basically accepts a pannel...
35125         // can accept a layout region..!?!?
35126         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35127         
35128         
35129         // theory?  children can only be panels??
35130         
35131         //if (!cfg.xtype.match(/Panel$/)) {
35132         //    return false;
35133         //}
35134         var ret = false;
35135         
35136         if (typeof(cfg.region) == 'undefined') {
35137             Roo.log("Failed to add Panel, region was not set");
35138             Roo.log(cfg);
35139             return false;
35140         }
35141         var region = cfg.region;
35142         delete cfg.region;
35143         
35144           
35145         var xitems = [];
35146         if (cfg.items) {
35147             xitems = cfg.items;
35148             delete cfg.items;
35149         }
35150         var nb = false;
35151         
35152         switch(cfg.xtype) 
35153         {
35154             case 'Content':  // ContentPanel (el, cfg)
35155             case 'Scroll':  // ContentPanel (el, cfg)
35156             case 'View': 
35157                 cfg.autoCreate = true;
35158                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35159                 //} else {
35160                 //    var el = this.el.createChild();
35161                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35162                 //}
35163                 
35164                 this.add(region, ret);
35165                 break;
35166             
35167             /*
35168             case 'TreePanel': // our new panel!
35169                 cfg.el = this.el.createChild();
35170                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35171                 this.add(region, ret);
35172                 break;
35173             */
35174             
35175             case 'Nest': 
35176                 // create a new Layout (which is  a Border Layout...
35177                 
35178                 var clayout = cfg.layout;
35179                 clayout.el  = this.el.createChild();
35180                 clayout.items   = clayout.items  || [];
35181                 
35182                 delete cfg.layout;
35183                 
35184                 // replace this exitems with the clayout ones..
35185                 xitems = clayout.items;
35186                  
35187                 // force background off if it's in center...
35188                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35189                     cfg.background = false;
35190                 }
35191                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35192                 
35193                 
35194                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35195                 //console.log('adding nested layout panel '  + cfg.toSource());
35196                 this.add(region, ret);
35197                 nb = {}; /// find first...
35198                 break;
35199             
35200             case 'Grid':
35201                 
35202                 // needs grid and region
35203                 
35204                 //var el = this.getRegion(region).el.createChild();
35205                 /*
35206                  *var el = this.el.createChild();
35207                 // create the grid first...
35208                 cfg.grid.container = el;
35209                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35210                 */
35211                 
35212                 if (region == 'center' && this.active ) {
35213                     cfg.background = false;
35214                 }
35215                 
35216                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35217                 
35218                 this.add(region, ret);
35219                 /*
35220                 if (cfg.background) {
35221                     // render grid on panel activation (if panel background)
35222                     ret.on('activate', function(gp) {
35223                         if (!gp.grid.rendered) {
35224                     //        gp.grid.render(el);
35225                         }
35226                     });
35227                 } else {
35228                   //  cfg.grid.render(el);
35229                 }
35230                 */
35231                 break;
35232            
35233            
35234             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35235                 // it was the old xcomponent building that caused this before.
35236                 // espeically if border is the top element in the tree.
35237                 ret = this;
35238                 break; 
35239                 
35240                     
35241                 
35242                 
35243                 
35244             default:
35245                 /*
35246                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35247                     
35248                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35249                     this.add(region, ret);
35250                 } else {
35251                 */
35252                     Roo.log(cfg);
35253                     throw "Can not add '" + cfg.xtype + "' to Border";
35254                     return null;
35255              
35256                                 
35257              
35258         }
35259         this.beginUpdate();
35260         // add children..
35261         var region = '';
35262         var abn = {};
35263         Roo.each(xitems, function(i)  {
35264             region = nb && i.region ? i.region : false;
35265             
35266             var add = ret.addxtype(i);
35267            
35268             if (region) {
35269                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35270                 if (!i.background) {
35271                     abn[region] = nb[region] ;
35272                 }
35273             }
35274             
35275         });
35276         this.endUpdate();
35277
35278         // make the last non-background panel active..
35279         //if (nb) { Roo.log(abn); }
35280         if (nb) {
35281             
35282             for(var r in abn) {
35283                 region = this.getRegion(r);
35284                 if (region) {
35285                     // tried using nb[r], but it does not work..
35286                      
35287                     region.showPanel(abn[r]);
35288                    
35289                 }
35290             }
35291         }
35292         return ret;
35293         
35294     },
35295     
35296     
35297 // private
35298     factory : function(cfg)
35299     {
35300         
35301         var validRegions = Roo.bootstrap.layout.Border.regions;
35302
35303         var target = cfg.region;
35304         cfg.mgr = this;
35305         
35306         var r = Roo.bootstrap.layout;
35307         Roo.log(target);
35308         switch(target){
35309             case "north":
35310                 return new r.North(cfg);
35311             case "south":
35312                 return new r.South(cfg);
35313             case "east":
35314                 return new r.East(cfg);
35315             case "west":
35316                 return new r.West(cfg);
35317             case "center":
35318                 return new r.Center(cfg);
35319         }
35320         throw 'Layout region "'+target+'" not supported.';
35321     }
35322     
35323     
35324 });
35325  /*
35326  * Based on:
35327  * Ext JS Library 1.1.1
35328  * Copyright(c) 2006-2007, Ext JS, LLC.
35329  *
35330  * Originally Released Under LGPL - original licence link has changed is not relivant.
35331  *
35332  * Fork - LGPL
35333  * <script type="text/javascript">
35334  */
35335  
35336 /**
35337  * @class Roo.bootstrap.layout.Basic
35338  * @extends Roo.util.Observable
35339  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35340  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35341  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35342  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35343  * @cfg {string}   region  the region that it inhabits..
35344  * @cfg {bool}   skipConfig skip config?
35345  * 
35346
35347  */
35348 Roo.bootstrap.layout.Basic = function(config){
35349     
35350     this.mgr = config.mgr;
35351     
35352     this.position = config.region;
35353     
35354     var skipConfig = config.skipConfig;
35355     
35356     this.events = {
35357         /**
35358          * @scope Roo.BasicLayoutRegion
35359          */
35360         
35361         /**
35362          * @event beforeremove
35363          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35364          * @param {Roo.LayoutRegion} this
35365          * @param {Roo.ContentPanel} panel The panel
35366          * @param {Object} e The cancel event object
35367          */
35368         "beforeremove" : true,
35369         /**
35370          * @event invalidated
35371          * Fires when the layout for this region is changed.
35372          * @param {Roo.LayoutRegion} this
35373          */
35374         "invalidated" : true,
35375         /**
35376          * @event visibilitychange
35377          * Fires when this region is shown or hidden 
35378          * @param {Roo.LayoutRegion} this
35379          * @param {Boolean} visibility true or false
35380          */
35381         "visibilitychange" : true,
35382         /**
35383          * @event paneladded
35384          * Fires when a panel is added. 
35385          * @param {Roo.LayoutRegion} this
35386          * @param {Roo.ContentPanel} panel The panel
35387          */
35388         "paneladded" : true,
35389         /**
35390          * @event panelremoved
35391          * Fires when a panel is removed. 
35392          * @param {Roo.LayoutRegion} this
35393          * @param {Roo.ContentPanel} panel The panel
35394          */
35395         "panelremoved" : true,
35396         /**
35397          * @event beforecollapse
35398          * Fires when this region before collapse.
35399          * @param {Roo.LayoutRegion} this
35400          */
35401         "beforecollapse" : true,
35402         /**
35403          * @event collapsed
35404          * Fires when this region is collapsed.
35405          * @param {Roo.LayoutRegion} this
35406          */
35407         "collapsed" : true,
35408         /**
35409          * @event expanded
35410          * Fires when this region is expanded.
35411          * @param {Roo.LayoutRegion} this
35412          */
35413         "expanded" : true,
35414         /**
35415          * @event slideshow
35416          * Fires when this region is slid into view.
35417          * @param {Roo.LayoutRegion} this
35418          */
35419         "slideshow" : true,
35420         /**
35421          * @event slidehide
35422          * Fires when this region slides out of view. 
35423          * @param {Roo.LayoutRegion} this
35424          */
35425         "slidehide" : true,
35426         /**
35427          * @event panelactivated
35428          * Fires when a panel is activated. 
35429          * @param {Roo.LayoutRegion} this
35430          * @param {Roo.ContentPanel} panel The activated panel
35431          */
35432         "panelactivated" : true,
35433         /**
35434          * @event resized
35435          * Fires when the user resizes this region. 
35436          * @param {Roo.LayoutRegion} this
35437          * @param {Number} newSize The new size (width for east/west, height for north/south)
35438          */
35439         "resized" : true
35440     };
35441     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35442     this.panels = new Roo.util.MixedCollection();
35443     this.panels.getKey = this.getPanelId.createDelegate(this);
35444     this.box = null;
35445     this.activePanel = null;
35446     // ensure listeners are added...
35447     
35448     if (config.listeners || config.events) {
35449         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35450             listeners : config.listeners || {},
35451             events : config.events || {}
35452         });
35453     }
35454     
35455     if(skipConfig !== true){
35456         this.applyConfig(config);
35457     }
35458 };
35459
35460 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35461 {
35462     getPanelId : function(p){
35463         return p.getId();
35464     },
35465     
35466     applyConfig : function(config){
35467         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35468         this.config = config;
35469         
35470     },
35471     
35472     /**
35473      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35474      * the width, for horizontal (north, south) the height.
35475      * @param {Number} newSize The new width or height
35476      */
35477     resizeTo : function(newSize){
35478         var el = this.el ? this.el :
35479                  (this.activePanel ? this.activePanel.getEl() : null);
35480         if(el){
35481             switch(this.position){
35482                 case "east":
35483                 case "west":
35484                     el.setWidth(newSize);
35485                     this.fireEvent("resized", this, newSize);
35486                 break;
35487                 case "north":
35488                 case "south":
35489                     el.setHeight(newSize);
35490                     this.fireEvent("resized", this, newSize);
35491                 break;                
35492             }
35493         }
35494     },
35495     
35496     getBox : function(){
35497         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35498     },
35499     
35500     getMargins : function(){
35501         return this.margins;
35502     },
35503     
35504     updateBox : function(box){
35505         this.box = box;
35506         var el = this.activePanel.getEl();
35507         el.dom.style.left = box.x + "px";
35508         el.dom.style.top = box.y + "px";
35509         this.activePanel.setSize(box.width, box.height);
35510     },
35511     
35512     /**
35513      * Returns the container element for this region.
35514      * @return {Roo.Element}
35515      */
35516     getEl : function(){
35517         return this.activePanel;
35518     },
35519     
35520     /**
35521      * Returns true if this region is currently visible.
35522      * @return {Boolean}
35523      */
35524     isVisible : function(){
35525         return this.activePanel ? true : false;
35526     },
35527     
35528     setActivePanel : function(panel){
35529         panel = this.getPanel(panel);
35530         if(this.activePanel && this.activePanel != panel){
35531             this.activePanel.setActiveState(false);
35532             this.activePanel.getEl().setLeftTop(-10000,-10000);
35533         }
35534         this.activePanel = panel;
35535         panel.setActiveState(true);
35536         if(this.box){
35537             panel.setSize(this.box.width, this.box.height);
35538         }
35539         this.fireEvent("panelactivated", this, panel);
35540         this.fireEvent("invalidated");
35541     },
35542     
35543     /**
35544      * Show the specified panel.
35545      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35546      * @return {Roo.ContentPanel} The shown panel or null
35547      */
35548     showPanel : function(panel){
35549         panel = this.getPanel(panel);
35550         if(panel){
35551             this.setActivePanel(panel);
35552         }
35553         return panel;
35554     },
35555     
35556     /**
35557      * Get the active panel for this region.
35558      * @return {Roo.ContentPanel} The active panel or null
35559      */
35560     getActivePanel : function(){
35561         return this.activePanel;
35562     },
35563     
35564     /**
35565      * Add the passed ContentPanel(s)
35566      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35567      * @return {Roo.ContentPanel} The panel added (if only one was added)
35568      */
35569     add : function(panel){
35570         if(arguments.length > 1){
35571             for(var i = 0, len = arguments.length; i < len; i++) {
35572                 this.add(arguments[i]);
35573             }
35574             return null;
35575         }
35576         if(this.hasPanel(panel)){
35577             this.showPanel(panel);
35578             return panel;
35579         }
35580         var el = panel.getEl();
35581         if(el.dom.parentNode != this.mgr.el.dom){
35582             this.mgr.el.dom.appendChild(el.dom);
35583         }
35584         if(panel.setRegion){
35585             panel.setRegion(this);
35586         }
35587         this.panels.add(panel);
35588         el.setStyle("position", "absolute");
35589         if(!panel.background){
35590             this.setActivePanel(panel);
35591             if(this.config.initialSize && this.panels.getCount()==1){
35592                 this.resizeTo(this.config.initialSize);
35593             }
35594         }
35595         this.fireEvent("paneladded", this, panel);
35596         return panel;
35597     },
35598     
35599     /**
35600      * Returns true if the panel is in this region.
35601      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35602      * @return {Boolean}
35603      */
35604     hasPanel : function(panel){
35605         if(typeof panel == "object"){ // must be panel obj
35606             panel = panel.getId();
35607         }
35608         return this.getPanel(panel) ? true : false;
35609     },
35610     
35611     /**
35612      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35613      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35614      * @param {Boolean} preservePanel Overrides the config preservePanel option
35615      * @return {Roo.ContentPanel} The panel that was removed
35616      */
35617     remove : function(panel, preservePanel){
35618         panel = this.getPanel(panel);
35619         if(!panel){
35620             return null;
35621         }
35622         var e = {};
35623         this.fireEvent("beforeremove", this, panel, e);
35624         if(e.cancel === true){
35625             return null;
35626         }
35627         var panelId = panel.getId();
35628         this.panels.removeKey(panelId);
35629         return panel;
35630     },
35631     
35632     /**
35633      * Returns the panel specified or null if it's not in this region.
35634      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35635      * @return {Roo.ContentPanel}
35636      */
35637     getPanel : function(id){
35638         if(typeof id == "object"){ // must be panel obj
35639             return id;
35640         }
35641         return this.panels.get(id);
35642     },
35643     
35644     /**
35645      * Returns this regions position (north/south/east/west/center).
35646      * @return {String} 
35647      */
35648     getPosition: function(){
35649         return this.position;    
35650     }
35651 });/*
35652  * Based on:
35653  * Ext JS Library 1.1.1
35654  * Copyright(c) 2006-2007, Ext JS, LLC.
35655  *
35656  * Originally Released Under LGPL - original licence link has changed is not relivant.
35657  *
35658  * Fork - LGPL
35659  * <script type="text/javascript">
35660  */
35661  
35662 /**
35663  * @class Roo.bootstrap.layout.Region
35664  * @extends Roo.bootstrap.layout.Basic
35665  * This class represents a region in a layout manager.
35666  
35667  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35668  * @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})
35669  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35670  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35671  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35672  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35673  * @cfg {String}    title           The title for the region (overrides panel titles)
35674  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35675  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35676  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35677  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35678  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35679  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35680  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35681  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35682  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35683  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35684
35685  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35686  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35687  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35688  * @cfg {Number}    width           For East/West panels
35689  * @cfg {Number}    height          For North/South panels
35690  * @cfg {Boolean}   split           To show the splitter
35691  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35692  * 
35693  * @cfg {string}   cls             Extra CSS classes to add to region
35694  * 
35695  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35696  * @cfg {string}   region  the region that it inhabits..
35697  *
35698
35699  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35700  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35701
35702  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35703  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35704  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35705  */
35706 Roo.bootstrap.layout.Region = function(config)
35707 {
35708     this.applyConfig(config);
35709
35710     var mgr = config.mgr;
35711     var pos = config.region;
35712     config.skipConfig = true;
35713     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35714     
35715     if (mgr.el) {
35716         this.onRender(mgr.el);   
35717     }
35718      
35719     this.visible = true;
35720     this.collapsed = false;
35721     this.unrendered_panels = [];
35722 };
35723
35724 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35725
35726     position: '', // set by wrapper (eg. north/south etc..)
35727     unrendered_panels : null,  // unrendered panels.
35728     createBody : function(){
35729         /** This region's body element 
35730         * @type Roo.Element */
35731         this.bodyEl = this.el.createChild({
35732                 tag: "div",
35733                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35734         });
35735     },
35736
35737     onRender: function(ctr, pos)
35738     {
35739         var dh = Roo.DomHelper;
35740         /** This region's container element 
35741         * @type Roo.Element */
35742         this.el = dh.append(ctr.dom, {
35743                 tag: "div",
35744                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35745             }, true);
35746         /** This region's title element 
35747         * @type Roo.Element */
35748     
35749         this.titleEl = dh.append(this.el.dom,
35750             {
35751                     tag: "div",
35752                     unselectable: "on",
35753                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35754                     children:[
35755                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35756                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35757                     ]}, true);
35758         
35759         this.titleEl.enableDisplayMode();
35760         /** This region's title text element 
35761         * @type HTMLElement */
35762         this.titleTextEl = this.titleEl.dom.firstChild;
35763         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35764         /*
35765         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35766         this.closeBtn.enableDisplayMode();
35767         this.closeBtn.on("click", this.closeClicked, this);
35768         this.closeBtn.hide();
35769     */
35770         this.createBody(this.config);
35771         if(this.config.hideWhenEmpty){
35772             this.hide();
35773             this.on("paneladded", this.validateVisibility, this);
35774             this.on("panelremoved", this.validateVisibility, this);
35775         }
35776         if(this.autoScroll){
35777             this.bodyEl.setStyle("overflow", "auto");
35778         }else{
35779             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35780         }
35781         //if(c.titlebar !== false){
35782             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35783                 this.titleEl.hide();
35784             }else{
35785                 this.titleEl.show();
35786                 if(this.config.title){
35787                     this.titleTextEl.innerHTML = this.config.title;
35788                 }
35789             }
35790         //}
35791         if(this.config.collapsed){
35792             this.collapse(true);
35793         }
35794         if(this.config.hidden){
35795             this.hide();
35796         }
35797         
35798         if (this.unrendered_panels && this.unrendered_panels.length) {
35799             for (var i =0;i< this.unrendered_panels.length; i++) {
35800                 this.add(this.unrendered_panels[i]);
35801             }
35802             this.unrendered_panels = null;
35803             
35804         }
35805         
35806     },
35807     
35808     applyConfig : function(c)
35809     {
35810         /*
35811          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35812             var dh = Roo.DomHelper;
35813             if(c.titlebar !== false){
35814                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35815                 this.collapseBtn.on("click", this.collapse, this);
35816                 this.collapseBtn.enableDisplayMode();
35817                 /*
35818                 if(c.showPin === true || this.showPin){
35819                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35820                     this.stickBtn.enableDisplayMode();
35821                     this.stickBtn.on("click", this.expand, this);
35822                     this.stickBtn.hide();
35823                 }
35824                 
35825             }
35826             */
35827             /** This region's collapsed element
35828             * @type Roo.Element */
35829             /*
35830              *
35831             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35832                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35833             ]}, true);
35834             
35835             if(c.floatable !== false){
35836                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35837                this.collapsedEl.on("click", this.collapseClick, this);
35838             }
35839
35840             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35841                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35842                    id: "message", unselectable: "on", style:{"float":"left"}});
35843                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35844              }
35845             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35846             this.expandBtn.on("click", this.expand, this);
35847             
35848         }
35849         
35850         if(this.collapseBtn){
35851             this.collapseBtn.setVisible(c.collapsible == true);
35852         }
35853         
35854         this.cmargins = c.cmargins || this.cmargins ||
35855                          (this.position == "west" || this.position == "east" ?
35856                              {top: 0, left: 2, right:2, bottom: 0} :
35857                              {top: 2, left: 0, right:0, bottom: 2});
35858         */
35859         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35860         
35861         
35862         this.bottomTabs = c.tabPosition != "top";
35863         
35864         this.autoScroll = c.autoScroll || false;
35865         
35866         
35867        
35868         
35869         this.duration = c.duration || .30;
35870         this.slideDuration = c.slideDuration || .45;
35871         this.config = c;
35872        
35873     },
35874     /**
35875      * Returns true if this region is currently visible.
35876      * @return {Boolean}
35877      */
35878     isVisible : function(){
35879         return this.visible;
35880     },
35881
35882     /**
35883      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35884      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35885      */
35886     //setCollapsedTitle : function(title){
35887     //    title = title || "&#160;";
35888      //   if(this.collapsedTitleTextEl){
35889       //      this.collapsedTitleTextEl.innerHTML = title;
35890        // }
35891     //},
35892
35893     getBox : function(){
35894         var b;
35895       //  if(!this.collapsed){
35896             b = this.el.getBox(false, true);
35897        // }else{
35898           //  b = this.collapsedEl.getBox(false, true);
35899         //}
35900         return b;
35901     },
35902
35903     getMargins : function(){
35904         return this.margins;
35905         //return this.collapsed ? this.cmargins : this.margins;
35906     },
35907 /*
35908     highlight : function(){
35909         this.el.addClass("x-layout-panel-dragover");
35910     },
35911
35912     unhighlight : function(){
35913         this.el.removeClass("x-layout-panel-dragover");
35914     },
35915 */
35916     updateBox : function(box)
35917     {
35918         if (!this.bodyEl) {
35919             return; // not rendered yet..
35920         }
35921         
35922         this.box = box;
35923         if(!this.collapsed){
35924             this.el.dom.style.left = box.x + "px";
35925             this.el.dom.style.top = box.y + "px";
35926             this.updateBody(box.width, box.height);
35927         }else{
35928             this.collapsedEl.dom.style.left = box.x + "px";
35929             this.collapsedEl.dom.style.top = box.y + "px";
35930             this.collapsedEl.setSize(box.width, box.height);
35931         }
35932         if(this.tabs){
35933             this.tabs.autoSizeTabs();
35934         }
35935     },
35936
35937     updateBody : function(w, h)
35938     {
35939         if(w !== null){
35940             this.el.setWidth(w);
35941             w -= this.el.getBorderWidth("rl");
35942             if(this.config.adjustments){
35943                 w += this.config.adjustments[0];
35944             }
35945         }
35946         if(h !== null && h > 0){
35947             this.el.setHeight(h);
35948             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35949             h -= this.el.getBorderWidth("tb");
35950             if(this.config.adjustments){
35951                 h += this.config.adjustments[1];
35952             }
35953             this.bodyEl.setHeight(h);
35954             if(this.tabs){
35955                 h = this.tabs.syncHeight(h);
35956             }
35957         }
35958         if(this.panelSize){
35959             w = w !== null ? w : this.panelSize.width;
35960             h = h !== null ? h : this.panelSize.height;
35961         }
35962         if(this.activePanel){
35963             var el = this.activePanel.getEl();
35964             w = w !== null ? w : el.getWidth();
35965             h = h !== null ? h : el.getHeight();
35966             this.panelSize = {width: w, height: h};
35967             this.activePanel.setSize(w, h);
35968         }
35969         if(Roo.isIE && this.tabs){
35970             this.tabs.el.repaint();
35971         }
35972     },
35973
35974     /**
35975      * Returns the container element for this region.
35976      * @return {Roo.Element}
35977      */
35978     getEl : function(){
35979         return this.el;
35980     },
35981
35982     /**
35983      * Hides this region.
35984      */
35985     hide : function(){
35986         //if(!this.collapsed){
35987             this.el.dom.style.left = "-2000px";
35988             this.el.hide();
35989         //}else{
35990          //   this.collapsedEl.dom.style.left = "-2000px";
35991          //   this.collapsedEl.hide();
35992        // }
35993         this.visible = false;
35994         this.fireEvent("visibilitychange", this, false);
35995     },
35996
35997     /**
35998      * Shows this region if it was previously hidden.
35999      */
36000     show : function(){
36001         //if(!this.collapsed){
36002             this.el.show();
36003         //}else{
36004         //    this.collapsedEl.show();
36005        // }
36006         this.visible = true;
36007         this.fireEvent("visibilitychange", this, true);
36008     },
36009 /*
36010     closeClicked : function(){
36011         if(this.activePanel){
36012             this.remove(this.activePanel);
36013         }
36014     },
36015
36016     collapseClick : function(e){
36017         if(this.isSlid){
36018            e.stopPropagation();
36019            this.slideIn();
36020         }else{
36021            e.stopPropagation();
36022            this.slideOut();
36023         }
36024     },
36025 */
36026     /**
36027      * Collapses this region.
36028      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36029      */
36030     /*
36031     collapse : function(skipAnim, skipCheck = false){
36032         if(this.collapsed) {
36033             return;
36034         }
36035         
36036         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36037             
36038             this.collapsed = true;
36039             if(this.split){
36040                 this.split.el.hide();
36041             }
36042             if(this.config.animate && skipAnim !== true){
36043                 this.fireEvent("invalidated", this);
36044                 this.animateCollapse();
36045             }else{
36046                 this.el.setLocation(-20000,-20000);
36047                 this.el.hide();
36048                 this.collapsedEl.show();
36049                 this.fireEvent("collapsed", this);
36050                 this.fireEvent("invalidated", this);
36051             }
36052         }
36053         
36054     },
36055 */
36056     animateCollapse : function(){
36057         // overridden
36058     },
36059
36060     /**
36061      * Expands this region if it was previously collapsed.
36062      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36063      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36064      */
36065     /*
36066     expand : function(e, skipAnim){
36067         if(e) {
36068             e.stopPropagation();
36069         }
36070         if(!this.collapsed || this.el.hasActiveFx()) {
36071             return;
36072         }
36073         if(this.isSlid){
36074             this.afterSlideIn();
36075             skipAnim = true;
36076         }
36077         this.collapsed = false;
36078         if(this.config.animate && skipAnim !== true){
36079             this.animateExpand();
36080         }else{
36081             this.el.show();
36082             if(this.split){
36083                 this.split.el.show();
36084             }
36085             this.collapsedEl.setLocation(-2000,-2000);
36086             this.collapsedEl.hide();
36087             this.fireEvent("invalidated", this);
36088             this.fireEvent("expanded", this);
36089         }
36090     },
36091 */
36092     animateExpand : function(){
36093         // overridden
36094     },
36095
36096     initTabs : function()
36097     {
36098         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36099         
36100         var ts = new Roo.bootstrap.panel.Tabs({
36101                 el: this.bodyEl.dom,
36102                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36103                 disableTooltips: this.config.disableTabTips,
36104                 toolbar : this.config.toolbar
36105             });
36106         
36107         if(this.config.hideTabs){
36108             ts.stripWrap.setDisplayed(false);
36109         }
36110         this.tabs = ts;
36111         ts.resizeTabs = this.config.resizeTabs === true;
36112         ts.minTabWidth = this.config.minTabWidth || 40;
36113         ts.maxTabWidth = this.config.maxTabWidth || 250;
36114         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36115         ts.monitorResize = false;
36116         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36117         ts.bodyEl.addClass('roo-layout-tabs-body');
36118         this.panels.each(this.initPanelAsTab, this);
36119     },
36120
36121     initPanelAsTab : function(panel){
36122         var ti = this.tabs.addTab(
36123             panel.getEl().id,
36124             panel.getTitle(),
36125             null,
36126             this.config.closeOnTab && panel.isClosable(),
36127             panel.tpl
36128         );
36129         if(panel.tabTip !== undefined){
36130             ti.setTooltip(panel.tabTip);
36131         }
36132         ti.on("activate", function(){
36133               this.setActivePanel(panel);
36134         }, this);
36135         
36136         if(this.config.closeOnTab){
36137             ti.on("beforeclose", function(t, e){
36138                 e.cancel = true;
36139                 this.remove(panel);
36140             }, this);
36141         }
36142         
36143         panel.tabItem = ti;
36144         
36145         return ti;
36146     },
36147
36148     updatePanelTitle : function(panel, title)
36149     {
36150         if(this.activePanel == panel){
36151             this.updateTitle(title);
36152         }
36153         if(this.tabs){
36154             var ti = this.tabs.getTab(panel.getEl().id);
36155             ti.setText(title);
36156             if(panel.tabTip !== undefined){
36157                 ti.setTooltip(panel.tabTip);
36158             }
36159         }
36160     },
36161
36162     updateTitle : function(title){
36163         if(this.titleTextEl && !this.config.title){
36164             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36165         }
36166     },
36167
36168     setActivePanel : function(panel)
36169     {
36170         panel = this.getPanel(panel);
36171         if(this.activePanel && this.activePanel != panel){
36172             if(this.activePanel.setActiveState(false) === false){
36173                 return;
36174             }
36175         }
36176         this.activePanel = panel;
36177         panel.setActiveState(true);
36178         if(this.panelSize){
36179             panel.setSize(this.panelSize.width, this.panelSize.height);
36180         }
36181         if(this.closeBtn){
36182             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36183         }
36184         this.updateTitle(panel.getTitle());
36185         if(this.tabs){
36186             this.fireEvent("invalidated", this);
36187         }
36188         this.fireEvent("panelactivated", this, panel);
36189     },
36190
36191     /**
36192      * Shows the specified panel.
36193      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36194      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36195      */
36196     showPanel : function(panel)
36197     {
36198         panel = this.getPanel(panel);
36199         if(panel){
36200             if(this.tabs){
36201                 var tab = this.tabs.getTab(panel.getEl().id);
36202                 if(tab.isHidden()){
36203                     this.tabs.unhideTab(tab.id);
36204                 }
36205                 tab.activate();
36206             }else{
36207                 this.setActivePanel(panel);
36208             }
36209         }
36210         return panel;
36211     },
36212
36213     /**
36214      * Get the active panel for this region.
36215      * @return {Roo.ContentPanel} The active panel or null
36216      */
36217     getActivePanel : function(){
36218         return this.activePanel;
36219     },
36220
36221     validateVisibility : function(){
36222         if(this.panels.getCount() < 1){
36223             this.updateTitle("&#160;");
36224             this.closeBtn.hide();
36225             this.hide();
36226         }else{
36227             if(!this.isVisible()){
36228                 this.show();
36229             }
36230         }
36231     },
36232
36233     /**
36234      * Adds the passed ContentPanel(s) to this region.
36235      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36236      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36237      */
36238     add : function(panel)
36239     {
36240         if(arguments.length > 1){
36241             for(var i = 0, len = arguments.length; i < len; i++) {
36242                 this.add(arguments[i]);
36243             }
36244             return null;
36245         }
36246         
36247         // if we have not been rendered yet, then we can not really do much of this..
36248         if (!this.bodyEl) {
36249             this.unrendered_panels.push(panel);
36250             return panel;
36251         }
36252         
36253         
36254         
36255         
36256         if(this.hasPanel(panel)){
36257             this.showPanel(panel);
36258             return panel;
36259         }
36260         panel.setRegion(this);
36261         this.panels.add(panel);
36262        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36263             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36264             // and hide them... ???
36265             this.bodyEl.dom.appendChild(panel.getEl().dom);
36266             if(panel.background !== true){
36267                 this.setActivePanel(panel);
36268             }
36269             this.fireEvent("paneladded", this, panel);
36270             return panel;
36271         }
36272         */
36273         if(!this.tabs){
36274             this.initTabs();
36275         }else{
36276             this.initPanelAsTab(panel);
36277         }
36278         
36279         
36280         if(panel.background !== true){
36281             this.tabs.activate(panel.getEl().id);
36282         }
36283         this.fireEvent("paneladded", this, panel);
36284         return panel;
36285     },
36286
36287     /**
36288      * Hides the tab for the specified panel.
36289      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36290      */
36291     hidePanel : function(panel){
36292         if(this.tabs && (panel = this.getPanel(panel))){
36293             this.tabs.hideTab(panel.getEl().id);
36294         }
36295     },
36296
36297     /**
36298      * Unhides the tab for a previously hidden panel.
36299      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36300      */
36301     unhidePanel : function(panel){
36302         if(this.tabs && (panel = this.getPanel(panel))){
36303             this.tabs.unhideTab(panel.getEl().id);
36304         }
36305     },
36306
36307     clearPanels : function(){
36308         while(this.panels.getCount() > 0){
36309              this.remove(this.panels.first());
36310         }
36311     },
36312
36313     /**
36314      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36315      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36316      * @param {Boolean} preservePanel Overrides the config preservePanel option
36317      * @return {Roo.ContentPanel} The panel that was removed
36318      */
36319     remove : function(panel, preservePanel)
36320     {
36321         panel = this.getPanel(panel);
36322         if(!panel){
36323             return null;
36324         }
36325         var e = {};
36326         this.fireEvent("beforeremove", this, panel, e);
36327         if(e.cancel === true){
36328             return null;
36329         }
36330         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36331         var panelId = panel.getId();
36332         this.panels.removeKey(panelId);
36333         if(preservePanel){
36334             document.body.appendChild(panel.getEl().dom);
36335         }
36336         if(this.tabs){
36337             this.tabs.removeTab(panel.getEl().id);
36338         }else if (!preservePanel){
36339             this.bodyEl.dom.removeChild(panel.getEl().dom);
36340         }
36341         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36342             var p = this.panels.first();
36343             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36344             tempEl.appendChild(p.getEl().dom);
36345             this.bodyEl.update("");
36346             this.bodyEl.dom.appendChild(p.getEl().dom);
36347             tempEl = null;
36348             this.updateTitle(p.getTitle());
36349             this.tabs = null;
36350             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36351             this.setActivePanel(p);
36352         }
36353         panel.setRegion(null);
36354         if(this.activePanel == panel){
36355             this.activePanel = null;
36356         }
36357         if(this.config.autoDestroy !== false && preservePanel !== true){
36358             try{panel.destroy();}catch(e){}
36359         }
36360         this.fireEvent("panelremoved", this, panel);
36361         return panel;
36362     },
36363
36364     /**
36365      * Returns the TabPanel component used by this region
36366      * @return {Roo.TabPanel}
36367      */
36368     getTabs : function(){
36369         return this.tabs;
36370     },
36371
36372     createTool : function(parentEl, className){
36373         var btn = Roo.DomHelper.append(parentEl, {
36374             tag: "div",
36375             cls: "x-layout-tools-button",
36376             children: [ {
36377                 tag: "div",
36378                 cls: "roo-layout-tools-button-inner " + className,
36379                 html: "&#160;"
36380             }]
36381         }, true);
36382         btn.addClassOnOver("roo-layout-tools-button-over");
36383         return btn;
36384     }
36385 });/*
36386  * Based on:
36387  * Ext JS Library 1.1.1
36388  * Copyright(c) 2006-2007, Ext JS, LLC.
36389  *
36390  * Originally Released Under LGPL - original licence link has changed is not relivant.
36391  *
36392  * Fork - LGPL
36393  * <script type="text/javascript">
36394  */
36395  
36396
36397
36398 /**
36399  * @class Roo.SplitLayoutRegion
36400  * @extends Roo.LayoutRegion
36401  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36402  */
36403 Roo.bootstrap.layout.Split = function(config){
36404     this.cursor = config.cursor;
36405     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36406 };
36407
36408 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36409 {
36410     splitTip : "Drag to resize.",
36411     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36412     useSplitTips : false,
36413
36414     applyConfig : function(config){
36415         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36416     },
36417     
36418     onRender : function(ctr,pos) {
36419         
36420         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36421         if(!this.config.split){
36422             return;
36423         }
36424         if(!this.split){
36425             
36426             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36427                             tag: "div",
36428                             id: this.el.id + "-split",
36429                             cls: "roo-layout-split roo-layout-split-"+this.position,
36430                             html: "&#160;"
36431             });
36432             /** The SplitBar for this region 
36433             * @type Roo.SplitBar */
36434             // does not exist yet...
36435             Roo.log([this.position, this.orientation]);
36436             
36437             this.split = new Roo.bootstrap.SplitBar({
36438                 dragElement : splitEl,
36439                 resizingElement: this.el,
36440                 orientation : this.orientation
36441             });
36442             
36443             this.split.on("moved", this.onSplitMove, this);
36444             this.split.useShim = this.config.useShim === true;
36445             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36446             if(this.useSplitTips){
36447                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36448             }
36449             //if(config.collapsible){
36450             //    this.split.el.on("dblclick", this.collapse,  this);
36451             //}
36452         }
36453         if(typeof this.config.minSize != "undefined"){
36454             this.split.minSize = this.config.minSize;
36455         }
36456         if(typeof this.config.maxSize != "undefined"){
36457             this.split.maxSize = this.config.maxSize;
36458         }
36459         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36460             this.hideSplitter();
36461         }
36462         
36463     },
36464
36465     getHMaxSize : function(){
36466          var cmax = this.config.maxSize || 10000;
36467          var center = this.mgr.getRegion("center");
36468          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36469     },
36470
36471     getVMaxSize : function(){
36472          var cmax = this.config.maxSize || 10000;
36473          var center = this.mgr.getRegion("center");
36474          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36475     },
36476
36477     onSplitMove : function(split, newSize){
36478         this.fireEvent("resized", this, newSize);
36479     },
36480     
36481     /** 
36482      * Returns the {@link Roo.SplitBar} for this region.
36483      * @return {Roo.SplitBar}
36484      */
36485     getSplitBar : function(){
36486         return this.split;
36487     },
36488     
36489     hide : function(){
36490         this.hideSplitter();
36491         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36492     },
36493
36494     hideSplitter : function(){
36495         if(this.split){
36496             this.split.el.setLocation(-2000,-2000);
36497             this.split.el.hide();
36498         }
36499     },
36500
36501     show : function(){
36502         if(this.split){
36503             this.split.el.show();
36504         }
36505         Roo.bootstrap.layout.Split.superclass.show.call(this);
36506     },
36507     
36508     beforeSlide: function(){
36509         if(Roo.isGecko){// firefox overflow auto bug workaround
36510             this.bodyEl.clip();
36511             if(this.tabs) {
36512                 this.tabs.bodyEl.clip();
36513             }
36514             if(this.activePanel){
36515                 this.activePanel.getEl().clip();
36516                 
36517                 if(this.activePanel.beforeSlide){
36518                     this.activePanel.beforeSlide();
36519                 }
36520             }
36521         }
36522     },
36523     
36524     afterSlide : function(){
36525         if(Roo.isGecko){// firefox overflow auto bug workaround
36526             this.bodyEl.unclip();
36527             if(this.tabs) {
36528                 this.tabs.bodyEl.unclip();
36529             }
36530             if(this.activePanel){
36531                 this.activePanel.getEl().unclip();
36532                 if(this.activePanel.afterSlide){
36533                     this.activePanel.afterSlide();
36534                 }
36535             }
36536         }
36537     },
36538
36539     initAutoHide : function(){
36540         if(this.autoHide !== false){
36541             if(!this.autoHideHd){
36542                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36543                 this.autoHideHd = {
36544                     "mouseout": function(e){
36545                         if(!e.within(this.el, true)){
36546                             st.delay(500);
36547                         }
36548                     },
36549                     "mouseover" : function(e){
36550                         st.cancel();
36551                     },
36552                     scope : this
36553                 };
36554             }
36555             this.el.on(this.autoHideHd);
36556         }
36557     },
36558
36559     clearAutoHide : function(){
36560         if(this.autoHide !== false){
36561             this.el.un("mouseout", this.autoHideHd.mouseout);
36562             this.el.un("mouseover", this.autoHideHd.mouseover);
36563         }
36564     },
36565
36566     clearMonitor : function(){
36567         Roo.get(document).un("click", this.slideInIf, this);
36568     },
36569
36570     // these names are backwards but not changed for compat
36571     slideOut : function(){
36572         if(this.isSlid || this.el.hasActiveFx()){
36573             return;
36574         }
36575         this.isSlid = true;
36576         if(this.collapseBtn){
36577             this.collapseBtn.hide();
36578         }
36579         this.closeBtnState = this.closeBtn.getStyle('display');
36580         this.closeBtn.hide();
36581         if(this.stickBtn){
36582             this.stickBtn.show();
36583         }
36584         this.el.show();
36585         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36586         this.beforeSlide();
36587         this.el.setStyle("z-index", 10001);
36588         this.el.slideIn(this.getSlideAnchor(), {
36589             callback: function(){
36590                 this.afterSlide();
36591                 this.initAutoHide();
36592                 Roo.get(document).on("click", this.slideInIf, this);
36593                 this.fireEvent("slideshow", this);
36594             },
36595             scope: this,
36596             block: true
36597         });
36598     },
36599
36600     afterSlideIn : function(){
36601         this.clearAutoHide();
36602         this.isSlid = false;
36603         this.clearMonitor();
36604         this.el.setStyle("z-index", "");
36605         if(this.collapseBtn){
36606             this.collapseBtn.show();
36607         }
36608         this.closeBtn.setStyle('display', this.closeBtnState);
36609         if(this.stickBtn){
36610             this.stickBtn.hide();
36611         }
36612         this.fireEvent("slidehide", this);
36613     },
36614
36615     slideIn : function(cb){
36616         if(!this.isSlid || this.el.hasActiveFx()){
36617             Roo.callback(cb);
36618             return;
36619         }
36620         this.isSlid = false;
36621         this.beforeSlide();
36622         this.el.slideOut(this.getSlideAnchor(), {
36623             callback: function(){
36624                 this.el.setLeftTop(-10000, -10000);
36625                 this.afterSlide();
36626                 this.afterSlideIn();
36627                 Roo.callback(cb);
36628             },
36629             scope: this,
36630             block: true
36631         });
36632     },
36633     
36634     slideInIf : function(e){
36635         if(!e.within(this.el)){
36636             this.slideIn();
36637         }
36638     },
36639
36640     animateCollapse : function(){
36641         this.beforeSlide();
36642         this.el.setStyle("z-index", 20000);
36643         var anchor = this.getSlideAnchor();
36644         this.el.slideOut(anchor, {
36645             callback : function(){
36646                 this.el.setStyle("z-index", "");
36647                 this.collapsedEl.slideIn(anchor, {duration:.3});
36648                 this.afterSlide();
36649                 this.el.setLocation(-10000,-10000);
36650                 this.el.hide();
36651                 this.fireEvent("collapsed", this);
36652             },
36653             scope: this,
36654             block: true
36655         });
36656     },
36657
36658     animateExpand : function(){
36659         this.beforeSlide();
36660         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36661         this.el.setStyle("z-index", 20000);
36662         this.collapsedEl.hide({
36663             duration:.1
36664         });
36665         this.el.slideIn(this.getSlideAnchor(), {
36666             callback : function(){
36667                 this.el.setStyle("z-index", "");
36668                 this.afterSlide();
36669                 if(this.split){
36670                     this.split.el.show();
36671                 }
36672                 this.fireEvent("invalidated", this);
36673                 this.fireEvent("expanded", this);
36674             },
36675             scope: this,
36676             block: true
36677         });
36678     },
36679
36680     anchors : {
36681         "west" : "left",
36682         "east" : "right",
36683         "north" : "top",
36684         "south" : "bottom"
36685     },
36686
36687     sanchors : {
36688         "west" : "l",
36689         "east" : "r",
36690         "north" : "t",
36691         "south" : "b"
36692     },
36693
36694     canchors : {
36695         "west" : "tl-tr",
36696         "east" : "tr-tl",
36697         "north" : "tl-bl",
36698         "south" : "bl-tl"
36699     },
36700
36701     getAnchor : function(){
36702         return this.anchors[this.position];
36703     },
36704
36705     getCollapseAnchor : function(){
36706         return this.canchors[this.position];
36707     },
36708
36709     getSlideAnchor : function(){
36710         return this.sanchors[this.position];
36711     },
36712
36713     getAlignAdj : function(){
36714         var cm = this.cmargins;
36715         switch(this.position){
36716             case "west":
36717                 return [0, 0];
36718             break;
36719             case "east":
36720                 return [0, 0];
36721             break;
36722             case "north":
36723                 return [0, 0];
36724             break;
36725             case "south":
36726                 return [0, 0];
36727             break;
36728         }
36729     },
36730
36731     getExpandAdj : function(){
36732         var c = this.collapsedEl, cm = this.cmargins;
36733         switch(this.position){
36734             case "west":
36735                 return [-(cm.right+c.getWidth()+cm.left), 0];
36736             break;
36737             case "east":
36738                 return [cm.right+c.getWidth()+cm.left, 0];
36739             break;
36740             case "north":
36741                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36742             break;
36743             case "south":
36744                 return [0, cm.top+cm.bottom+c.getHeight()];
36745             break;
36746         }
36747     }
36748 });/*
36749  * Based on:
36750  * Ext JS Library 1.1.1
36751  * Copyright(c) 2006-2007, Ext JS, LLC.
36752  *
36753  * Originally Released Under LGPL - original licence link has changed is not relivant.
36754  *
36755  * Fork - LGPL
36756  * <script type="text/javascript">
36757  */
36758 /*
36759  * These classes are private internal classes
36760  */
36761 Roo.bootstrap.layout.Center = function(config){
36762     config.region = "center";
36763     Roo.bootstrap.layout.Region.call(this, config);
36764     this.visible = true;
36765     this.minWidth = config.minWidth || 20;
36766     this.minHeight = config.minHeight || 20;
36767 };
36768
36769 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36770     hide : function(){
36771         // center panel can't be hidden
36772     },
36773     
36774     show : function(){
36775         // center panel can't be hidden
36776     },
36777     
36778     getMinWidth: function(){
36779         return this.minWidth;
36780     },
36781     
36782     getMinHeight: function(){
36783         return this.minHeight;
36784     }
36785 });
36786
36787
36788
36789
36790  
36791
36792
36793
36794
36795
36796 Roo.bootstrap.layout.North = function(config)
36797 {
36798     config.region = 'north';
36799     config.cursor = 'n-resize';
36800     
36801     Roo.bootstrap.layout.Split.call(this, config);
36802     
36803     
36804     if(this.split){
36805         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36806         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36807         this.split.el.addClass("roo-layout-split-v");
36808     }
36809     var size = config.initialSize || config.height;
36810     if(typeof size != "undefined"){
36811         this.el.setHeight(size);
36812     }
36813 };
36814 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36815 {
36816     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36817     
36818     
36819     
36820     getBox : function(){
36821         if(this.collapsed){
36822             return this.collapsedEl.getBox();
36823         }
36824         var box = this.el.getBox();
36825         if(this.split){
36826             box.height += this.split.el.getHeight();
36827         }
36828         return box;
36829     },
36830     
36831     updateBox : function(box){
36832         if(this.split && !this.collapsed){
36833             box.height -= this.split.el.getHeight();
36834             this.split.el.setLeft(box.x);
36835             this.split.el.setTop(box.y+box.height);
36836             this.split.el.setWidth(box.width);
36837         }
36838         if(this.collapsed){
36839             this.updateBody(box.width, null);
36840         }
36841         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36842     }
36843 });
36844
36845
36846
36847
36848
36849 Roo.bootstrap.layout.South = function(config){
36850     config.region = 'south';
36851     config.cursor = 's-resize';
36852     Roo.bootstrap.layout.Split.call(this, config);
36853     if(this.split){
36854         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36855         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36856         this.split.el.addClass("roo-layout-split-v");
36857     }
36858     var size = config.initialSize || config.height;
36859     if(typeof size != "undefined"){
36860         this.el.setHeight(size);
36861     }
36862 };
36863
36864 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36865     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36866     getBox : function(){
36867         if(this.collapsed){
36868             return this.collapsedEl.getBox();
36869         }
36870         var box = this.el.getBox();
36871         if(this.split){
36872             var sh = this.split.el.getHeight();
36873             box.height += sh;
36874             box.y -= sh;
36875         }
36876         return box;
36877     },
36878     
36879     updateBox : function(box){
36880         if(this.split && !this.collapsed){
36881             var sh = this.split.el.getHeight();
36882             box.height -= sh;
36883             box.y += sh;
36884             this.split.el.setLeft(box.x);
36885             this.split.el.setTop(box.y-sh);
36886             this.split.el.setWidth(box.width);
36887         }
36888         if(this.collapsed){
36889             this.updateBody(box.width, null);
36890         }
36891         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36892     }
36893 });
36894
36895 Roo.bootstrap.layout.East = function(config){
36896     config.region = "east";
36897     config.cursor = "e-resize";
36898     Roo.bootstrap.layout.Split.call(this, config);
36899     if(this.split){
36900         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36901         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36902         this.split.el.addClass("roo-layout-split-h");
36903     }
36904     var size = config.initialSize || config.width;
36905     if(typeof size != "undefined"){
36906         this.el.setWidth(size);
36907     }
36908 };
36909 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36910     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36911     getBox : function(){
36912         if(this.collapsed){
36913             return this.collapsedEl.getBox();
36914         }
36915         var box = this.el.getBox();
36916         if(this.split){
36917             var sw = this.split.el.getWidth();
36918             box.width += sw;
36919             box.x -= sw;
36920         }
36921         return box;
36922     },
36923
36924     updateBox : function(box){
36925         if(this.split && !this.collapsed){
36926             var sw = this.split.el.getWidth();
36927             box.width -= sw;
36928             this.split.el.setLeft(box.x);
36929             this.split.el.setTop(box.y);
36930             this.split.el.setHeight(box.height);
36931             box.x += sw;
36932         }
36933         if(this.collapsed){
36934             this.updateBody(null, box.height);
36935         }
36936         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36937     }
36938 });
36939
36940 Roo.bootstrap.layout.West = function(config){
36941     config.region = "west";
36942     config.cursor = "w-resize";
36943     
36944     Roo.bootstrap.layout.Split.call(this, config);
36945     if(this.split){
36946         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36947         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36948         this.split.el.addClass("roo-layout-split-h");
36949     }
36950     
36951 };
36952 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36953     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36954     
36955     onRender: function(ctr, pos)
36956     {
36957         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36958         var size = this.config.initialSize || this.config.width;
36959         if(typeof size != "undefined"){
36960             this.el.setWidth(size);
36961         }
36962     },
36963     
36964     getBox : function(){
36965         if(this.collapsed){
36966             return this.collapsedEl.getBox();
36967         }
36968         var box = this.el.getBox();
36969         if(this.split){
36970             box.width += this.split.el.getWidth();
36971         }
36972         return box;
36973     },
36974     
36975     updateBox : function(box){
36976         if(this.split && !this.collapsed){
36977             var sw = this.split.el.getWidth();
36978             box.width -= sw;
36979             this.split.el.setLeft(box.x+box.width);
36980             this.split.el.setTop(box.y);
36981             this.split.el.setHeight(box.height);
36982         }
36983         if(this.collapsed){
36984             this.updateBody(null, box.height);
36985         }
36986         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36987     }
36988 });
36989 Roo.namespace("Roo.bootstrap.panel");/*
36990  * Based on:
36991  * Ext JS Library 1.1.1
36992  * Copyright(c) 2006-2007, Ext JS, LLC.
36993  *
36994  * Originally Released Under LGPL - original licence link has changed is not relivant.
36995  *
36996  * Fork - LGPL
36997  * <script type="text/javascript">
36998  */
36999 /**
37000  * @class Roo.ContentPanel
37001  * @extends Roo.util.Observable
37002  * A basic ContentPanel element.
37003  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37004  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37005  * @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
37006  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37007  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37008  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37009  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37010  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37011  * @cfg {String} title          The title for this panel
37012  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37013  * @cfg {String} url            Calls {@link #setUrl} with this value
37014  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37015  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37016  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37017  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37018  * @cfg {Boolean} badges render the badges
37019
37020  * @constructor
37021  * Create a new ContentPanel.
37022  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37023  * @param {String/Object} config A string to set only the title or a config object
37024  * @param {String} content (optional) Set the HTML content for this panel
37025  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37026  */
37027 Roo.bootstrap.panel.Content = function( config){
37028     
37029     this.tpl = config.tpl || false;
37030     
37031     var el = config.el;
37032     var content = config.content;
37033
37034     if(config.autoCreate){ // xtype is available if this is called from factory
37035         el = Roo.id();
37036     }
37037     this.el = Roo.get(el);
37038     if(!this.el && config && config.autoCreate){
37039         if(typeof config.autoCreate == "object"){
37040             if(!config.autoCreate.id){
37041                 config.autoCreate.id = config.id||el;
37042             }
37043             this.el = Roo.DomHelper.append(document.body,
37044                         config.autoCreate, true);
37045         }else{
37046             var elcfg =  {   tag: "div",
37047                             cls: "roo-layout-inactive-content",
37048                             id: config.id||el
37049                             };
37050             if (config.html) {
37051                 elcfg.html = config.html;
37052                 
37053             }
37054                         
37055             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37056         }
37057     } 
37058     this.closable = false;
37059     this.loaded = false;
37060     this.active = false;
37061    
37062       
37063     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37064         
37065         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37066         
37067         this.wrapEl = this.el; //this.el.wrap();
37068         var ti = [];
37069         if (config.toolbar.items) {
37070             ti = config.toolbar.items ;
37071             delete config.toolbar.items ;
37072         }
37073         
37074         var nitems = [];
37075         this.toolbar.render(this.wrapEl, 'before');
37076         for(var i =0;i < ti.length;i++) {
37077           //  Roo.log(['add child', items[i]]);
37078             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37079         }
37080         this.toolbar.items = nitems;
37081         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37082         delete config.toolbar;
37083         
37084     }
37085     /*
37086     // xtype created footer. - not sure if will work as we normally have to render first..
37087     if (this.footer && !this.footer.el && this.footer.xtype) {
37088         if (!this.wrapEl) {
37089             this.wrapEl = this.el.wrap();
37090         }
37091     
37092         this.footer.container = this.wrapEl.createChild();
37093          
37094         this.footer = Roo.factory(this.footer, Roo);
37095         
37096     }
37097     */
37098     
37099      if(typeof config == "string"){
37100         this.title = config;
37101     }else{
37102         Roo.apply(this, config);
37103     }
37104     
37105     if(this.resizeEl){
37106         this.resizeEl = Roo.get(this.resizeEl, true);
37107     }else{
37108         this.resizeEl = this.el;
37109     }
37110     // handle view.xtype
37111     
37112  
37113     
37114     
37115     this.addEvents({
37116         /**
37117          * @event activate
37118          * Fires when this panel is activated. 
37119          * @param {Roo.ContentPanel} this
37120          */
37121         "activate" : true,
37122         /**
37123          * @event deactivate
37124          * Fires when this panel is activated. 
37125          * @param {Roo.ContentPanel} this
37126          */
37127         "deactivate" : true,
37128
37129         /**
37130          * @event resize
37131          * Fires when this panel is resized if fitToFrame is true.
37132          * @param {Roo.ContentPanel} this
37133          * @param {Number} width The width after any component adjustments
37134          * @param {Number} height The height after any component adjustments
37135          */
37136         "resize" : true,
37137         
37138          /**
37139          * @event render
37140          * Fires when this tab is created
37141          * @param {Roo.ContentPanel} this
37142          */
37143         "render" : true
37144         
37145         
37146         
37147     });
37148     
37149
37150     
37151     
37152     if(this.autoScroll){
37153         this.resizeEl.setStyle("overflow", "auto");
37154     } else {
37155         // fix randome scrolling
37156         //this.el.on('scroll', function() {
37157         //    Roo.log('fix random scolling');
37158         //    this.scrollTo('top',0); 
37159         //});
37160     }
37161     content = content || this.content;
37162     if(content){
37163         this.setContent(content);
37164     }
37165     if(config && config.url){
37166         this.setUrl(this.url, this.params, this.loadOnce);
37167     }
37168     
37169     
37170     
37171     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37172     
37173     if (this.view && typeof(this.view.xtype) != 'undefined') {
37174         this.view.el = this.el.appendChild(document.createElement("div"));
37175         this.view = Roo.factory(this.view); 
37176         this.view.render  &&  this.view.render(false, '');  
37177     }
37178     
37179     
37180     this.fireEvent('render', this);
37181 };
37182
37183 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37184     
37185     tabTip : '',
37186     
37187     setRegion : function(region){
37188         this.region = region;
37189         this.setActiveClass(region && !this.background);
37190     },
37191     
37192     
37193     setActiveClass: function(state)
37194     {
37195         if(state){
37196            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37197            this.el.setStyle('position','relative');
37198         }else{
37199            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37200            this.el.setStyle('position', 'absolute');
37201         } 
37202     },
37203     
37204     /**
37205      * Returns the toolbar for this Panel if one was configured. 
37206      * @return {Roo.Toolbar} 
37207      */
37208     getToolbar : function(){
37209         return this.toolbar;
37210     },
37211     
37212     setActiveState : function(active)
37213     {
37214         this.active = active;
37215         this.setActiveClass(active);
37216         if(!active){
37217             if(this.fireEvent("deactivate", this) === false){
37218                 return false;
37219             }
37220             return true;
37221         }
37222         this.fireEvent("activate", this);
37223         return true;
37224     },
37225     /**
37226      * Updates this panel's element
37227      * @param {String} content The new content
37228      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37229     */
37230     setContent : function(content, loadScripts){
37231         this.el.update(content, loadScripts);
37232     },
37233
37234     ignoreResize : function(w, h){
37235         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37236             return true;
37237         }else{
37238             this.lastSize = {width: w, height: h};
37239             return false;
37240         }
37241     },
37242     /**
37243      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37244      * @return {Roo.UpdateManager} The UpdateManager
37245      */
37246     getUpdateManager : function(){
37247         return this.el.getUpdateManager();
37248     },
37249      /**
37250      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37251      * @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:
37252 <pre><code>
37253 panel.load({
37254     url: "your-url.php",
37255     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37256     callback: yourFunction,
37257     scope: yourObject, //(optional scope)
37258     discardUrl: false,
37259     nocache: false,
37260     text: "Loading...",
37261     timeout: 30,
37262     scripts: false
37263 });
37264 </code></pre>
37265      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37266      * 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.
37267      * @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}
37268      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37269      * @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.
37270      * @return {Roo.ContentPanel} this
37271      */
37272     load : function(){
37273         var um = this.el.getUpdateManager();
37274         um.update.apply(um, arguments);
37275         return this;
37276     },
37277
37278
37279     /**
37280      * 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.
37281      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37282      * @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)
37283      * @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)
37284      * @return {Roo.UpdateManager} The UpdateManager
37285      */
37286     setUrl : function(url, params, loadOnce){
37287         if(this.refreshDelegate){
37288             this.removeListener("activate", this.refreshDelegate);
37289         }
37290         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37291         this.on("activate", this.refreshDelegate);
37292         return this.el.getUpdateManager();
37293     },
37294     
37295     _handleRefresh : function(url, params, loadOnce){
37296         if(!loadOnce || !this.loaded){
37297             var updater = this.el.getUpdateManager();
37298             updater.update(url, params, this._setLoaded.createDelegate(this));
37299         }
37300     },
37301     
37302     _setLoaded : function(){
37303         this.loaded = true;
37304     }, 
37305     
37306     /**
37307      * Returns this panel's id
37308      * @return {String} 
37309      */
37310     getId : function(){
37311         return this.el.id;
37312     },
37313     
37314     /** 
37315      * Returns this panel's element - used by regiosn to add.
37316      * @return {Roo.Element} 
37317      */
37318     getEl : function(){
37319         return this.wrapEl || this.el;
37320     },
37321     
37322    
37323     
37324     adjustForComponents : function(width, height)
37325     {
37326         //Roo.log('adjustForComponents ');
37327         if(this.resizeEl != this.el){
37328             width -= this.el.getFrameWidth('lr');
37329             height -= this.el.getFrameWidth('tb');
37330         }
37331         if(this.toolbar){
37332             var te = this.toolbar.getEl();
37333             te.setWidth(width);
37334             height -= te.getHeight();
37335         }
37336         if(this.footer){
37337             var te = this.footer.getEl();
37338             te.setWidth(width);
37339             height -= te.getHeight();
37340         }
37341         
37342         
37343         if(this.adjustments){
37344             width += this.adjustments[0];
37345             height += this.adjustments[1];
37346         }
37347         return {"width": width, "height": height};
37348     },
37349     
37350     setSize : function(width, height){
37351         if(this.fitToFrame && !this.ignoreResize(width, height)){
37352             if(this.fitContainer && this.resizeEl != this.el){
37353                 this.el.setSize(width, height);
37354             }
37355             var size = this.adjustForComponents(width, height);
37356             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37357             this.fireEvent('resize', this, size.width, size.height);
37358         }
37359     },
37360     
37361     /**
37362      * Returns this panel's title
37363      * @return {String} 
37364      */
37365     getTitle : function(){
37366         
37367         if (typeof(this.title) != 'object') {
37368             return this.title;
37369         }
37370         
37371         var t = '';
37372         for (var k in this.title) {
37373             if (!this.title.hasOwnProperty(k)) {
37374                 continue;
37375             }
37376             
37377             if (k.indexOf('-') >= 0) {
37378                 var s = k.split('-');
37379                 for (var i = 0; i<s.length; i++) {
37380                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37381                 }
37382             } else {
37383                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37384             }
37385         }
37386         return t;
37387     },
37388     
37389     /**
37390      * Set this panel's title
37391      * @param {String} title
37392      */
37393     setTitle : function(title){
37394         this.title = title;
37395         if(this.region){
37396             this.region.updatePanelTitle(this, title);
37397         }
37398     },
37399     
37400     /**
37401      * Returns true is this panel was configured to be closable
37402      * @return {Boolean} 
37403      */
37404     isClosable : function(){
37405         return this.closable;
37406     },
37407     
37408     beforeSlide : function(){
37409         this.el.clip();
37410         this.resizeEl.clip();
37411     },
37412     
37413     afterSlide : function(){
37414         this.el.unclip();
37415         this.resizeEl.unclip();
37416     },
37417     
37418     /**
37419      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37420      *   Will fail silently if the {@link #setUrl} method has not been called.
37421      *   This does not activate the panel, just updates its content.
37422      */
37423     refresh : function(){
37424         if(this.refreshDelegate){
37425            this.loaded = false;
37426            this.refreshDelegate();
37427         }
37428     },
37429     
37430     /**
37431      * Destroys this panel
37432      */
37433     destroy : function(){
37434         this.el.removeAllListeners();
37435         var tempEl = document.createElement("span");
37436         tempEl.appendChild(this.el.dom);
37437         tempEl.innerHTML = "";
37438         this.el.remove();
37439         this.el = null;
37440     },
37441     
37442     /**
37443      * form - if the content panel contains a form - this is a reference to it.
37444      * @type {Roo.form.Form}
37445      */
37446     form : false,
37447     /**
37448      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37449      *    This contains a reference to it.
37450      * @type {Roo.View}
37451      */
37452     view : false,
37453     
37454       /**
37455      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37456      * <pre><code>
37457
37458 layout.addxtype({
37459        xtype : 'Form',
37460        items: [ .... ]
37461    }
37462 );
37463
37464 </code></pre>
37465      * @param {Object} cfg Xtype definition of item to add.
37466      */
37467     
37468     
37469     getChildContainer: function () {
37470         return this.getEl();
37471     }
37472     
37473     
37474     /*
37475         var  ret = new Roo.factory(cfg);
37476         return ret;
37477         
37478         
37479         // add form..
37480         if (cfg.xtype.match(/^Form$/)) {
37481             
37482             var el;
37483             //if (this.footer) {
37484             //    el = this.footer.container.insertSibling(false, 'before');
37485             //} else {
37486                 el = this.el.createChild();
37487             //}
37488
37489             this.form = new  Roo.form.Form(cfg);
37490             
37491             
37492             if ( this.form.allItems.length) {
37493                 this.form.render(el.dom);
37494             }
37495             return this.form;
37496         }
37497         // should only have one of theses..
37498         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37499             // views.. should not be just added - used named prop 'view''
37500             
37501             cfg.el = this.el.appendChild(document.createElement("div"));
37502             // factory?
37503             
37504             var ret = new Roo.factory(cfg);
37505              
37506              ret.render && ret.render(false, ''); // render blank..
37507             this.view = ret;
37508             return ret;
37509         }
37510         return false;
37511     }
37512     \*/
37513 });
37514  
37515 /**
37516  * @class Roo.bootstrap.panel.Grid
37517  * @extends Roo.bootstrap.panel.Content
37518  * @constructor
37519  * Create a new GridPanel.
37520  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37521  * @param {Object} config A the config object
37522   
37523  */
37524
37525
37526
37527 Roo.bootstrap.panel.Grid = function(config)
37528 {
37529     
37530       
37531     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37532         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37533
37534     config.el = this.wrapper;
37535     //this.el = this.wrapper;
37536     
37537       if (config.container) {
37538         // ctor'ed from a Border/panel.grid
37539         
37540         
37541         this.wrapper.setStyle("overflow", "hidden");
37542         this.wrapper.addClass('roo-grid-container');
37543
37544     }
37545     
37546     
37547     if(config.toolbar){
37548         var tool_el = this.wrapper.createChild();    
37549         this.toolbar = Roo.factory(config.toolbar);
37550         var ti = [];
37551         if (config.toolbar.items) {
37552             ti = config.toolbar.items ;
37553             delete config.toolbar.items ;
37554         }
37555         
37556         var nitems = [];
37557         this.toolbar.render(tool_el);
37558         for(var i =0;i < ti.length;i++) {
37559           //  Roo.log(['add child', items[i]]);
37560             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37561         }
37562         this.toolbar.items = nitems;
37563         
37564         delete config.toolbar;
37565     }
37566     
37567     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37568     config.grid.scrollBody = true;;
37569     config.grid.monitorWindowResize = false; // turn off autosizing
37570     config.grid.autoHeight = false;
37571     config.grid.autoWidth = false;
37572     
37573     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37574     
37575     if (config.background) {
37576         // render grid on panel activation (if panel background)
37577         this.on('activate', function(gp) {
37578             if (!gp.grid.rendered) {
37579                 gp.grid.render(this.wrapper);
37580                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37581             }
37582         });
37583             
37584     } else {
37585         this.grid.render(this.wrapper);
37586         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37587
37588     }
37589     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37590     // ??? needed ??? config.el = this.wrapper;
37591     
37592     
37593     
37594   
37595     // xtype created footer. - not sure if will work as we normally have to render first..
37596     if (this.footer && !this.footer.el && this.footer.xtype) {
37597         
37598         var ctr = this.grid.getView().getFooterPanel(true);
37599         this.footer.dataSource = this.grid.dataSource;
37600         this.footer = Roo.factory(this.footer, Roo);
37601         this.footer.render(ctr);
37602         
37603     }
37604     
37605     
37606     
37607     
37608      
37609 };
37610
37611 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37612     getId : function(){
37613         return this.grid.id;
37614     },
37615     
37616     /**
37617      * Returns the grid for this panel
37618      * @return {Roo.bootstrap.Table} 
37619      */
37620     getGrid : function(){
37621         return this.grid;    
37622     },
37623     
37624     setSize : function(width, height){
37625         if(!this.ignoreResize(width, height)){
37626             var grid = this.grid;
37627             var size = this.adjustForComponents(width, height);
37628             var gridel = grid.getGridEl();
37629             gridel.setSize(size.width, size.height);
37630             /*
37631             var thd = grid.getGridEl().select('thead',true).first();
37632             var tbd = grid.getGridEl().select('tbody', true).first();
37633             if (tbd) {
37634                 tbd.setSize(width, height - thd.getHeight());
37635             }
37636             */
37637             grid.autoSize();
37638         }
37639     },
37640      
37641     
37642     
37643     beforeSlide : function(){
37644         this.grid.getView().scroller.clip();
37645     },
37646     
37647     afterSlide : function(){
37648         this.grid.getView().scroller.unclip();
37649     },
37650     
37651     destroy : function(){
37652         this.grid.destroy();
37653         delete this.grid;
37654         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37655     }
37656 });
37657
37658 /**
37659  * @class Roo.bootstrap.panel.Nest
37660  * @extends Roo.bootstrap.panel.Content
37661  * @constructor
37662  * Create a new Panel, that can contain a layout.Border.
37663  * 
37664  * 
37665  * @param {Roo.BorderLayout} layout The layout for this panel
37666  * @param {String/Object} config A string to set only the title or a config object
37667  */
37668 Roo.bootstrap.panel.Nest = function(config)
37669 {
37670     // construct with only one argument..
37671     /* FIXME - implement nicer consturctors
37672     if (layout.layout) {
37673         config = layout;
37674         layout = config.layout;
37675         delete config.layout;
37676     }
37677     if (layout.xtype && !layout.getEl) {
37678         // then layout needs constructing..
37679         layout = Roo.factory(layout, Roo);
37680     }
37681     */
37682     
37683     config.el =  config.layout.getEl();
37684     
37685     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37686     
37687     config.layout.monitorWindowResize = false; // turn off autosizing
37688     this.layout = config.layout;
37689     this.layout.getEl().addClass("roo-layout-nested-layout");
37690     
37691     
37692     
37693     
37694 };
37695
37696 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37697
37698     setSize : function(width, height){
37699         if(!this.ignoreResize(width, height)){
37700             var size = this.adjustForComponents(width, height);
37701             var el = this.layout.getEl();
37702             if (size.height < 1) {
37703                 el.setWidth(size.width);   
37704             } else {
37705                 el.setSize(size.width, size.height);
37706             }
37707             var touch = el.dom.offsetWidth;
37708             this.layout.layout();
37709             // ie requires a double layout on the first pass
37710             if(Roo.isIE && !this.initialized){
37711                 this.initialized = true;
37712                 this.layout.layout();
37713             }
37714         }
37715     },
37716     
37717     // activate all subpanels if not currently active..
37718     
37719     setActiveState : function(active){
37720         this.active = active;
37721         this.setActiveClass(active);
37722         
37723         if(!active){
37724             this.fireEvent("deactivate", this);
37725             return;
37726         }
37727         
37728         this.fireEvent("activate", this);
37729         // not sure if this should happen before or after..
37730         if (!this.layout) {
37731             return; // should not happen..
37732         }
37733         var reg = false;
37734         for (var r in this.layout.regions) {
37735             reg = this.layout.getRegion(r);
37736             if (reg.getActivePanel()) {
37737                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37738                 reg.setActivePanel(reg.getActivePanel());
37739                 continue;
37740             }
37741             if (!reg.panels.length) {
37742                 continue;
37743             }
37744             reg.showPanel(reg.getPanel(0));
37745         }
37746         
37747         
37748         
37749         
37750     },
37751     
37752     /**
37753      * Returns the nested BorderLayout for this panel
37754      * @return {Roo.BorderLayout} 
37755      */
37756     getLayout : function(){
37757         return this.layout;
37758     },
37759     
37760      /**
37761      * Adds a xtype elements to the layout of the nested panel
37762      * <pre><code>
37763
37764 panel.addxtype({
37765        xtype : 'ContentPanel',
37766        region: 'west',
37767        items: [ .... ]
37768    }
37769 );
37770
37771 panel.addxtype({
37772         xtype : 'NestedLayoutPanel',
37773         region: 'west',
37774         layout: {
37775            center: { },
37776            west: { }   
37777         },
37778         items : [ ... list of content panels or nested layout panels.. ]
37779    }
37780 );
37781 </code></pre>
37782      * @param {Object} cfg Xtype definition of item to add.
37783      */
37784     addxtype : function(cfg) {
37785         return this.layout.addxtype(cfg);
37786     
37787     }
37788 });        /*
37789  * Based on:
37790  * Ext JS Library 1.1.1
37791  * Copyright(c) 2006-2007, Ext JS, LLC.
37792  *
37793  * Originally Released Under LGPL - original licence link has changed is not relivant.
37794  *
37795  * Fork - LGPL
37796  * <script type="text/javascript">
37797  */
37798 /**
37799  * @class Roo.TabPanel
37800  * @extends Roo.util.Observable
37801  * A lightweight tab container.
37802  * <br><br>
37803  * Usage:
37804  * <pre><code>
37805 // basic tabs 1, built from existing content
37806 var tabs = new Roo.TabPanel("tabs1");
37807 tabs.addTab("script", "View Script");
37808 tabs.addTab("markup", "View Markup");
37809 tabs.activate("script");
37810
37811 // more advanced tabs, built from javascript
37812 var jtabs = new Roo.TabPanel("jtabs");
37813 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37814
37815 // set up the UpdateManager
37816 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37817 var updater = tab2.getUpdateManager();
37818 updater.setDefaultUrl("ajax1.htm");
37819 tab2.on('activate', updater.refresh, updater, true);
37820
37821 // Use setUrl for Ajax loading
37822 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37823 tab3.setUrl("ajax2.htm", null, true);
37824
37825 // Disabled tab
37826 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37827 tab4.disable();
37828
37829 jtabs.activate("jtabs-1");
37830  * </code></pre>
37831  * @constructor
37832  * Create a new TabPanel.
37833  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37834  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37835  */
37836 Roo.bootstrap.panel.Tabs = function(config){
37837     /**
37838     * The container element for this TabPanel.
37839     * @type Roo.Element
37840     */
37841     this.el = Roo.get(config.el);
37842     delete config.el;
37843     if(config){
37844         if(typeof config == "boolean"){
37845             this.tabPosition = config ? "bottom" : "top";
37846         }else{
37847             Roo.apply(this, config);
37848         }
37849     }
37850     
37851     if(this.tabPosition == "bottom"){
37852         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37853         this.el.addClass("roo-tabs-bottom");
37854     }
37855     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37856     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37857     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37858     if(Roo.isIE){
37859         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37860     }
37861     if(this.tabPosition != "bottom"){
37862         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37863          * @type Roo.Element
37864          */
37865         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37866         this.el.addClass("roo-tabs-top");
37867     }
37868     this.items = [];
37869
37870     this.bodyEl.setStyle("position", "relative");
37871
37872     this.active = null;
37873     this.activateDelegate = this.activate.createDelegate(this);
37874
37875     this.addEvents({
37876         /**
37877          * @event tabchange
37878          * Fires when the active tab changes
37879          * @param {Roo.TabPanel} this
37880          * @param {Roo.TabPanelItem} activePanel The new active tab
37881          */
37882         "tabchange": true,
37883         /**
37884          * @event beforetabchange
37885          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37886          * @param {Roo.TabPanel} this
37887          * @param {Object} e Set cancel to true on this object to cancel the tab change
37888          * @param {Roo.TabPanelItem} tab The tab being changed to
37889          */
37890         "beforetabchange" : true
37891     });
37892
37893     Roo.EventManager.onWindowResize(this.onResize, this);
37894     this.cpad = this.el.getPadding("lr");
37895     this.hiddenCount = 0;
37896
37897
37898     // toolbar on the tabbar support...
37899     if (this.toolbar) {
37900         alert("no toolbar support yet");
37901         this.toolbar  = false;
37902         /*
37903         var tcfg = this.toolbar;
37904         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37905         this.toolbar = new Roo.Toolbar(tcfg);
37906         if (Roo.isSafari) {
37907             var tbl = tcfg.container.child('table', true);
37908             tbl.setAttribute('width', '100%');
37909         }
37910         */
37911         
37912     }
37913    
37914
37915
37916     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37917 };
37918
37919 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37920     /*
37921      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37922      */
37923     tabPosition : "top",
37924     /*
37925      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37926      */
37927     currentTabWidth : 0,
37928     /*
37929      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37930      */
37931     minTabWidth : 40,
37932     /*
37933      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37934      */
37935     maxTabWidth : 250,
37936     /*
37937      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37938      */
37939     preferredTabWidth : 175,
37940     /*
37941      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37942      */
37943     resizeTabs : false,
37944     /*
37945      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37946      */
37947     monitorResize : true,
37948     /*
37949      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37950      */
37951     toolbar : false,
37952
37953     /**
37954      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37955      * @param {String} id The id of the div to use <b>or create</b>
37956      * @param {String} text The text for the tab
37957      * @param {String} content (optional) Content to put in the TabPanelItem body
37958      * @param {Boolean} closable (optional) True to create a close icon on the tab
37959      * @return {Roo.TabPanelItem} The created TabPanelItem
37960      */
37961     addTab : function(id, text, content, closable, tpl)
37962     {
37963         var item = new Roo.bootstrap.panel.TabItem({
37964             panel: this,
37965             id : id,
37966             text : text,
37967             closable : closable,
37968             tpl : tpl
37969         });
37970         this.addTabItem(item);
37971         if(content){
37972             item.setContent(content);
37973         }
37974         return item;
37975     },
37976
37977     /**
37978      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37979      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37980      * @return {Roo.TabPanelItem}
37981      */
37982     getTab : function(id){
37983         return this.items[id];
37984     },
37985
37986     /**
37987      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37988      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37989      */
37990     hideTab : function(id){
37991         var t = this.items[id];
37992         if(!t.isHidden()){
37993            t.setHidden(true);
37994            this.hiddenCount++;
37995            this.autoSizeTabs();
37996         }
37997     },
37998
37999     /**
38000      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38001      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38002      */
38003     unhideTab : function(id){
38004         var t = this.items[id];
38005         if(t.isHidden()){
38006            t.setHidden(false);
38007            this.hiddenCount--;
38008            this.autoSizeTabs();
38009         }
38010     },
38011
38012     /**
38013      * Adds an existing {@link Roo.TabPanelItem}.
38014      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38015      */
38016     addTabItem : function(item){
38017         this.items[item.id] = item;
38018         this.items.push(item);
38019       //  if(this.resizeTabs){
38020     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38021   //         this.autoSizeTabs();
38022 //        }else{
38023 //            item.autoSize();
38024        // }
38025     },
38026
38027     /**
38028      * Removes a {@link Roo.TabPanelItem}.
38029      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38030      */
38031     removeTab : function(id){
38032         var items = this.items;
38033         var tab = items[id];
38034         if(!tab) { return; }
38035         var index = items.indexOf(tab);
38036         if(this.active == tab && items.length > 1){
38037             var newTab = this.getNextAvailable(index);
38038             if(newTab) {
38039                 newTab.activate();
38040             }
38041         }
38042         this.stripEl.dom.removeChild(tab.pnode.dom);
38043         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38044             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38045         }
38046         items.splice(index, 1);
38047         delete this.items[tab.id];
38048         tab.fireEvent("close", tab);
38049         tab.purgeListeners();
38050         this.autoSizeTabs();
38051     },
38052
38053     getNextAvailable : function(start){
38054         var items = this.items;
38055         var index = start;
38056         // look for a next tab that will slide over to
38057         // replace the one being removed
38058         while(index < items.length){
38059             var item = items[++index];
38060             if(item && !item.isHidden()){
38061                 return item;
38062             }
38063         }
38064         // if one isn't found select the previous tab (on the left)
38065         index = start;
38066         while(index >= 0){
38067             var item = items[--index];
38068             if(item && !item.isHidden()){
38069                 return item;
38070             }
38071         }
38072         return null;
38073     },
38074
38075     /**
38076      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38077      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38078      */
38079     disableTab : function(id){
38080         var tab = this.items[id];
38081         if(tab && this.active != tab){
38082             tab.disable();
38083         }
38084     },
38085
38086     /**
38087      * Enables a {@link Roo.TabPanelItem} that is disabled.
38088      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38089      */
38090     enableTab : function(id){
38091         var tab = this.items[id];
38092         tab.enable();
38093     },
38094
38095     /**
38096      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38097      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38098      * @return {Roo.TabPanelItem} The TabPanelItem.
38099      */
38100     activate : function(id){
38101         var tab = this.items[id];
38102         if(!tab){
38103             return null;
38104         }
38105         if(tab == this.active || tab.disabled){
38106             return tab;
38107         }
38108         var e = {};
38109         this.fireEvent("beforetabchange", this, e, tab);
38110         if(e.cancel !== true && !tab.disabled){
38111             if(this.active){
38112                 this.active.hide();
38113             }
38114             this.active = this.items[id];
38115             this.active.show();
38116             this.fireEvent("tabchange", this, this.active);
38117         }
38118         return tab;
38119     },
38120
38121     /**
38122      * Gets the active {@link Roo.TabPanelItem}.
38123      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38124      */
38125     getActiveTab : function(){
38126         return this.active;
38127     },
38128
38129     /**
38130      * Updates the tab body element to fit the height of the container element
38131      * for overflow scrolling
38132      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38133      */
38134     syncHeight : function(targetHeight){
38135         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38136         var bm = this.bodyEl.getMargins();
38137         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38138         this.bodyEl.setHeight(newHeight);
38139         return newHeight;
38140     },
38141
38142     onResize : function(){
38143         if(this.monitorResize){
38144             this.autoSizeTabs();
38145         }
38146     },
38147
38148     /**
38149      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38150      */
38151     beginUpdate : function(){
38152         this.updating = true;
38153     },
38154
38155     /**
38156      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38157      */
38158     endUpdate : function(){
38159         this.updating = false;
38160         this.autoSizeTabs();
38161     },
38162
38163     /**
38164      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38165      */
38166     autoSizeTabs : function(){
38167         var count = this.items.length;
38168         var vcount = count - this.hiddenCount;
38169         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38170             return;
38171         }
38172         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38173         var availWidth = Math.floor(w / vcount);
38174         var b = this.stripBody;
38175         if(b.getWidth() > w){
38176             var tabs = this.items;
38177             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38178             if(availWidth < this.minTabWidth){
38179                 /*if(!this.sleft){    // incomplete scrolling code
38180                     this.createScrollButtons();
38181                 }
38182                 this.showScroll();
38183                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38184             }
38185         }else{
38186             if(this.currentTabWidth < this.preferredTabWidth){
38187                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38188             }
38189         }
38190     },
38191
38192     /**
38193      * Returns the number of tabs in this TabPanel.
38194      * @return {Number}
38195      */
38196      getCount : function(){
38197          return this.items.length;
38198      },
38199
38200     /**
38201      * Resizes all the tabs to the passed width
38202      * @param {Number} The new width
38203      */
38204     setTabWidth : function(width){
38205         this.currentTabWidth = width;
38206         for(var i = 0, len = this.items.length; i < len; i++) {
38207                 if(!this.items[i].isHidden()) {
38208                 this.items[i].setWidth(width);
38209             }
38210         }
38211     },
38212
38213     /**
38214      * Destroys this TabPanel
38215      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38216      */
38217     destroy : function(removeEl){
38218         Roo.EventManager.removeResizeListener(this.onResize, this);
38219         for(var i = 0, len = this.items.length; i < len; i++){
38220             this.items[i].purgeListeners();
38221         }
38222         if(removeEl === true){
38223             this.el.update("");
38224             this.el.remove();
38225         }
38226     },
38227     
38228     createStrip : function(container)
38229     {
38230         var strip = document.createElement("nav");
38231         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38232         container.appendChild(strip);
38233         return strip;
38234     },
38235     
38236     createStripList : function(strip)
38237     {
38238         // div wrapper for retard IE
38239         // returns the "tr" element.
38240         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38241         //'<div class="x-tabs-strip-wrap">'+
38242           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38243           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38244         return strip.firstChild; //.firstChild.firstChild.firstChild;
38245     },
38246     createBody : function(container)
38247     {
38248         var body = document.createElement("div");
38249         Roo.id(body, "tab-body");
38250         //Roo.fly(body).addClass("x-tabs-body");
38251         Roo.fly(body).addClass("tab-content");
38252         container.appendChild(body);
38253         return body;
38254     },
38255     createItemBody :function(bodyEl, id){
38256         var body = Roo.getDom(id);
38257         if(!body){
38258             body = document.createElement("div");
38259             body.id = id;
38260         }
38261         //Roo.fly(body).addClass("x-tabs-item-body");
38262         Roo.fly(body).addClass("tab-pane");
38263          bodyEl.insertBefore(body, bodyEl.firstChild);
38264         return body;
38265     },
38266     /** @private */
38267     createStripElements :  function(stripEl, text, closable, tpl)
38268     {
38269         var td = document.createElement("li"); // was td..
38270         
38271         
38272         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38273         
38274         
38275         stripEl.appendChild(td);
38276         /*if(closable){
38277             td.className = "x-tabs-closable";
38278             if(!this.closeTpl){
38279                 this.closeTpl = new Roo.Template(
38280                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38281                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38282                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38283                 );
38284             }
38285             var el = this.closeTpl.overwrite(td, {"text": text});
38286             var close = el.getElementsByTagName("div")[0];
38287             var inner = el.getElementsByTagName("em")[0];
38288             return {"el": el, "close": close, "inner": inner};
38289         } else {
38290         */
38291         // not sure what this is..
38292 //            if(!this.tabTpl){
38293                 //this.tabTpl = new Roo.Template(
38294                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38295                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38296                 //);
38297 //                this.tabTpl = new Roo.Template(
38298 //                   '<a href="#">' +
38299 //                   '<span unselectable="on"' +
38300 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38301 //                            ' >{text}</span></a>'
38302 //                );
38303 //                
38304 //            }
38305
38306
38307             var template = tpl || this.tabTpl || false;
38308             
38309             if(!template){
38310                 
38311                 template = new Roo.Template(
38312                    '<a href="#">' +
38313                    '<span unselectable="on"' +
38314                             (this.disableTooltips ? '' : ' title="{text}"') +
38315                             ' >{text}</span></a>'
38316                 );
38317             }
38318             
38319             switch (typeof(template)) {
38320                 case 'object' :
38321                     break;
38322                 case 'string' :
38323                     template = new Roo.Template(template);
38324                     break;
38325                 default :
38326                     break;
38327             }
38328             
38329             var el = template.overwrite(td, {"text": text});
38330             
38331             var inner = el.getElementsByTagName("span")[0];
38332             
38333             return {"el": el, "inner": inner};
38334             
38335     }
38336         
38337     
38338 });
38339
38340 /**
38341  * @class Roo.TabPanelItem
38342  * @extends Roo.util.Observable
38343  * Represents an individual item (tab plus body) in a TabPanel.
38344  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38345  * @param {String} id The id of this TabPanelItem
38346  * @param {String} text The text for the tab of this TabPanelItem
38347  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38348  */
38349 Roo.bootstrap.panel.TabItem = function(config){
38350     /**
38351      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38352      * @type Roo.TabPanel
38353      */
38354     this.tabPanel = config.panel;
38355     /**
38356      * The id for this TabPanelItem
38357      * @type String
38358      */
38359     this.id = config.id;
38360     /** @private */
38361     this.disabled = false;
38362     /** @private */
38363     this.text = config.text;
38364     /** @private */
38365     this.loaded = false;
38366     this.closable = config.closable;
38367
38368     /**
38369      * The body element for this TabPanelItem.
38370      * @type Roo.Element
38371      */
38372     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38373     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38374     this.bodyEl.setStyle("display", "block");
38375     this.bodyEl.setStyle("zoom", "1");
38376     //this.hideAction();
38377
38378     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38379     /** @private */
38380     this.el = Roo.get(els.el);
38381     this.inner = Roo.get(els.inner, true);
38382     this.textEl = Roo.get(this.el.dom.firstChild, true);
38383     this.pnode = Roo.get(els.el.parentNode, true);
38384 //    this.el.on("mousedown", this.onTabMouseDown, this);
38385     this.el.on("click", this.onTabClick, this);
38386     /** @private */
38387     if(config.closable){
38388         var c = Roo.get(els.close, true);
38389         c.dom.title = this.closeText;
38390         c.addClassOnOver("close-over");
38391         c.on("click", this.closeClick, this);
38392      }
38393
38394     this.addEvents({
38395          /**
38396          * @event activate
38397          * Fires when this tab becomes the active tab.
38398          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38399          * @param {Roo.TabPanelItem} this
38400          */
38401         "activate": true,
38402         /**
38403          * @event beforeclose
38404          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38405          * @param {Roo.TabPanelItem} this
38406          * @param {Object} e Set cancel to true on this object to cancel the close.
38407          */
38408         "beforeclose": true,
38409         /**
38410          * @event close
38411          * Fires when this tab is closed.
38412          * @param {Roo.TabPanelItem} this
38413          */
38414          "close": true,
38415         /**
38416          * @event deactivate
38417          * Fires when this tab is no longer the active tab.
38418          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38419          * @param {Roo.TabPanelItem} this
38420          */
38421          "deactivate" : true
38422     });
38423     this.hidden = false;
38424
38425     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38426 };
38427
38428 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38429            {
38430     purgeListeners : function(){
38431        Roo.util.Observable.prototype.purgeListeners.call(this);
38432        this.el.removeAllListeners();
38433     },
38434     /**
38435      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38436      */
38437     show : function(){
38438         this.pnode.addClass("active");
38439         this.showAction();
38440         if(Roo.isOpera){
38441             this.tabPanel.stripWrap.repaint();
38442         }
38443         this.fireEvent("activate", this.tabPanel, this);
38444     },
38445
38446     /**
38447      * Returns true if this tab is the active tab.
38448      * @return {Boolean}
38449      */
38450     isActive : function(){
38451         return this.tabPanel.getActiveTab() == this;
38452     },
38453
38454     /**
38455      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38456      */
38457     hide : function(){
38458         this.pnode.removeClass("active");
38459         this.hideAction();
38460         this.fireEvent("deactivate", this.tabPanel, this);
38461     },
38462
38463     hideAction : function(){
38464         this.bodyEl.hide();
38465         this.bodyEl.setStyle("position", "absolute");
38466         this.bodyEl.setLeft("-20000px");
38467         this.bodyEl.setTop("-20000px");
38468     },
38469
38470     showAction : function(){
38471         this.bodyEl.setStyle("position", "relative");
38472         this.bodyEl.setTop("");
38473         this.bodyEl.setLeft("");
38474         this.bodyEl.show();
38475     },
38476
38477     /**
38478      * Set the tooltip for the tab.
38479      * @param {String} tooltip The tab's tooltip
38480      */
38481     setTooltip : function(text){
38482         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38483             this.textEl.dom.qtip = text;
38484             this.textEl.dom.removeAttribute('title');
38485         }else{
38486             this.textEl.dom.title = text;
38487         }
38488     },
38489
38490     onTabClick : function(e){
38491         e.preventDefault();
38492         this.tabPanel.activate(this.id);
38493     },
38494
38495     onTabMouseDown : function(e){
38496         e.preventDefault();
38497         this.tabPanel.activate(this.id);
38498     },
38499 /*
38500     getWidth : function(){
38501         return this.inner.getWidth();
38502     },
38503
38504     setWidth : function(width){
38505         var iwidth = width - this.pnode.getPadding("lr");
38506         this.inner.setWidth(iwidth);
38507         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38508         this.pnode.setWidth(width);
38509     },
38510 */
38511     /**
38512      * Show or hide the tab
38513      * @param {Boolean} hidden True to hide or false to show.
38514      */
38515     setHidden : function(hidden){
38516         this.hidden = hidden;
38517         this.pnode.setStyle("display", hidden ? "none" : "");
38518     },
38519
38520     /**
38521      * Returns true if this tab is "hidden"
38522      * @return {Boolean}
38523      */
38524     isHidden : function(){
38525         return this.hidden;
38526     },
38527
38528     /**
38529      * Returns the text for this tab
38530      * @return {String}
38531      */
38532     getText : function(){
38533         return this.text;
38534     },
38535     /*
38536     autoSize : function(){
38537         //this.el.beginMeasure();
38538         this.textEl.setWidth(1);
38539         /*
38540          *  #2804 [new] Tabs in Roojs
38541          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38542          */
38543         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38544         //this.el.endMeasure();
38545     //},
38546
38547     /**
38548      * Sets the text for the tab (Note: this also sets the tooltip text)
38549      * @param {String} text The tab's text and tooltip
38550      */
38551     setText : function(text){
38552         this.text = text;
38553         this.textEl.update(text);
38554         this.setTooltip(text);
38555         //if(!this.tabPanel.resizeTabs){
38556         //    this.autoSize();
38557         //}
38558     },
38559     /**
38560      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38561      */
38562     activate : function(){
38563         this.tabPanel.activate(this.id);
38564     },
38565
38566     /**
38567      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38568      */
38569     disable : function(){
38570         if(this.tabPanel.active != this){
38571             this.disabled = true;
38572             this.pnode.addClass("disabled");
38573         }
38574     },
38575
38576     /**
38577      * Enables this TabPanelItem if it was previously disabled.
38578      */
38579     enable : function(){
38580         this.disabled = false;
38581         this.pnode.removeClass("disabled");
38582     },
38583
38584     /**
38585      * Sets the content for this TabPanelItem.
38586      * @param {String} content The content
38587      * @param {Boolean} loadScripts true to look for and load scripts
38588      */
38589     setContent : function(content, loadScripts){
38590         this.bodyEl.update(content, loadScripts);
38591     },
38592
38593     /**
38594      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38595      * @return {Roo.UpdateManager} The UpdateManager
38596      */
38597     getUpdateManager : function(){
38598         return this.bodyEl.getUpdateManager();
38599     },
38600
38601     /**
38602      * Set a URL to be used to load the content for this TabPanelItem.
38603      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38604      * @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)
38605      * @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)
38606      * @return {Roo.UpdateManager} The UpdateManager
38607      */
38608     setUrl : function(url, params, loadOnce){
38609         if(this.refreshDelegate){
38610             this.un('activate', this.refreshDelegate);
38611         }
38612         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38613         this.on("activate", this.refreshDelegate);
38614         return this.bodyEl.getUpdateManager();
38615     },
38616
38617     /** @private */
38618     _handleRefresh : function(url, params, loadOnce){
38619         if(!loadOnce || !this.loaded){
38620             var updater = this.bodyEl.getUpdateManager();
38621             updater.update(url, params, this._setLoaded.createDelegate(this));
38622         }
38623     },
38624
38625     /**
38626      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38627      *   Will fail silently if the setUrl method has not been called.
38628      *   This does not activate the panel, just updates its content.
38629      */
38630     refresh : function(){
38631         if(this.refreshDelegate){
38632            this.loaded = false;
38633            this.refreshDelegate();
38634         }
38635     },
38636
38637     /** @private */
38638     _setLoaded : function(){
38639         this.loaded = true;
38640     },
38641
38642     /** @private */
38643     closeClick : function(e){
38644         var o = {};
38645         e.stopEvent();
38646         this.fireEvent("beforeclose", this, o);
38647         if(o.cancel !== true){
38648             this.tabPanel.removeTab(this.id);
38649         }
38650     },
38651     /**
38652      * The text displayed in the tooltip for the close icon.
38653      * @type String
38654      */
38655     closeText : "Close this tab"
38656 });
38657 /**
38658 *    This script refer to:
38659 *    Title: International Telephone Input
38660 *    Author: Jack O'Connor
38661 *    Code version:  v12.1.12
38662 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38663 **/
38664
38665 Roo.bootstrap.PhoneInputData = function() {
38666     var d = [
38667       [
38668         "Afghanistan (‫افغانستان‬‎)",
38669         "af",
38670         "93"
38671       ],
38672       [
38673         "Albania (Shqipëri)",
38674         "al",
38675         "355"
38676       ],
38677       [
38678         "Algeria (‫الجزائر‬‎)",
38679         "dz",
38680         "213"
38681       ],
38682       [
38683         "American Samoa",
38684         "as",
38685         "1684"
38686       ],
38687       [
38688         "Andorra",
38689         "ad",
38690         "376"
38691       ],
38692       [
38693         "Angola",
38694         "ao",
38695         "244"
38696       ],
38697       [
38698         "Anguilla",
38699         "ai",
38700         "1264"
38701       ],
38702       [
38703         "Antigua and Barbuda",
38704         "ag",
38705         "1268"
38706       ],
38707       [
38708         "Argentina",
38709         "ar",
38710         "54"
38711       ],
38712       [
38713         "Armenia (Հայաստան)",
38714         "am",
38715         "374"
38716       ],
38717       [
38718         "Aruba",
38719         "aw",
38720         "297"
38721       ],
38722       [
38723         "Australia",
38724         "au",
38725         "61",
38726         0
38727       ],
38728       [
38729         "Austria (Österreich)",
38730         "at",
38731         "43"
38732       ],
38733       [
38734         "Azerbaijan (Azərbaycan)",
38735         "az",
38736         "994"
38737       ],
38738       [
38739         "Bahamas",
38740         "bs",
38741         "1242"
38742       ],
38743       [
38744         "Bahrain (‫البحرين‬‎)",
38745         "bh",
38746         "973"
38747       ],
38748       [
38749         "Bangladesh (বাংলাদেশ)",
38750         "bd",
38751         "880"
38752       ],
38753       [
38754         "Barbados",
38755         "bb",
38756         "1246"
38757       ],
38758       [
38759         "Belarus (Беларусь)",
38760         "by",
38761         "375"
38762       ],
38763       [
38764         "Belgium (België)",
38765         "be",
38766         "32"
38767       ],
38768       [
38769         "Belize",
38770         "bz",
38771         "501"
38772       ],
38773       [
38774         "Benin (Bénin)",
38775         "bj",
38776         "229"
38777       ],
38778       [
38779         "Bermuda",
38780         "bm",
38781         "1441"
38782       ],
38783       [
38784         "Bhutan (འབྲུག)",
38785         "bt",
38786         "975"
38787       ],
38788       [
38789         "Bolivia",
38790         "bo",
38791         "591"
38792       ],
38793       [
38794         "Bosnia and Herzegovina (Босна и Херцеговина)",
38795         "ba",
38796         "387"
38797       ],
38798       [
38799         "Botswana",
38800         "bw",
38801         "267"
38802       ],
38803       [
38804         "Brazil (Brasil)",
38805         "br",
38806         "55"
38807       ],
38808       [
38809         "British Indian Ocean Territory",
38810         "io",
38811         "246"
38812       ],
38813       [
38814         "British Virgin Islands",
38815         "vg",
38816         "1284"
38817       ],
38818       [
38819         "Brunei",
38820         "bn",
38821         "673"
38822       ],
38823       [
38824         "Bulgaria (България)",
38825         "bg",
38826         "359"
38827       ],
38828       [
38829         "Burkina Faso",
38830         "bf",
38831         "226"
38832       ],
38833       [
38834         "Burundi (Uburundi)",
38835         "bi",
38836         "257"
38837       ],
38838       [
38839         "Cambodia (កម្ពុជា)",
38840         "kh",
38841         "855"
38842       ],
38843       [
38844         "Cameroon (Cameroun)",
38845         "cm",
38846         "237"
38847       ],
38848       [
38849         "Canada",
38850         "ca",
38851         "1",
38852         1,
38853         ["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"]
38854       ],
38855       [
38856         "Cape Verde (Kabu Verdi)",
38857         "cv",
38858         "238"
38859       ],
38860       [
38861         "Caribbean Netherlands",
38862         "bq",
38863         "599",
38864         1
38865       ],
38866       [
38867         "Cayman Islands",
38868         "ky",
38869         "1345"
38870       ],
38871       [
38872         "Central African Republic (République centrafricaine)",
38873         "cf",
38874         "236"
38875       ],
38876       [
38877         "Chad (Tchad)",
38878         "td",
38879         "235"
38880       ],
38881       [
38882         "Chile",
38883         "cl",
38884         "56"
38885       ],
38886       [
38887         "China (中国)",
38888         "cn",
38889         "86"
38890       ],
38891       [
38892         "Christmas Island",
38893         "cx",
38894         "61",
38895         2
38896       ],
38897       [
38898         "Cocos (Keeling) Islands",
38899         "cc",
38900         "61",
38901         1
38902       ],
38903       [
38904         "Colombia",
38905         "co",
38906         "57"
38907       ],
38908       [
38909         "Comoros (‫جزر القمر‬‎)",
38910         "km",
38911         "269"
38912       ],
38913       [
38914         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38915         "cd",
38916         "243"
38917       ],
38918       [
38919         "Congo (Republic) (Congo-Brazzaville)",
38920         "cg",
38921         "242"
38922       ],
38923       [
38924         "Cook Islands",
38925         "ck",
38926         "682"
38927       ],
38928       [
38929         "Costa Rica",
38930         "cr",
38931         "506"
38932       ],
38933       [
38934         "Côte d’Ivoire",
38935         "ci",
38936         "225"
38937       ],
38938       [
38939         "Croatia (Hrvatska)",
38940         "hr",
38941         "385"
38942       ],
38943       [
38944         "Cuba",
38945         "cu",
38946         "53"
38947       ],
38948       [
38949         "Curaçao",
38950         "cw",
38951         "599",
38952         0
38953       ],
38954       [
38955         "Cyprus (Κύπρος)",
38956         "cy",
38957         "357"
38958       ],
38959       [
38960         "Czech Republic (Česká republika)",
38961         "cz",
38962         "420"
38963       ],
38964       [
38965         "Denmark (Danmark)",
38966         "dk",
38967         "45"
38968       ],
38969       [
38970         "Djibouti",
38971         "dj",
38972         "253"
38973       ],
38974       [
38975         "Dominica",
38976         "dm",
38977         "1767"
38978       ],
38979       [
38980         "Dominican Republic (República Dominicana)",
38981         "do",
38982         "1",
38983         2,
38984         ["809", "829", "849"]
38985       ],
38986       [
38987         "Ecuador",
38988         "ec",
38989         "593"
38990       ],
38991       [
38992         "Egypt (‫مصر‬‎)",
38993         "eg",
38994         "20"
38995       ],
38996       [
38997         "El Salvador",
38998         "sv",
38999         "503"
39000       ],
39001       [
39002         "Equatorial Guinea (Guinea Ecuatorial)",
39003         "gq",
39004         "240"
39005       ],
39006       [
39007         "Eritrea",
39008         "er",
39009         "291"
39010       ],
39011       [
39012         "Estonia (Eesti)",
39013         "ee",
39014         "372"
39015       ],
39016       [
39017         "Ethiopia",
39018         "et",
39019         "251"
39020       ],
39021       [
39022         "Falkland Islands (Islas Malvinas)",
39023         "fk",
39024         "500"
39025       ],
39026       [
39027         "Faroe Islands (Føroyar)",
39028         "fo",
39029         "298"
39030       ],
39031       [
39032         "Fiji",
39033         "fj",
39034         "679"
39035       ],
39036       [
39037         "Finland (Suomi)",
39038         "fi",
39039         "358",
39040         0
39041       ],
39042       [
39043         "France",
39044         "fr",
39045         "33"
39046       ],
39047       [
39048         "French Guiana (Guyane française)",
39049         "gf",
39050         "594"
39051       ],
39052       [
39053         "French Polynesia (Polynésie française)",
39054         "pf",
39055         "689"
39056       ],
39057       [
39058         "Gabon",
39059         "ga",
39060         "241"
39061       ],
39062       [
39063         "Gambia",
39064         "gm",
39065         "220"
39066       ],
39067       [
39068         "Georgia (საქართველო)",
39069         "ge",
39070         "995"
39071       ],
39072       [
39073         "Germany (Deutschland)",
39074         "de",
39075         "49"
39076       ],
39077       [
39078         "Ghana (Gaana)",
39079         "gh",
39080         "233"
39081       ],
39082       [
39083         "Gibraltar",
39084         "gi",
39085         "350"
39086       ],
39087       [
39088         "Greece (Ελλάδα)",
39089         "gr",
39090         "30"
39091       ],
39092       [
39093         "Greenland (Kalaallit Nunaat)",
39094         "gl",
39095         "299"
39096       ],
39097       [
39098         "Grenada",
39099         "gd",
39100         "1473"
39101       ],
39102       [
39103         "Guadeloupe",
39104         "gp",
39105         "590",
39106         0
39107       ],
39108       [
39109         "Guam",
39110         "gu",
39111         "1671"
39112       ],
39113       [
39114         "Guatemala",
39115         "gt",
39116         "502"
39117       ],
39118       [
39119         "Guernsey",
39120         "gg",
39121         "44",
39122         1
39123       ],
39124       [
39125         "Guinea (Guinée)",
39126         "gn",
39127         "224"
39128       ],
39129       [
39130         "Guinea-Bissau (Guiné Bissau)",
39131         "gw",
39132         "245"
39133       ],
39134       [
39135         "Guyana",
39136         "gy",
39137         "592"
39138       ],
39139       [
39140         "Haiti",
39141         "ht",
39142         "509"
39143       ],
39144       [
39145         "Honduras",
39146         "hn",
39147         "504"
39148       ],
39149       [
39150         "Hong Kong (香港)",
39151         "hk",
39152         "852"
39153       ],
39154       [
39155         "Hungary (Magyarország)",
39156         "hu",
39157         "36"
39158       ],
39159       [
39160         "Iceland (Ísland)",
39161         "is",
39162         "354"
39163       ],
39164       [
39165         "India (भारत)",
39166         "in",
39167         "91"
39168       ],
39169       [
39170         "Indonesia",
39171         "id",
39172         "62"
39173       ],
39174       [
39175         "Iran (‫ایران‬‎)",
39176         "ir",
39177         "98"
39178       ],
39179       [
39180         "Iraq (‫العراق‬‎)",
39181         "iq",
39182         "964"
39183       ],
39184       [
39185         "Ireland",
39186         "ie",
39187         "353"
39188       ],
39189       [
39190         "Isle of Man",
39191         "im",
39192         "44",
39193         2
39194       ],
39195       [
39196         "Israel (‫ישראל‬‎)",
39197         "il",
39198         "972"
39199       ],
39200       [
39201         "Italy (Italia)",
39202         "it",
39203         "39",
39204         0
39205       ],
39206       [
39207         "Jamaica",
39208         "jm",
39209         "1876"
39210       ],
39211       [
39212         "Japan (日本)",
39213         "jp",
39214         "81"
39215       ],
39216       [
39217         "Jersey",
39218         "je",
39219         "44",
39220         3
39221       ],
39222       [
39223         "Jordan (‫الأردن‬‎)",
39224         "jo",
39225         "962"
39226       ],
39227       [
39228         "Kazakhstan (Казахстан)",
39229         "kz",
39230         "7",
39231         1
39232       ],
39233       [
39234         "Kenya",
39235         "ke",
39236         "254"
39237       ],
39238       [
39239         "Kiribati",
39240         "ki",
39241         "686"
39242       ],
39243       [
39244         "Kosovo",
39245         "xk",
39246         "383"
39247       ],
39248       [
39249         "Kuwait (‫الكويت‬‎)",
39250         "kw",
39251         "965"
39252       ],
39253       [
39254         "Kyrgyzstan (Кыргызстан)",
39255         "kg",
39256         "996"
39257       ],
39258       [
39259         "Laos (ລາວ)",
39260         "la",
39261         "856"
39262       ],
39263       [
39264         "Latvia (Latvija)",
39265         "lv",
39266         "371"
39267       ],
39268       [
39269         "Lebanon (‫لبنان‬‎)",
39270         "lb",
39271         "961"
39272       ],
39273       [
39274         "Lesotho",
39275         "ls",
39276         "266"
39277       ],
39278       [
39279         "Liberia",
39280         "lr",
39281         "231"
39282       ],
39283       [
39284         "Libya (‫ليبيا‬‎)",
39285         "ly",
39286         "218"
39287       ],
39288       [
39289         "Liechtenstein",
39290         "li",
39291         "423"
39292       ],
39293       [
39294         "Lithuania (Lietuva)",
39295         "lt",
39296         "370"
39297       ],
39298       [
39299         "Luxembourg",
39300         "lu",
39301         "352"
39302       ],
39303       [
39304         "Macau (澳門)",
39305         "mo",
39306         "853"
39307       ],
39308       [
39309         "Macedonia (FYROM) (Македонија)",
39310         "mk",
39311         "389"
39312       ],
39313       [
39314         "Madagascar (Madagasikara)",
39315         "mg",
39316         "261"
39317       ],
39318       [
39319         "Malawi",
39320         "mw",
39321         "265"
39322       ],
39323       [
39324         "Malaysia",
39325         "my",
39326         "60"
39327       ],
39328       [
39329         "Maldives",
39330         "mv",
39331         "960"
39332       ],
39333       [
39334         "Mali",
39335         "ml",
39336         "223"
39337       ],
39338       [
39339         "Malta",
39340         "mt",
39341         "356"
39342       ],
39343       [
39344         "Marshall Islands",
39345         "mh",
39346         "692"
39347       ],
39348       [
39349         "Martinique",
39350         "mq",
39351         "596"
39352       ],
39353       [
39354         "Mauritania (‫موريتانيا‬‎)",
39355         "mr",
39356         "222"
39357       ],
39358       [
39359         "Mauritius (Moris)",
39360         "mu",
39361         "230"
39362       ],
39363       [
39364         "Mayotte",
39365         "yt",
39366         "262",
39367         1
39368       ],
39369       [
39370         "Mexico (México)",
39371         "mx",
39372         "52"
39373       ],
39374       [
39375         "Micronesia",
39376         "fm",
39377         "691"
39378       ],
39379       [
39380         "Moldova (Republica Moldova)",
39381         "md",
39382         "373"
39383       ],
39384       [
39385         "Monaco",
39386         "mc",
39387         "377"
39388       ],
39389       [
39390         "Mongolia (Монгол)",
39391         "mn",
39392         "976"
39393       ],
39394       [
39395         "Montenegro (Crna Gora)",
39396         "me",
39397         "382"
39398       ],
39399       [
39400         "Montserrat",
39401         "ms",
39402         "1664"
39403       ],
39404       [
39405         "Morocco (‫المغرب‬‎)",
39406         "ma",
39407         "212",
39408         0
39409       ],
39410       [
39411         "Mozambique (Moçambique)",
39412         "mz",
39413         "258"
39414       ],
39415       [
39416         "Myanmar (Burma) (မြန်မာ)",
39417         "mm",
39418         "95"
39419       ],
39420       [
39421         "Namibia (Namibië)",
39422         "na",
39423         "264"
39424       ],
39425       [
39426         "Nauru",
39427         "nr",
39428         "674"
39429       ],
39430       [
39431         "Nepal (नेपाल)",
39432         "np",
39433         "977"
39434       ],
39435       [
39436         "Netherlands (Nederland)",
39437         "nl",
39438         "31"
39439       ],
39440       [
39441         "New Caledonia (Nouvelle-Calédonie)",
39442         "nc",
39443         "687"
39444       ],
39445       [
39446         "New Zealand",
39447         "nz",
39448         "64"
39449       ],
39450       [
39451         "Nicaragua",
39452         "ni",
39453         "505"
39454       ],
39455       [
39456         "Niger (Nijar)",
39457         "ne",
39458         "227"
39459       ],
39460       [
39461         "Nigeria",
39462         "ng",
39463         "234"
39464       ],
39465       [
39466         "Niue",
39467         "nu",
39468         "683"
39469       ],
39470       [
39471         "Norfolk Island",
39472         "nf",
39473         "672"
39474       ],
39475       [
39476         "North Korea (조선 민주주의 인민 공화국)",
39477         "kp",
39478         "850"
39479       ],
39480       [
39481         "Northern Mariana Islands",
39482         "mp",
39483         "1670"
39484       ],
39485       [
39486         "Norway (Norge)",
39487         "no",
39488         "47",
39489         0
39490       ],
39491       [
39492         "Oman (‫عُمان‬‎)",
39493         "om",
39494         "968"
39495       ],
39496       [
39497         "Pakistan (‫پاکستان‬‎)",
39498         "pk",
39499         "92"
39500       ],
39501       [
39502         "Palau",
39503         "pw",
39504         "680"
39505       ],
39506       [
39507         "Palestine (‫فلسطين‬‎)",
39508         "ps",
39509         "970"
39510       ],
39511       [
39512         "Panama (Panamá)",
39513         "pa",
39514         "507"
39515       ],
39516       [
39517         "Papua New Guinea",
39518         "pg",
39519         "675"
39520       ],
39521       [
39522         "Paraguay",
39523         "py",
39524         "595"
39525       ],
39526       [
39527         "Peru (Perú)",
39528         "pe",
39529         "51"
39530       ],
39531       [
39532         "Philippines",
39533         "ph",
39534         "63"
39535       ],
39536       [
39537         "Poland (Polska)",
39538         "pl",
39539         "48"
39540       ],
39541       [
39542         "Portugal",
39543         "pt",
39544         "351"
39545       ],
39546       [
39547         "Puerto Rico",
39548         "pr",
39549         "1",
39550         3,
39551         ["787", "939"]
39552       ],
39553       [
39554         "Qatar (‫قطر‬‎)",
39555         "qa",
39556         "974"
39557       ],
39558       [
39559         "Réunion (La Réunion)",
39560         "re",
39561         "262",
39562         0
39563       ],
39564       [
39565         "Romania (România)",
39566         "ro",
39567         "40"
39568       ],
39569       [
39570         "Russia (Россия)",
39571         "ru",
39572         "7",
39573         0
39574       ],
39575       [
39576         "Rwanda",
39577         "rw",
39578         "250"
39579       ],
39580       [
39581         "Saint Barthélemy",
39582         "bl",
39583         "590",
39584         1
39585       ],
39586       [
39587         "Saint Helena",
39588         "sh",
39589         "290"
39590       ],
39591       [
39592         "Saint Kitts and Nevis",
39593         "kn",
39594         "1869"
39595       ],
39596       [
39597         "Saint Lucia",
39598         "lc",
39599         "1758"
39600       ],
39601       [
39602         "Saint Martin (Saint-Martin (partie française))",
39603         "mf",
39604         "590",
39605         2
39606       ],
39607       [
39608         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39609         "pm",
39610         "508"
39611       ],
39612       [
39613         "Saint Vincent and the Grenadines",
39614         "vc",
39615         "1784"
39616       ],
39617       [
39618         "Samoa",
39619         "ws",
39620         "685"
39621       ],
39622       [
39623         "San Marino",
39624         "sm",
39625         "378"
39626       ],
39627       [
39628         "São Tomé and Príncipe (São Tomé e Príncipe)",
39629         "st",
39630         "239"
39631       ],
39632       [
39633         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39634         "sa",
39635         "966"
39636       ],
39637       [
39638         "Senegal (Sénégal)",
39639         "sn",
39640         "221"
39641       ],
39642       [
39643         "Serbia (Србија)",
39644         "rs",
39645         "381"
39646       ],
39647       [
39648         "Seychelles",
39649         "sc",
39650         "248"
39651       ],
39652       [
39653         "Sierra Leone",
39654         "sl",
39655         "232"
39656       ],
39657       [
39658         "Singapore",
39659         "sg",
39660         "65"
39661       ],
39662       [
39663         "Sint Maarten",
39664         "sx",
39665         "1721"
39666       ],
39667       [
39668         "Slovakia (Slovensko)",
39669         "sk",
39670         "421"
39671       ],
39672       [
39673         "Slovenia (Slovenija)",
39674         "si",
39675         "386"
39676       ],
39677       [
39678         "Solomon Islands",
39679         "sb",
39680         "677"
39681       ],
39682       [
39683         "Somalia (Soomaaliya)",
39684         "so",
39685         "252"
39686       ],
39687       [
39688         "South Africa",
39689         "za",
39690         "27"
39691       ],
39692       [
39693         "South Korea (대한민국)",
39694         "kr",
39695         "82"
39696       ],
39697       [
39698         "South Sudan (‫جنوب السودان‬‎)",
39699         "ss",
39700         "211"
39701       ],
39702       [
39703         "Spain (España)",
39704         "es",
39705         "34"
39706       ],
39707       [
39708         "Sri Lanka (ශ්‍රී ලංකාව)",
39709         "lk",
39710         "94"
39711       ],
39712       [
39713         "Sudan (‫السودان‬‎)",
39714         "sd",
39715         "249"
39716       ],
39717       [
39718         "Suriname",
39719         "sr",
39720         "597"
39721       ],
39722       [
39723         "Svalbard and Jan Mayen",
39724         "sj",
39725         "47",
39726         1
39727       ],
39728       [
39729         "Swaziland",
39730         "sz",
39731         "268"
39732       ],
39733       [
39734         "Sweden (Sverige)",
39735         "se",
39736         "46"
39737       ],
39738       [
39739         "Switzerland (Schweiz)",
39740         "ch",
39741         "41"
39742       ],
39743       [
39744         "Syria (‫سوريا‬‎)",
39745         "sy",
39746         "963"
39747       ],
39748       [
39749         "Taiwan (台灣)",
39750         "tw",
39751         "886"
39752       ],
39753       [
39754         "Tajikistan",
39755         "tj",
39756         "992"
39757       ],
39758       [
39759         "Tanzania",
39760         "tz",
39761         "255"
39762       ],
39763       [
39764         "Thailand (ไทย)",
39765         "th",
39766         "66"
39767       ],
39768       [
39769         "Timor-Leste",
39770         "tl",
39771         "670"
39772       ],
39773       [
39774         "Togo",
39775         "tg",
39776         "228"
39777       ],
39778       [
39779         "Tokelau",
39780         "tk",
39781         "690"
39782       ],
39783       [
39784         "Tonga",
39785         "to",
39786         "676"
39787       ],
39788       [
39789         "Trinidad and Tobago",
39790         "tt",
39791         "1868"
39792       ],
39793       [
39794         "Tunisia (‫تونس‬‎)",
39795         "tn",
39796         "216"
39797       ],
39798       [
39799         "Turkey (Türkiye)",
39800         "tr",
39801         "90"
39802       ],
39803       [
39804         "Turkmenistan",
39805         "tm",
39806         "993"
39807       ],
39808       [
39809         "Turks and Caicos Islands",
39810         "tc",
39811         "1649"
39812       ],
39813       [
39814         "Tuvalu",
39815         "tv",
39816         "688"
39817       ],
39818       [
39819         "U.S. Virgin Islands",
39820         "vi",
39821         "1340"
39822       ],
39823       [
39824         "Uganda",
39825         "ug",
39826         "256"
39827       ],
39828       [
39829         "Ukraine (Україна)",
39830         "ua",
39831         "380"
39832       ],
39833       [
39834         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39835         "ae",
39836         "971"
39837       ],
39838       [
39839         "United Kingdom",
39840         "gb",
39841         "44",
39842         0
39843       ],
39844       [
39845         "United States",
39846         "us",
39847         "1",
39848         0
39849       ],
39850       [
39851         "Uruguay",
39852         "uy",
39853         "598"
39854       ],
39855       [
39856         "Uzbekistan (Oʻzbekiston)",
39857         "uz",
39858         "998"
39859       ],
39860       [
39861         "Vanuatu",
39862         "vu",
39863         "678"
39864       ],
39865       [
39866         "Vatican City (Città del Vaticano)",
39867         "va",
39868         "39",
39869         1
39870       ],
39871       [
39872         "Venezuela",
39873         "ve",
39874         "58"
39875       ],
39876       [
39877         "Vietnam (Việt Nam)",
39878         "vn",
39879         "84"
39880       ],
39881       [
39882         "Wallis and Futuna (Wallis-et-Futuna)",
39883         "wf",
39884         "681"
39885       ],
39886       [
39887         "Western Sahara (‫الصحراء الغربية‬‎)",
39888         "eh",
39889         "212",
39890         1
39891       ],
39892       [
39893         "Yemen (‫اليمن‬‎)",
39894         "ye",
39895         "967"
39896       ],
39897       [
39898         "Zambia",
39899         "zm",
39900         "260"
39901       ],
39902       [
39903         "Zimbabwe",
39904         "zw",
39905         "263"
39906       ],
39907       [
39908         "Åland Islands",
39909         "ax",
39910         "358",
39911         1
39912       ]
39913   ];
39914   
39915   return d;
39916 }/**
39917 *    This script refer to:
39918 *    Title: International Telephone Input
39919 *    Author: Jack O'Connor
39920 *    Code version:  v12.1.12
39921 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39922 **/
39923
39924 /**
39925  * @class Roo.bootstrap.PhoneInput
39926  * @extends Roo.bootstrap.TriggerField
39927  * An input with International dial-code selection
39928  
39929  * @cfg {String} defaultDialCode default '+852'
39930  * @cfg {Array} preferedCountries default []
39931   
39932  * @constructor
39933  * Create a new PhoneInput.
39934  * @param {Object} config Configuration options
39935  */
39936
39937 Roo.bootstrap.PhoneInput = function(config) {
39938     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39939 };
39940
39941 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39942         
39943         listWidth: undefined,
39944         
39945         selectedClass: 'active',
39946         
39947         invalidClass : "has-warning",
39948         
39949         validClass: 'has-success',
39950         
39951         allowed: '0123456789',
39952         
39953         max_length: 15,
39954         
39955         /**
39956          * @cfg {String} defaultDialCode The default dial code when initializing the input
39957          */
39958         defaultDialCode: '+852',
39959         
39960         /**
39961          * @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
39962          */
39963         preferedCountries: false,
39964         
39965         getAutoCreate : function()
39966         {
39967             var data = Roo.bootstrap.PhoneInputData();
39968             var align = this.labelAlign || this.parentLabelAlign();
39969             var id = Roo.id();
39970             
39971             this.allCountries = [];
39972             this.dialCodeMapping = [];
39973             
39974             for (var i = 0; i < data.length; i++) {
39975               var c = data[i];
39976               this.allCountries[i] = {
39977                 name: c[0],
39978                 iso2: c[1],
39979                 dialCode: c[2],
39980                 priority: c[3] || 0,
39981                 areaCodes: c[4] || null
39982               };
39983               this.dialCodeMapping[c[2]] = {
39984                   name: c[0],
39985                   iso2: c[1],
39986                   priority: c[3] || 0,
39987                   areaCodes: c[4] || null
39988               };
39989             }
39990             
39991             var cfg = {
39992                 cls: 'form-group',
39993                 cn: []
39994             };
39995             
39996             var input =  {
39997                 tag: 'input',
39998                 id : id,
39999                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40000                 maxlength: this.max_length,
40001                 cls : 'form-control tel-input',
40002                 autocomplete: 'new-password'
40003             };
40004             
40005             var hiddenInput = {
40006                 tag: 'input',
40007                 type: 'hidden',
40008                 cls: 'hidden-tel-input'
40009             };
40010             
40011             if (this.name) {
40012                 hiddenInput.name = this.name;
40013             }
40014             
40015             if (this.disabled) {
40016                 input.disabled = true;
40017             }
40018             
40019             var flag_container = {
40020                 tag: 'div',
40021                 cls: 'flag-box',
40022                 cn: [
40023                     {
40024                         tag: 'div',
40025                         cls: 'flag'
40026                     },
40027                     {
40028                         tag: 'div',
40029                         cls: 'caret'
40030                     }
40031                 ]
40032             };
40033             
40034             var box = {
40035                 tag: 'div',
40036                 cls: this.hasFeedback ? 'has-feedback' : '',
40037                 cn: [
40038                     hiddenInput,
40039                     input,
40040                     {
40041                         tag: 'input',
40042                         cls: 'dial-code-holder',
40043                         disabled: true
40044                     }
40045                 ]
40046             };
40047             
40048             var container = {
40049                 cls: 'roo-select2-container input-group',
40050                 cn: [
40051                     flag_container,
40052                     box
40053                 ]
40054             };
40055             
40056             if (this.fieldLabel.length) {
40057                 var indicator = {
40058                     tag: 'i',
40059                     tooltip: 'This field is required'
40060                 };
40061                 
40062                 var label = {
40063                     tag: 'label',
40064                     'for':  id,
40065                     cls: 'control-label',
40066                     cn: []
40067                 };
40068                 
40069                 var label_text = {
40070                     tag: 'span',
40071                     html: this.fieldLabel
40072                 };
40073                 
40074                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40075                 label.cn = [
40076                     indicator,
40077                     label_text
40078                 ];
40079                 
40080                 if(this.indicatorpos == 'right') {
40081                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40082                     label.cn = [
40083                         label_text,
40084                         indicator
40085                     ];
40086                 }
40087                 
40088                 if(align == 'left') {
40089                     container = {
40090                         tag: 'div',
40091                         cn: [
40092                             container
40093                         ]
40094                     };
40095                     
40096                     if(this.labelWidth > 12){
40097                         label.style = "width: " + this.labelWidth + 'px';
40098                     }
40099                     if(this.labelWidth < 13 && this.labelmd == 0){
40100                         this.labelmd = this.labelWidth;
40101                     }
40102                     if(this.labellg > 0){
40103                         label.cls += ' col-lg-' + this.labellg;
40104                         input.cls += ' col-lg-' + (12 - this.labellg);
40105                     }
40106                     if(this.labelmd > 0){
40107                         label.cls += ' col-md-' + this.labelmd;
40108                         container.cls += ' col-md-' + (12 - this.labelmd);
40109                     }
40110                     if(this.labelsm > 0){
40111                         label.cls += ' col-sm-' + this.labelsm;
40112                         container.cls += ' col-sm-' + (12 - this.labelsm);
40113                     }
40114                     if(this.labelxs > 0){
40115                         label.cls += ' col-xs-' + this.labelxs;
40116                         container.cls += ' col-xs-' + (12 - this.labelxs);
40117                     }
40118                 }
40119             }
40120             
40121             cfg.cn = [
40122                 label,
40123                 container
40124             ];
40125             
40126             var settings = this;
40127             
40128             ['xs','sm','md','lg'].map(function(size){
40129                 if (settings[size]) {
40130                     cfg.cls += ' col-' + size + '-' + settings[size];
40131                 }
40132             });
40133             
40134             this.store = new Roo.data.Store({
40135                 proxy : new Roo.data.MemoryProxy({}),
40136                 reader : new Roo.data.JsonReader({
40137                     fields : [
40138                         {
40139                             'name' : 'name',
40140                             'type' : 'string'
40141                         },
40142                         {
40143                             'name' : 'iso2',
40144                             'type' : 'string'
40145                         },
40146                         {
40147                             'name' : 'dialCode',
40148                             'type' : 'string'
40149                         },
40150                         {
40151                             'name' : 'priority',
40152                             'type' : 'string'
40153                         },
40154                         {
40155                             'name' : 'areaCodes',
40156                             'type' : 'string'
40157                         }
40158                     ]
40159                 })
40160             });
40161             
40162             if(!this.preferedCountries) {
40163                 this.preferedCountries = [
40164                     'hk',
40165                     'gb',
40166                     'us'
40167                 ];
40168             }
40169             
40170             var p = this.preferedCountries.reverse();
40171             
40172             if(p) {
40173                 for (var i = 0; i < p.length; i++) {
40174                     for (var j = 0; j < this.allCountries.length; j++) {
40175                         if(this.allCountries[j].iso2 == p[i]) {
40176                             var t = this.allCountries[j];
40177                             this.allCountries.splice(j,1);
40178                             this.allCountries.unshift(t);
40179                         }
40180                     } 
40181                 }
40182             }
40183             
40184             this.store.proxy.data = {
40185                 success: true,
40186                 data: this.allCountries
40187             };
40188             
40189             return cfg;
40190         },
40191         
40192         initEvents : function()
40193         {
40194             this.createList();
40195             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40196             
40197             this.indicator = this.indicatorEl();
40198             this.flag = this.flagEl();
40199             this.dialCodeHolder = this.dialCodeHolderEl();
40200             
40201             this.trigger = this.el.select('div.flag-box',true).first();
40202             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40203             
40204             var _this = this;
40205             
40206             (function(){
40207                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40208                 _this.list.setWidth(lw);
40209             }).defer(100);
40210             
40211             this.list.on('mouseover', this.onViewOver, this);
40212             this.list.on('mousemove', this.onViewMove, this);
40213             this.inputEl().on("keyup", this.onKeyUp, this);
40214             this.inputEl().on("keypress", this.onKeyPress, this);
40215             
40216             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40217
40218             this.view = new Roo.View(this.list, this.tpl, {
40219                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40220             });
40221             
40222             this.view.on('click', this.onViewClick, this);
40223             this.setValue(this.defaultDialCode);
40224         },
40225         
40226         onTriggerClick : function(e)
40227         {
40228             Roo.log('trigger click');
40229             if(this.disabled){
40230                 return;
40231             }
40232             
40233             if(this.isExpanded()){
40234                 this.collapse();
40235                 this.hasFocus = false;
40236             }else {
40237                 this.store.load({});
40238                 this.hasFocus = true;
40239                 this.expand();
40240             }
40241         },
40242         
40243         isExpanded : function()
40244         {
40245             return this.list.isVisible();
40246         },
40247         
40248         collapse : function()
40249         {
40250             if(!this.isExpanded()){
40251                 return;
40252             }
40253             this.list.hide();
40254             Roo.get(document).un('mousedown', this.collapseIf, this);
40255             Roo.get(document).un('mousewheel', this.collapseIf, this);
40256             this.fireEvent('collapse', this);
40257             this.validate();
40258         },
40259         
40260         expand : function()
40261         {
40262             Roo.log('expand');
40263
40264             if(this.isExpanded() || !this.hasFocus){
40265                 return;
40266             }
40267             
40268             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40269             this.list.setWidth(lw);
40270             
40271             this.list.show();
40272             this.restrictHeight();
40273             
40274             Roo.get(document).on('mousedown', this.collapseIf, this);
40275             Roo.get(document).on('mousewheel', this.collapseIf, this);
40276             
40277             this.fireEvent('expand', this);
40278         },
40279         
40280         restrictHeight : function()
40281         {
40282             this.list.alignTo(this.inputEl(), this.listAlign);
40283             this.list.alignTo(this.inputEl(), this.listAlign);
40284         },
40285         
40286         onViewOver : function(e, t)
40287         {
40288             if(this.inKeyMode){
40289                 return;
40290             }
40291             var item = this.view.findItemFromChild(t);
40292             
40293             if(item){
40294                 var index = this.view.indexOf(item);
40295                 this.select(index, false);
40296             }
40297         },
40298
40299         // private
40300         onViewClick : function(view, doFocus, el, e)
40301         {
40302             var index = this.view.getSelectedIndexes()[0];
40303             
40304             var r = this.store.getAt(index);
40305             
40306             if(r){
40307                 this.onSelect(r, index);
40308             }
40309             if(doFocus !== false && !this.blockFocus){
40310                 this.inputEl().focus();
40311             }
40312         },
40313         
40314         onViewMove : function(e, t)
40315         {
40316             this.inKeyMode = false;
40317         },
40318         
40319         select : function(index, scrollIntoView)
40320         {
40321             this.selectedIndex = index;
40322             this.view.select(index);
40323             if(scrollIntoView !== false){
40324                 var el = this.view.getNode(index);
40325                 if(el){
40326                     this.list.scrollChildIntoView(el, false);
40327                 }
40328             }
40329         },
40330         
40331         createList : function()
40332         {
40333             this.list = Roo.get(document.body).createChild({
40334                 tag: 'ul',
40335                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40336                 style: 'display:none'
40337             });
40338             
40339             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40340         },
40341         
40342         collapseIf : function(e)
40343         {
40344             var in_combo  = e.within(this.el);
40345             var in_list =  e.within(this.list);
40346             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40347             
40348             if (in_combo || in_list || is_list) {
40349                 return;
40350             }
40351             this.collapse();
40352         },
40353         
40354         onSelect : function(record, index)
40355         {
40356             if(this.fireEvent('beforeselect', this, record, index) !== false){
40357                 
40358                 this.setFlagClass(record.data.iso2);
40359                 this.setDialCode(record.data.dialCode);
40360                 this.hasFocus = false;
40361                 this.collapse();
40362                 this.fireEvent('select', this, record, index);
40363             }
40364         },
40365         
40366         flagEl : function()
40367         {
40368             var flag = this.el.select('div.flag',true).first();
40369             if(!flag){
40370                 return false;
40371             }
40372             return flag;
40373         },
40374         
40375         dialCodeHolderEl : function()
40376         {
40377             var d = this.el.select('input.dial-code-holder',true).first();
40378             if(!d){
40379                 return false;
40380             }
40381             return d;
40382         },
40383         
40384         setDialCode : function(v)
40385         {
40386             this.dialCodeHolder.dom.value = '+'+v;
40387         },
40388         
40389         setFlagClass : function(n)
40390         {
40391             this.flag.dom.className = 'flag '+n;
40392         },
40393         
40394         getValue : function()
40395         {
40396             var v = this.inputEl().getValue();
40397             if(this.dialCodeHolder) {
40398                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40399             }
40400             return v;
40401         },
40402         
40403         setValue : function(v)
40404         {
40405             var d = this.getDialCode(v);
40406             
40407             //invalid dial code
40408             if(v.length == 0 || !d || d.length == 0) {
40409                 if(this.rendered){
40410                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40411                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40412                 }
40413                 return;
40414             }
40415             
40416             //valid dial code
40417             this.setFlagClass(this.dialCodeMapping[d].iso2);
40418             this.setDialCode(d);
40419             this.inputEl().dom.value = v.replace('+'+d,'');
40420             this.hiddenEl().dom.value = this.getValue();
40421             
40422             this.validate();
40423         },
40424         
40425         getDialCode : function(v)
40426         {
40427             v = v ||  '';
40428             
40429             if (v.length == 0) {
40430                 return this.dialCodeHolder.dom.value;
40431             }
40432             
40433             var dialCode = "";
40434             if (v.charAt(0) != "+") {
40435                 return false;
40436             }
40437             var numericChars = "";
40438             for (var i = 1; i < v.length; i++) {
40439               var c = v.charAt(i);
40440               if (!isNaN(c)) {
40441                 numericChars += c;
40442                 if (this.dialCodeMapping[numericChars]) {
40443                   dialCode = v.substr(1, i);
40444                 }
40445                 if (numericChars.length == 4) {
40446                   break;
40447                 }
40448               }
40449             }
40450             return dialCode;
40451         },
40452         
40453         reset : function()
40454         {
40455             this.setValue(this.defaultDialCode);
40456             this.validate();
40457         },
40458         
40459         hiddenEl : function()
40460         {
40461             return this.el.select('input.hidden-tel-input',true).first();
40462         },
40463         
40464         // after setting val
40465         onKeyUp : function(e){
40466             this.setValue(this.getValue());
40467         },
40468         
40469         onKeyPress : function(e){
40470             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40471                 e.stopEvent();
40472             }
40473         }
40474         
40475 });
40476 /**
40477  * @class Roo.bootstrap.MoneyField
40478  * @extends Roo.bootstrap.ComboBox
40479  * Bootstrap MoneyField class
40480  * 
40481  * @constructor
40482  * Create a new MoneyField.
40483  * @param {Object} config Configuration options
40484  */
40485
40486 Roo.bootstrap.MoneyField = function(config) {
40487     
40488     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40489     
40490 };
40491
40492 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40493     
40494     /**
40495      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40496      */
40497     allowDecimals : true,
40498     /**
40499      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40500      */
40501     decimalSeparator : ".",
40502     /**
40503      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40504      */
40505     decimalPrecision : 0,
40506     /**
40507      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40508      */
40509     allowNegative : true,
40510     /**
40511      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40512      */
40513     allowZero: true,
40514     /**
40515      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40516      */
40517     minValue : Number.NEGATIVE_INFINITY,
40518     /**
40519      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40520      */
40521     maxValue : Number.MAX_VALUE,
40522     /**
40523      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40524      */
40525     minText : "The minimum value for this field is {0}",
40526     /**
40527      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40528      */
40529     maxText : "The maximum value for this field is {0}",
40530     /**
40531      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40532      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40533      */
40534     nanText : "{0} is not a valid number",
40535     /**
40536      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40537      */
40538     castInt : true,
40539     /**
40540      * @cfg {String} defaults currency of the MoneyField
40541      * value should be in lkey
40542      */
40543     defaultCurrency : false,
40544     /**
40545      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40546      */
40547     thousandsDelimiter : false,
40548     /**
40549      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40550      */
40551     max_length: false,
40552     
40553     inputlg : 9,
40554     inputmd : 9,
40555     inputsm : 9,
40556     inputxs : 6,
40557     
40558     store : false,
40559     
40560     getAutoCreate : function()
40561     {
40562         var align = this.labelAlign || this.parentLabelAlign();
40563         
40564         var id = Roo.id();
40565
40566         var cfg = {
40567             cls: 'form-group',
40568             cn: []
40569         };
40570
40571         var input =  {
40572             tag: 'input',
40573             id : id,
40574             cls : 'form-control roo-money-amount-input',
40575             autocomplete: 'new-password'
40576         };
40577         
40578         var hiddenInput = {
40579             tag: 'input',
40580             type: 'hidden',
40581             id: Roo.id(),
40582             cls: 'hidden-number-input'
40583         };
40584         
40585         if(this.max_length) {
40586             input.maxlength = this.max_length; 
40587         }
40588         
40589         if (this.name) {
40590             hiddenInput.name = this.name;
40591         }
40592
40593         if (this.disabled) {
40594             input.disabled = true;
40595         }
40596
40597         var clg = 12 - this.inputlg;
40598         var cmd = 12 - this.inputmd;
40599         var csm = 12 - this.inputsm;
40600         var cxs = 12 - this.inputxs;
40601         
40602         var container = {
40603             tag : 'div',
40604             cls : 'row roo-money-field',
40605             cn : [
40606                 {
40607                     tag : 'div',
40608                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40609                     cn : [
40610                         {
40611                             tag : 'div',
40612                             cls: 'roo-select2-container input-group',
40613                             cn: [
40614                                 {
40615                                     tag : 'input',
40616                                     cls : 'form-control roo-money-currency-input',
40617                                     autocomplete: 'new-password',
40618                                     readOnly : 1,
40619                                     name : this.currencyName
40620                                 },
40621                                 {
40622                                     tag :'span',
40623                                     cls : 'input-group-addon',
40624                                     cn : [
40625                                         {
40626                                             tag: 'span',
40627                                             cls: 'caret'
40628                                         }
40629                                     ]
40630                                 }
40631                             ]
40632                         }
40633                     ]
40634                 },
40635                 {
40636                     tag : 'div',
40637                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40638                     cn : [
40639                         {
40640                             tag: 'div',
40641                             cls: this.hasFeedback ? 'has-feedback' : '',
40642                             cn: [
40643                                 input
40644                             ]
40645                         }
40646                     ]
40647                 }
40648             ]
40649             
40650         };
40651         
40652         if (this.fieldLabel.length) {
40653             var indicator = {
40654                 tag: 'i',
40655                 tooltip: 'This field is required'
40656             };
40657
40658             var label = {
40659                 tag: 'label',
40660                 'for':  id,
40661                 cls: 'control-label',
40662                 cn: []
40663             };
40664
40665             var label_text = {
40666                 tag: 'span',
40667                 html: this.fieldLabel
40668             };
40669
40670             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40671             label.cn = [
40672                 indicator,
40673                 label_text
40674             ];
40675
40676             if(this.indicatorpos == 'right') {
40677                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40678                 label.cn = [
40679                     label_text,
40680                     indicator
40681                 ];
40682             }
40683
40684             if(align == 'left') {
40685                 container = {
40686                     tag: 'div',
40687                     cn: [
40688                         container
40689                     ]
40690                 };
40691
40692                 if(this.labelWidth > 12){
40693                     label.style = "width: " + this.labelWidth + 'px';
40694                 }
40695                 if(this.labelWidth < 13 && this.labelmd == 0){
40696                     this.labelmd = this.labelWidth;
40697                 }
40698                 if(this.labellg > 0){
40699                     label.cls += ' col-lg-' + this.labellg;
40700                     input.cls += ' col-lg-' + (12 - this.labellg);
40701                 }
40702                 if(this.labelmd > 0){
40703                     label.cls += ' col-md-' + this.labelmd;
40704                     container.cls += ' col-md-' + (12 - this.labelmd);
40705                 }
40706                 if(this.labelsm > 0){
40707                     label.cls += ' col-sm-' + this.labelsm;
40708                     container.cls += ' col-sm-' + (12 - this.labelsm);
40709                 }
40710                 if(this.labelxs > 0){
40711                     label.cls += ' col-xs-' + this.labelxs;
40712                     container.cls += ' col-xs-' + (12 - this.labelxs);
40713                 }
40714             }
40715         }
40716
40717         cfg.cn = [
40718             label,
40719             container,
40720             hiddenInput
40721         ];
40722         
40723         var settings = this;
40724
40725         ['xs','sm','md','lg'].map(function(size){
40726             if (settings[size]) {
40727                 cfg.cls += ' col-' + size + '-' + settings[size];
40728             }
40729         });
40730         
40731         return cfg;
40732     },
40733     
40734     initEvents : function()
40735     {
40736         this.indicator = this.indicatorEl();
40737         
40738         this.initCurrencyEvent();
40739         
40740         this.initNumberEvent();
40741     },
40742     
40743     initCurrencyEvent : function()
40744     {
40745         if (!this.store) {
40746             throw "can not find store for combo";
40747         }
40748         
40749         this.store = Roo.factory(this.store, Roo.data);
40750         this.store.parent = this;
40751         
40752         this.createList();
40753         
40754         this.triggerEl = this.el.select('.input-group-addon', true).first();
40755         
40756         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40757         
40758         var _this = this;
40759         
40760         (function(){
40761             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40762             _this.list.setWidth(lw);
40763         }).defer(100);
40764         
40765         this.list.on('mouseover', this.onViewOver, this);
40766         this.list.on('mousemove', this.onViewMove, this);
40767         this.list.on('scroll', this.onViewScroll, this);
40768         
40769         if(!this.tpl){
40770             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40771         }
40772         
40773         this.view = new Roo.View(this.list, this.tpl, {
40774             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40775         });
40776         
40777         this.view.on('click', this.onViewClick, this);
40778         
40779         this.store.on('beforeload', this.onBeforeLoad, this);
40780         this.store.on('load', this.onLoad, this);
40781         this.store.on('loadexception', this.onLoadException, this);
40782         
40783         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40784             "up" : function(e){
40785                 this.inKeyMode = true;
40786                 this.selectPrev();
40787             },
40788
40789             "down" : function(e){
40790                 if(!this.isExpanded()){
40791                     this.onTriggerClick();
40792                 }else{
40793                     this.inKeyMode = true;
40794                     this.selectNext();
40795                 }
40796             },
40797
40798             "enter" : function(e){
40799                 this.collapse();
40800                 
40801                 if(this.fireEvent("specialkey", this, e)){
40802                     this.onViewClick(false);
40803                 }
40804                 
40805                 return true;
40806             },
40807
40808             "esc" : function(e){
40809                 this.collapse();
40810             },
40811
40812             "tab" : function(e){
40813                 this.collapse();
40814                 
40815                 if(this.fireEvent("specialkey", this, e)){
40816                     this.onViewClick(false);
40817                 }
40818                 
40819                 return true;
40820             },
40821
40822             scope : this,
40823
40824             doRelay : function(foo, bar, hname){
40825                 if(hname == 'down' || this.scope.isExpanded()){
40826                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40827                 }
40828                 return true;
40829             },
40830
40831             forceKeyDown: true
40832         });
40833         
40834         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40835         
40836     },
40837     
40838     initNumberEvent : function(e)
40839     {
40840         this.inputEl().on("keydown" , this.fireKey,  this);
40841         this.inputEl().on("focus", this.onFocus,  this);
40842         this.inputEl().on("blur", this.onBlur,  this);
40843         
40844         this.inputEl().relayEvent('keyup', this);
40845         
40846         if(this.indicator){
40847             this.indicator.addClass('invisible');
40848         }
40849  
40850         this.originalValue = this.getValue();
40851         
40852         if(this.validationEvent == 'keyup'){
40853             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40854             this.inputEl().on('keyup', this.filterValidation, this);
40855         }
40856         else if(this.validationEvent !== false){
40857             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40858         }
40859         
40860         if(this.selectOnFocus){
40861             this.on("focus", this.preFocus, this);
40862             
40863         }
40864         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40865             this.inputEl().on("keypress", this.filterKeys, this);
40866         } else {
40867             this.inputEl().relayEvent('keypress', this);
40868         }
40869         
40870         var allowed = "0123456789";
40871         
40872         if(this.allowDecimals){
40873             allowed += this.decimalSeparator;
40874         }
40875         
40876         if(this.allowNegative){
40877             allowed += "-";
40878         }
40879         
40880         if(this.thousandsDelimiter) {
40881             allowed += ",";
40882         }
40883         
40884         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40885         
40886         var keyPress = function(e){
40887             
40888             var k = e.getKey();
40889             
40890             var c = e.getCharCode();
40891             
40892             if(
40893                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40894                     allowed.indexOf(String.fromCharCode(c)) === -1
40895             ){
40896                 e.stopEvent();
40897                 return;
40898             }
40899             
40900             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40901                 return;
40902             }
40903             
40904             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40905                 e.stopEvent();
40906             }
40907         };
40908         
40909         this.inputEl().on("keypress", keyPress, this);
40910         
40911     },
40912     
40913     onTriggerClick : function(e)
40914     {   
40915         if(this.disabled){
40916             return;
40917         }
40918         
40919         this.page = 0;
40920         this.loadNext = false;
40921         
40922         if(this.isExpanded()){
40923             this.collapse();
40924             return;
40925         }
40926         
40927         this.hasFocus = true;
40928         
40929         if(this.triggerAction == 'all') {
40930             this.doQuery(this.allQuery, true);
40931             return;
40932         }
40933         
40934         this.doQuery(this.getRawValue());
40935     },
40936     
40937     getCurrency : function()
40938     {   
40939         var v = this.currencyEl().getValue();
40940         
40941         return v;
40942     },
40943     
40944     restrictHeight : function()
40945     {
40946         this.list.alignTo(this.currencyEl(), this.listAlign);
40947         this.list.alignTo(this.currencyEl(), this.listAlign);
40948     },
40949     
40950     onViewClick : function(view, doFocus, el, e)
40951     {
40952         var index = this.view.getSelectedIndexes()[0];
40953         
40954         var r = this.store.getAt(index);
40955         
40956         if(r){
40957             this.onSelect(r, index);
40958         }
40959     },
40960     
40961     onSelect : function(record, index){
40962         
40963         if(this.fireEvent('beforeselect', this, record, index) !== false){
40964         
40965             this.setFromCurrencyData(index > -1 ? record.data : false);
40966             
40967             this.collapse();
40968             
40969             this.fireEvent('select', this, record, index);
40970         }
40971     },
40972     
40973     setFromCurrencyData : function(o)
40974     {
40975         var currency = '';
40976         
40977         this.lastCurrency = o;
40978         
40979         if (this.currencyField) {
40980             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40981         } else {
40982             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40983         }
40984         
40985         this.lastSelectionText = currency;
40986         
40987         //setting default currency
40988         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40989             this.setCurrency(this.defaultCurrency);
40990             return;
40991         }
40992         
40993         this.setCurrency(currency);
40994     },
40995     
40996     setFromData : function(o)
40997     {
40998         var c = {};
40999         
41000         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41001         
41002         this.setFromCurrencyData(c);
41003         
41004         var value = '';
41005         
41006         if (this.name) {
41007             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41008         } else {
41009             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41010         }
41011         
41012         this.setValue(value);
41013         
41014     },
41015     
41016     setCurrency : function(v)
41017     {   
41018         this.currencyValue = v;
41019         
41020         if(this.rendered){
41021             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41022             this.validate();
41023         }
41024     },
41025     
41026     setValue : function(v)
41027     {
41028         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41029         
41030         this.value = v;
41031         
41032         if(this.rendered){
41033             
41034             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41035             
41036             this.inputEl().dom.value = (v == '') ? '' :
41037                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41038             
41039             if(!this.allowZero && v === '0') {
41040                 this.hiddenEl().dom.value = '';
41041                 this.inputEl().dom.value = '';
41042             }
41043             
41044             this.validate();
41045         }
41046     },
41047     
41048     getRawValue : function()
41049     {
41050         var v = this.inputEl().getValue();
41051         
41052         return v;
41053     },
41054     
41055     getValue : function()
41056     {
41057         return this.fixPrecision(this.parseValue(this.getRawValue()));
41058     },
41059     
41060     parseValue : function(value)
41061     {
41062         if(this.thousandsDelimiter) {
41063             value += "";
41064             r = new RegExp(",", "g");
41065             value = value.replace(r, "");
41066         }
41067         
41068         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41069         return isNaN(value) ? '' : value;
41070         
41071     },
41072     
41073     fixPrecision : function(value)
41074     {
41075         if(this.thousandsDelimiter) {
41076             value += "";
41077             r = new RegExp(",", "g");
41078             value = value.replace(r, "");
41079         }
41080         
41081         var nan = isNaN(value);
41082         
41083         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41084             return nan ? '' : value;
41085         }
41086         return parseFloat(value).toFixed(this.decimalPrecision);
41087     },
41088     
41089     decimalPrecisionFcn : function(v)
41090     {
41091         return Math.floor(v);
41092     },
41093     
41094     validateValue : function(value)
41095     {
41096         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41097             return false;
41098         }
41099         
41100         var num = this.parseValue(value);
41101         
41102         if(isNaN(num)){
41103             this.markInvalid(String.format(this.nanText, value));
41104             return false;
41105         }
41106         
41107         if(num < this.minValue){
41108             this.markInvalid(String.format(this.minText, this.minValue));
41109             return false;
41110         }
41111         
41112         if(num > this.maxValue){
41113             this.markInvalid(String.format(this.maxText, this.maxValue));
41114             return false;
41115         }
41116         
41117         return true;
41118     },
41119     
41120     validate : function()
41121     {
41122         if(this.disabled || this.allowBlank){
41123             this.markValid();
41124             return true;
41125         }
41126         
41127         var currency = this.getCurrency();
41128         
41129         if(this.validateValue(this.getRawValue()) && currency.length){
41130             this.markValid();
41131             return true;
41132         }
41133         
41134         this.markInvalid();
41135         return false;
41136     },
41137     
41138     getName: function()
41139     {
41140         return this.name;
41141     },
41142     
41143     beforeBlur : function()
41144     {
41145         if(!this.castInt){
41146             return;
41147         }
41148         
41149         var v = this.parseValue(this.getRawValue());
41150         
41151         if(v || v == 0){
41152             this.setValue(v);
41153         }
41154     },
41155     
41156     onBlur : function()
41157     {
41158         this.beforeBlur();
41159         
41160         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41161             //this.el.removeClass(this.focusClass);
41162         }
41163         
41164         this.hasFocus = false;
41165         
41166         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41167             this.validate();
41168         }
41169         
41170         var v = this.getValue();
41171         
41172         if(String(v) !== String(this.startValue)){
41173             this.fireEvent('change', this, v, this.startValue);
41174         }
41175         
41176         this.fireEvent("blur", this);
41177     },
41178     
41179     inputEl : function()
41180     {
41181         return this.el.select('.roo-money-amount-input', true).first();
41182     },
41183     
41184     currencyEl : function()
41185     {
41186         return this.el.select('.roo-money-currency-input', true).first();
41187     },
41188     
41189     hiddenEl : function()
41190     {
41191         return this.el.select('input.hidden-number-input',true).first();
41192     }
41193     
41194 });