Uncommited changes synced
[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',
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-' +
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-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-' +
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         
9160         if (align ==='left' && this.fieldLabel.length) {
9161             
9162             cfg.cls += ' roo-form-group-label-left';
9163             
9164             cfg.cn = [
9165                 {
9166                     tag : 'i',
9167                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9168                     tooltip : 'This field is required'
9169                 },
9170                 {
9171                     tag: 'label',
9172                     'for' :  id,
9173                     cls : 'control-label',
9174                     html : this.fieldLabel
9175
9176                 },
9177                 {
9178                     cls : "", 
9179                     cn: [
9180                         inputblock
9181                     ]
9182                 }
9183             ];
9184             
9185             var labelCfg = cfg.cn[1];
9186             var contentCfg = cfg.cn[2];
9187             
9188             if(this.indicatorpos == 'right'){
9189                 cfg.cn = [
9190                     {
9191                         tag: 'label',
9192                         'for' :  id,
9193                         cls : 'control-label',
9194                         cn : [
9195                             {
9196                                 tag : 'span',
9197                                 html : this.fieldLabel
9198                             },
9199                             {
9200                                 tag : 'i',
9201                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9202                                 tooltip : 'This field is required'
9203                             }
9204                         ]
9205                     },
9206                     {
9207                         cls : "",
9208                         cn: [
9209                             inputblock
9210                         ]
9211                     }
9212
9213                 ];
9214                 
9215                 labelCfg = cfg.cn[0];
9216                 contentCfg = cfg.cn[1];
9217             
9218             }
9219             
9220             if(this.labelWidth > 12){
9221                 labelCfg.style = "width: " + this.labelWidth + 'px';
9222             }
9223             
9224             if(this.labelWidth < 13 && this.labelmd == 0){
9225                 this.labelmd = this.labelWidth;
9226             }
9227             
9228             if(this.labellg > 0){
9229                 labelCfg.cls += ' col-lg-' + this.labellg;
9230                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9231             }
9232             
9233             if(this.labelmd > 0){
9234                 labelCfg.cls += ' col-md-' + this.labelmd;
9235                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9236             }
9237             
9238             if(this.labelsm > 0){
9239                 labelCfg.cls += ' col-sm-' + this.labelsm;
9240                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9241             }
9242             
9243             if(this.labelxs > 0){
9244                 labelCfg.cls += ' col-xs-' + this.labelxs;
9245                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9246             }
9247             
9248             
9249         } else if ( this.fieldLabel.length) {
9250                 
9251             cfg.cn = [
9252                 {
9253                     tag : 'i',
9254                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9255                     tooltip : 'This field is required'
9256                 },
9257                 {
9258                     tag: 'label',
9259                    //cls : 'input-group-addon',
9260                     html : this.fieldLabel
9261
9262                 },
9263
9264                inputblock
9265
9266            ];
9267            
9268            if(this.indicatorpos == 'right'){
9269                 
9270                 cfg.cn = [
9271                     {
9272                         tag: 'label',
9273                        //cls : 'input-group-addon',
9274                         html : this.fieldLabel
9275
9276                     },
9277                     {
9278                         tag : 'i',
9279                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9280                         tooltip : 'This field is required'
9281                     },
9282
9283                    inputblock
9284
9285                ];
9286
9287             }
9288
9289         } else {
9290             
9291             cfg.cn = [
9292
9293                     inputblock
9294
9295             ];
9296                 
9297                 
9298         };
9299         
9300         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9301            cfg.cls += ' navbar-form';
9302         }
9303         
9304         if (this.parentType === 'NavGroup') {
9305            cfg.cls += ' navbar-form';
9306            cfg.tag = 'li';
9307         }
9308         
9309         return cfg;
9310         
9311     },
9312     /**
9313      * return the real input element.
9314      */
9315     inputEl: function ()
9316     {
9317         return this.el.select('input.form-control',true).first();
9318     },
9319     
9320     tooltipEl : function()
9321     {
9322         return this.inputEl();
9323     },
9324     
9325     indicatorEl : function()
9326     {
9327         var indicator = this.el.select('i.roo-required-indicator',true).first();
9328         
9329         if(!indicator){
9330             return false;
9331         }
9332         
9333         return indicator;
9334         
9335     },
9336     
9337     setDisabled : function(v)
9338     {
9339         var i  = this.inputEl().dom;
9340         if (!v) {
9341             i.removeAttribute('disabled');
9342             return;
9343             
9344         }
9345         i.setAttribute('disabled','true');
9346     },
9347     initEvents : function()
9348     {
9349           
9350         this.inputEl().on("keydown" , this.fireKey,  this);
9351         this.inputEl().on("focus", this.onFocus,  this);
9352         this.inputEl().on("blur", this.onBlur,  this);
9353         
9354         this.inputEl().relayEvent('keyup', this);
9355         
9356         this.indicator = this.indicatorEl();
9357         
9358         if(this.indicator){
9359             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9360         }
9361  
9362         // reference to original value for reset
9363         this.originalValue = this.getValue();
9364         //Roo.form.TextField.superclass.initEvents.call(this);
9365         if(this.validationEvent == 'keyup'){
9366             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9367             this.inputEl().on('keyup', this.filterValidation, this);
9368         }
9369         else if(this.validationEvent !== false){
9370             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9371         }
9372         
9373         if(this.selectOnFocus){
9374             this.on("focus", this.preFocus, this);
9375             
9376         }
9377         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9378             this.inputEl().on("keypress", this.filterKeys, this);
9379         } else {
9380             this.inputEl().relayEvent('keypress', this);
9381         }
9382        /* if(this.grow){
9383             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9384             this.el.on("click", this.autoSize,  this);
9385         }
9386         */
9387         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9388             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9389         }
9390         
9391         if (typeof(this.before) == 'object') {
9392             this.before.render(this.el.select('.roo-input-before',true).first());
9393         }
9394         if (typeof(this.after) == 'object') {
9395             this.after.render(this.el.select('.roo-input-after',true).first());
9396         }
9397         
9398         this.inputEl().on('change', this.onChange, this);
9399         
9400     },
9401     filterValidation : function(e){
9402         if(!e.isNavKeyPress()){
9403             this.validationTask.delay(this.validationDelay);
9404         }
9405     },
9406      /**
9407      * Validates the field value
9408      * @return {Boolean} True if the value is valid, else false
9409      */
9410     validate : function(){
9411         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9412         if(this.disabled || this.validateValue(this.getRawValue())){
9413             this.markValid();
9414             return true;
9415         }
9416         
9417         this.markInvalid();
9418         return false;
9419     },
9420     
9421     
9422     /**
9423      * Validates a value according to the field's validation rules and marks the field as invalid
9424      * if the validation fails
9425      * @param {Mixed} value The value to validate
9426      * @return {Boolean} True if the value is valid, else false
9427      */
9428     validateValue : function(value)
9429     {
9430         if(this.getVisibilityEl().hasClass('hidden')){
9431             return true;
9432         }
9433         
9434         if(value.length < 1)  { // if it's blank
9435             if(this.allowBlank){
9436                 return true;
9437             }
9438             return false;
9439         }
9440         
9441         if(value.length < this.minLength){
9442             return false;
9443         }
9444         if(value.length > this.maxLength){
9445             return false;
9446         }
9447         if(this.vtype){
9448             var vt = Roo.form.VTypes;
9449             if(!vt[this.vtype](value, this)){
9450                 return false;
9451             }
9452         }
9453         if(typeof this.validator == "function"){
9454             var msg = this.validator(value);
9455             if(msg !== true){
9456                 return false;
9457             }
9458             if (typeof(msg) == 'string') {
9459                 this.invalidText = msg;
9460             }
9461         }
9462         
9463         if(this.regex && !this.regex.test(value)){
9464             return false;
9465         }
9466         
9467         return true;
9468     },
9469     
9470      // private
9471     fireKey : function(e){
9472         //Roo.log('field ' + e.getKey());
9473         if(e.isNavKeyPress()){
9474             this.fireEvent("specialkey", this, e);
9475         }
9476     },
9477     focus : function (selectText){
9478         if(this.rendered){
9479             this.inputEl().focus();
9480             if(selectText === true){
9481                 this.inputEl().dom.select();
9482             }
9483         }
9484         return this;
9485     } ,
9486     
9487     onFocus : function(){
9488         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9489            // this.el.addClass(this.focusClass);
9490         }
9491         if(!this.hasFocus){
9492             this.hasFocus = true;
9493             this.startValue = this.getValue();
9494             this.fireEvent("focus", this);
9495         }
9496     },
9497     
9498     beforeBlur : Roo.emptyFn,
9499
9500     
9501     // private
9502     onBlur : function(){
9503         this.beforeBlur();
9504         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9505             //this.el.removeClass(this.focusClass);
9506         }
9507         this.hasFocus = false;
9508         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9509             this.validate();
9510         }
9511         var v = this.getValue();
9512         if(String(v) !== String(this.startValue)){
9513             this.fireEvent('change', this, v, this.startValue);
9514         }
9515         this.fireEvent("blur", this);
9516     },
9517     
9518     onChange : function(e)
9519     {
9520         var v = this.getValue();
9521         if(String(v) !== String(this.startValue)){
9522             this.fireEvent('change', this, v, this.startValue);
9523         }
9524         
9525     },
9526     
9527     /**
9528      * Resets the current field value to the originally loaded value and clears any validation messages
9529      */
9530     reset : function(){
9531         this.setValue(this.originalValue);
9532         this.validate();
9533     },
9534      /**
9535      * Returns the name of the field
9536      * @return {Mixed} name The name field
9537      */
9538     getName: function(){
9539         return this.name;
9540     },
9541      /**
9542      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9543      * @return {Mixed} value The field value
9544      */
9545     getValue : function(){
9546         
9547         var v = this.inputEl().getValue();
9548         
9549         return v;
9550     },
9551     /**
9552      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9553      * @return {Mixed} value The field value
9554      */
9555     getRawValue : function(){
9556         var v = this.inputEl().getValue();
9557         
9558         return v;
9559     },
9560     
9561     /**
9562      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9563      * @param {Mixed} value The value to set
9564      */
9565     setRawValue : function(v){
9566         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9567     },
9568     
9569     selectText : function(start, end){
9570         var v = this.getRawValue();
9571         if(v.length > 0){
9572             start = start === undefined ? 0 : start;
9573             end = end === undefined ? v.length : end;
9574             var d = this.inputEl().dom;
9575             if(d.setSelectionRange){
9576                 d.setSelectionRange(start, end);
9577             }else if(d.createTextRange){
9578                 var range = d.createTextRange();
9579                 range.moveStart("character", start);
9580                 range.moveEnd("character", v.length-end);
9581                 range.select();
9582             }
9583         }
9584     },
9585     
9586     /**
9587      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9588      * @param {Mixed} value The value to set
9589      */
9590     setValue : function(v){
9591         this.value = v;
9592         if(this.rendered){
9593             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9594             this.validate();
9595         }
9596     },
9597     
9598     /*
9599     processValue : function(value){
9600         if(this.stripCharsRe){
9601             var newValue = value.replace(this.stripCharsRe, '');
9602             if(newValue !== value){
9603                 this.setRawValue(newValue);
9604                 return newValue;
9605             }
9606         }
9607         return value;
9608     },
9609   */
9610     preFocus : function(){
9611         
9612         if(this.selectOnFocus){
9613             this.inputEl().dom.select();
9614         }
9615     },
9616     filterKeys : function(e){
9617         var k = e.getKey();
9618         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9619             return;
9620         }
9621         var c = e.getCharCode(), cc = String.fromCharCode(c);
9622         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9623             return;
9624         }
9625         if(!this.maskRe.test(cc)){
9626             e.stopEvent();
9627         }
9628     },
9629      /**
9630      * Clear any invalid styles/messages for this field
9631      */
9632     clearInvalid : function(){
9633         
9634         if(!this.el || this.preventMark){ // not rendered
9635             return;
9636         }
9637         
9638      
9639         this.el.removeClass(this.invalidClass);
9640         
9641         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9642             
9643             var feedback = this.el.select('.form-control-feedback', true).first();
9644             
9645             if(feedback){
9646                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9647             }
9648             
9649         }
9650         
9651         if(this.indicator){
9652             this.indicator.removeClass('visible');
9653             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9654         }
9655         
9656         this.fireEvent('valid', this);
9657     },
9658     
9659      /**
9660      * Mark this field as valid
9661      */
9662     markValid : function()
9663     {
9664         if(!this.el  || this.preventMark){ // not rendered...
9665             return;
9666         }
9667         
9668         this.el.removeClass([this.invalidClass, this.validClass]);
9669         
9670         var feedback = this.el.select('.form-control-feedback', true).first();
9671             
9672         if(feedback){
9673             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9674         }
9675         
9676         if(this.indicator){
9677             this.indicator.removeClass('visible');
9678             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9679         }
9680         
9681         if(this.disabled){
9682             return;
9683         }
9684         
9685         if(this.allowBlank && !this.getRawValue().length){
9686             return;
9687         }
9688         
9689         this.el.addClass(this.validClass);
9690         
9691         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9692             
9693             var feedback = this.el.select('.form-control-feedback', true).first();
9694             
9695             if(feedback){
9696                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9697                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9698             }
9699             
9700         }
9701         
9702         this.fireEvent('valid', this);
9703     },
9704     
9705      /**
9706      * Mark this field as invalid
9707      * @param {String} msg The validation message
9708      */
9709     markInvalid : function(msg)
9710     {
9711         if(!this.el  || this.preventMark){ // not rendered
9712             return;
9713         }
9714         
9715         this.el.removeClass([this.invalidClass, this.validClass]);
9716         
9717         var feedback = this.el.select('.form-control-feedback', true).first();
9718             
9719         if(feedback){
9720             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9721         }
9722
9723         if(this.disabled){
9724             return;
9725         }
9726         
9727         if(this.allowBlank && !this.getRawValue().length){
9728             return;
9729         }
9730         
9731         if(this.indicator){
9732             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9733             this.indicator.addClass('visible');
9734         }
9735         
9736         this.el.addClass(this.invalidClass);
9737         
9738         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9739             
9740             var feedback = this.el.select('.form-control-feedback', true).first();
9741             
9742             if(feedback){
9743                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9744                 
9745                 if(this.getValue().length || this.forceFeedback){
9746                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9747                 }
9748                 
9749             }
9750             
9751         }
9752         
9753         this.fireEvent('invalid', this, msg);
9754     },
9755     // private
9756     SafariOnKeyDown : function(event)
9757     {
9758         // this is a workaround for a password hang bug on chrome/ webkit.
9759         if (this.inputEl().dom.type != 'password') {
9760             return;
9761         }
9762         
9763         var isSelectAll = false;
9764         
9765         if(this.inputEl().dom.selectionEnd > 0){
9766             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9767         }
9768         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9769             event.preventDefault();
9770             this.setValue('');
9771             return;
9772         }
9773         
9774         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9775             
9776             event.preventDefault();
9777             // this is very hacky as keydown always get's upper case.
9778             //
9779             var cc = String.fromCharCode(event.getCharCode());
9780             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9781             
9782         }
9783     },
9784     adjustWidth : function(tag, w){
9785         tag = tag.toLowerCase();
9786         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9787             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9788                 if(tag == 'input'){
9789                     return w + 2;
9790                 }
9791                 if(tag == 'textarea'){
9792                     return w-2;
9793                 }
9794             }else if(Roo.isOpera){
9795                 if(tag == 'input'){
9796                     return w + 2;
9797                 }
9798                 if(tag == 'textarea'){
9799                     return w-2;
9800                 }
9801             }
9802         }
9803         return w;
9804     },
9805     
9806     setFieldLabel : function(v)
9807     {
9808         if(!this.rendered){
9809             return;
9810         }
9811         
9812         if(this.indicator){
9813             var ar = this.el.select('label > span',true);
9814             
9815             if (ar.elements.length) {
9816                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9817                 this.fieldLabel = v;
9818                 return;
9819             }
9820             
9821             var br = this.el.select('label',true);
9822             
9823             if(br.elements.length) {
9824                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9825                 this.fieldLabel = v;
9826                 return;
9827             }
9828             
9829             Roo.log('Cannot Found any of label > span || label in input');
9830             return;
9831         }
9832         
9833         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9834         this.fieldLabel = v;
9835         
9836         
9837     }
9838 });
9839
9840  
9841 /*
9842  * - LGPL
9843  *
9844  * Input
9845  * 
9846  */
9847
9848 /**
9849  * @class Roo.bootstrap.TextArea
9850  * @extends Roo.bootstrap.Input
9851  * Bootstrap TextArea class
9852  * @cfg {Number} cols Specifies the visible width of a text area
9853  * @cfg {Number} rows Specifies the visible number of lines in a text area
9854  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9855  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9856  * @cfg {string} html text
9857  * 
9858  * @constructor
9859  * Create a new TextArea
9860  * @param {Object} config The config object
9861  */
9862
9863 Roo.bootstrap.TextArea = function(config){
9864     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9865    
9866 };
9867
9868 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9869      
9870     cols : false,
9871     rows : 5,
9872     readOnly : false,
9873     warp : 'soft',
9874     resize : false,
9875     value: false,
9876     html: false,
9877     
9878     getAutoCreate : function(){
9879         
9880         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9881         
9882         var id = Roo.id();
9883         
9884         var cfg = {};
9885         
9886         if(this.inputType != 'hidden'){
9887             cfg.cls = 'form-group' //input-group
9888         }
9889         
9890         var input =  {
9891             tag: 'textarea',
9892             id : id,
9893             warp : this.warp,
9894             rows : this.rows,
9895             value : this.value || '',
9896             html: this.html || '',
9897             cls : 'form-control',
9898             placeholder : this.placeholder || '' 
9899             
9900         };
9901         
9902         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9903             input.maxLength = this.maxLength;
9904         }
9905         
9906         if(this.resize){
9907             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9908         }
9909         
9910         if(this.cols){
9911             input.cols = this.cols;
9912         }
9913         
9914         if (this.readOnly) {
9915             input.readonly = true;
9916         }
9917         
9918         if (this.name) {
9919             input.name = this.name;
9920         }
9921         
9922         if (this.size) {
9923             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9924         }
9925         
9926         var settings=this;
9927         ['xs','sm','md','lg'].map(function(size){
9928             if (settings[size]) {
9929                 cfg.cls += ' col-' + size + '-' + settings[size];
9930             }
9931         });
9932         
9933         var inputblock = input;
9934         
9935         if(this.hasFeedback && !this.allowBlank){
9936             
9937             var feedback = {
9938                 tag: 'span',
9939                 cls: 'glyphicon form-control-feedback'
9940             };
9941
9942             inputblock = {
9943                 cls : 'has-feedback',
9944                 cn :  [
9945                     input,
9946                     feedback
9947                 ] 
9948             };  
9949         }
9950         
9951         
9952         if (this.before || this.after) {
9953             
9954             inputblock = {
9955                 cls : 'input-group',
9956                 cn :  [] 
9957             };
9958             if (this.before) {
9959                 inputblock.cn.push({
9960                     tag :'span',
9961                     cls : 'input-group-addon',
9962                     html : this.before
9963                 });
9964             }
9965             
9966             inputblock.cn.push(input);
9967             
9968             if(this.hasFeedback && !this.allowBlank){
9969                 inputblock.cls += ' has-feedback';
9970                 inputblock.cn.push(feedback);
9971             }
9972             
9973             if (this.after) {
9974                 inputblock.cn.push({
9975                     tag :'span',
9976                     cls : 'input-group-addon',
9977                     html : this.after
9978                 });
9979             }
9980             
9981         }
9982         
9983         if (align ==='left' && this.fieldLabel.length) {
9984             cfg.cn = [
9985                 {
9986                     tag: 'label',
9987                     'for' :  id,
9988                     cls : 'control-label',
9989                     html : this.fieldLabel
9990                 },
9991                 {
9992                     cls : "",
9993                     cn: [
9994                         inputblock
9995                     ]
9996                 }
9997
9998             ];
9999             
10000             if(this.labelWidth > 12){
10001                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10002             }
10003
10004             if(this.labelWidth < 13 && this.labelmd == 0){
10005                 this.labelmd = this.labelWidth;
10006             }
10007
10008             if(this.labellg > 0){
10009                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10010                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10011             }
10012
10013             if(this.labelmd > 0){
10014                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10015                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10016             }
10017
10018             if(this.labelsm > 0){
10019                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10020                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10021             }
10022
10023             if(this.labelxs > 0){
10024                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10025                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10026             }
10027             
10028         } else if ( this.fieldLabel.length) {
10029             cfg.cn = [
10030
10031                {
10032                    tag: 'label',
10033                    //cls : 'input-group-addon',
10034                    html : this.fieldLabel
10035
10036                },
10037
10038                inputblock
10039
10040            ];
10041
10042         } else {
10043
10044             cfg.cn = [
10045
10046                 inputblock
10047
10048             ];
10049                 
10050         }
10051         
10052         if (this.disabled) {
10053             input.disabled=true;
10054         }
10055         
10056         return cfg;
10057         
10058     },
10059     /**
10060      * return the real textarea element.
10061      */
10062     inputEl: function ()
10063     {
10064         return this.el.select('textarea.form-control',true).first();
10065     },
10066     
10067     /**
10068      * Clear any invalid styles/messages for this field
10069      */
10070     clearInvalid : function()
10071     {
10072         
10073         if(!this.el || this.preventMark){ // not rendered
10074             return;
10075         }
10076         
10077         var label = this.el.select('label', true).first();
10078         var icon = this.el.select('i.fa-star', true).first();
10079         
10080         if(label && icon){
10081             icon.remove();
10082         }
10083         
10084         this.el.removeClass(this.invalidClass);
10085         
10086         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10087             
10088             var feedback = this.el.select('.form-control-feedback', true).first();
10089             
10090             if(feedback){
10091                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10092             }
10093             
10094         }
10095         
10096         this.fireEvent('valid', this);
10097     },
10098     
10099      /**
10100      * Mark this field as valid
10101      */
10102     markValid : function()
10103     {
10104         if(!this.el  || this.preventMark){ // not rendered
10105             return;
10106         }
10107         
10108         this.el.removeClass([this.invalidClass, this.validClass]);
10109         
10110         var feedback = this.el.select('.form-control-feedback', true).first();
10111             
10112         if(feedback){
10113             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10114         }
10115
10116         if(this.disabled || this.allowBlank){
10117             return;
10118         }
10119         
10120         var label = this.el.select('label', true).first();
10121         var icon = this.el.select('i.fa-star', true).first();
10122         
10123         if(label && icon){
10124             icon.remove();
10125         }
10126         
10127         this.el.addClass(this.validClass);
10128         
10129         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10130             
10131             var feedback = this.el.select('.form-control-feedback', true).first();
10132             
10133             if(feedback){
10134                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10135                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10136             }
10137             
10138         }
10139         
10140         this.fireEvent('valid', this);
10141     },
10142     
10143      /**
10144      * Mark this field as invalid
10145      * @param {String} msg The validation message
10146      */
10147     markInvalid : function(msg)
10148     {
10149         if(!this.el  || this.preventMark){ // not rendered
10150             return;
10151         }
10152         
10153         this.el.removeClass([this.invalidClass, this.validClass]);
10154         
10155         var feedback = this.el.select('.form-control-feedback', true).first();
10156             
10157         if(feedback){
10158             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10159         }
10160
10161         if(this.disabled || this.allowBlank){
10162             return;
10163         }
10164         
10165         var label = this.el.select('label', true).first();
10166         var icon = this.el.select('i.fa-star', true).first();
10167         
10168         if(!this.getValue().length && label && !icon){
10169             this.el.createChild({
10170                 tag : 'i',
10171                 cls : 'text-danger fa fa-lg fa-star',
10172                 tooltip : 'This field is required',
10173                 style : 'margin-right:5px;'
10174             }, label, true);
10175         }
10176
10177         this.el.addClass(this.invalidClass);
10178         
10179         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10180             
10181             var feedback = this.el.select('.form-control-feedback', true).first();
10182             
10183             if(feedback){
10184                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10185                 
10186                 if(this.getValue().length || this.forceFeedback){
10187                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10188                 }
10189                 
10190             }
10191             
10192         }
10193         
10194         this.fireEvent('invalid', this, msg);
10195     }
10196 });
10197
10198  
10199 /*
10200  * - LGPL
10201  *
10202  * trigger field - base class for combo..
10203  * 
10204  */
10205  
10206 /**
10207  * @class Roo.bootstrap.TriggerField
10208  * @extends Roo.bootstrap.Input
10209  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10210  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10211  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10212  * for which you can provide a custom implementation.  For example:
10213  * <pre><code>
10214 var trigger = new Roo.bootstrap.TriggerField();
10215 trigger.onTriggerClick = myTriggerFn;
10216 trigger.applyTo('my-field');
10217 </code></pre>
10218  *
10219  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10220  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10221  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10222  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10223  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10224
10225  * @constructor
10226  * Create a new TriggerField.
10227  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10228  * to the base TextField)
10229  */
10230 Roo.bootstrap.TriggerField = function(config){
10231     this.mimicing = false;
10232     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10233 };
10234
10235 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10236     /**
10237      * @cfg {String} triggerClass A CSS class to apply to the trigger
10238      */
10239      /**
10240      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10241      */
10242     hideTrigger:false,
10243
10244     /**
10245      * @cfg {Boolean} removable (true|false) special filter default false
10246      */
10247     removable : false,
10248     
10249     /** @cfg {Boolean} grow @hide */
10250     /** @cfg {Number} growMin @hide */
10251     /** @cfg {Number} growMax @hide */
10252
10253     /**
10254      * @hide 
10255      * @method
10256      */
10257     autoSize: Roo.emptyFn,
10258     // private
10259     monitorTab : true,
10260     // private
10261     deferHeight : true,
10262
10263     
10264     actionMode : 'wrap',
10265     
10266     caret : false,
10267     
10268     
10269     getAutoCreate : function(){
10270        
10271         var align = this.labelAlign || this.parentLabelAlign();
10272         
10273         var id = Roo.id();
10274         
10275         var cfg = {
10276             cls: 'form-group' //input-group
10277         };
10278         
10279         
10280         var input =  {
10281             tag: 'input',
10282             id : id,
10283             type : this.inputType,
10284             cls : 'form-control',
10285             autocomplete: 'new-password',
10286             placeholder : this.placeholder || '' 
10287             
10288         };
10289         if (this.name) {
10290             input.name = this.name;
10291         }
10292         if (this.size) {
10293             input.cls += ' input-' + this.size;
10294         }
10295         
10296         if (this.disabled) {
10297             input.disabled=true;
10298         }
10299         
10300         var inputblock = input;
10301         
10302         if(this.hasFeedback && !this.allowBlank){
10303             
10304             var feedback = {
10305                 tag: 'span',
10306                 cls: 'glyphicon form-control-feedback'
10307             };
10308             
10309             if(this.removable && !this.editable && !this.tickable){
10310                 inputblock = {
10311                     cls : 'has-feedback',
10312                     cn :  [
10313                         inputblock,
10314                         {
10315                             tag: 'button',
10316                             html : 'x',
10317                             cls : 'roo-combo-removable-btn close'
10318                         },
10319                         feedback
10320                     ] 
10321                 };
10322             } else {
10323                 inputblock = {
10324                     cls : 'has-feedback',
10325                     cn :  [
10326                         inputblock,
10327                         feedback
10328                     ] 
10329                 };
10330             }
10331
10332         } else {
10333             if(this.removable && !this.editable && !this.tickable){
10334                 inputblock = {
10335                     cls : 'roo-removable',
10336                     cn :  [
10337                         inputblock,
10338                         {
10339                             tag: 'button',
10340                             html : 'x',
10341                             cls : 'roo-combo-removable-btn close'
10342                         }
10343                     ] 
10344                 };
10345             }
10346         }
10347         
10348         if (this.before || this.after) {
10349             
10350             inputblock = {
10351                 cls : 'input-group',
10352                 cn :  [] 
10353             };
10354             if (this.before) {
10355                 inputblock.cn.push({
10356                     tag :'span',
10357                     cls : 'input-group-addon',
10358                     html : this.before
10359                 });
10360             }
10361             
10362             inputblock.cn.push(input);
10363             
10364             if(this.hasFeedback && !this.allowBlank){
10365                 inputblock.cls += ' has-feedback';
10366                 inputblock.cn.push(feedback);
10367             }
10368             
10369             if (this.after) {
10370                 inputblock.cn.push({
10371                     tag :'span',
10372                     cls : 'input-group-addon',
10373                     html : this.after
10374                 });
10375             }
10376             
10377         };
10378         
10379         var box = {
10380             tag: 'div',
10381             cn: [
10382                 {
10383                     tag: 'input',
10384                     type : 'hidden',
10385                     cls: 'form-hidden-field'
10386                 },
10387                 inputblock
10388             ]
10389             
10390         };
10391         
10392         if(this.multiple){
10393             box = {
10394                 tag: 'div',
10395                 cn: [
10396                     {
10397                         tag: 'input',
10398                         type : 'hidden',
10399                         cls: 'form-hidden-field'
10400                     },
10401                     {
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         
10419         var combobox = {
10420             cls: 'roo-select2-container input-group',
10421             cn: [
10422                 box
10423 //                {
10424 //                    tag: 'ul',
10425 //                    cls: 'typeahead typeahead-long dropdown-menu',
10426 //                    style: 'display:none'
10427 //                }
10428             ]
10429         };
10430         
10431         if(!this.multiple && this.showToggleBtn){
10432             
10433             var caret = {
10434                         tag: 'span',
10435                         cls: 'caret'
10436              };
10437             if (this.caret != false) {
10438                 caret = {
10439                      tag: 'i',
10440                      cls: 'fa fa-' + this.caret
10441                 };
10442                 
10443             }
10444             
10445             combobox.cn.push({
10446                 tag :'span',
10447                 cls : 'input-group-addon btn dropdown-toggle',
10448                 cn : [
10449                     caret,
10450                     {
10451                         tag: 'span',
10452                         cls: 'combobox-clear',
10453                         cn  : [
10454                             {
10455                                 tag : 'i',
10456                                 cls: 'icon-remove'
10457                             }
10458                         ]
10459                     }
10460                 ]
10461
10462             })
10463         }
10464         
10465         if(this.multiple){
10466             combobox.cls += ' roo-select2-container-multi';
10467         }
10468         
10469         if (align ==='left' && this.fieldLabel.length) {
10470             
10471             cfg.cls += ' roo-form-group-label-left';
10472
10473             cfg.cn = [
10474                 {
10475                     tag : 'i',
10476                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10477                     tooltip : 'This field is required'
10478                 },
10479                 {
10480                     tag: 'label',
10481                     'for' :  id,
10482                     cls : 'control-label',
10483                     html : this.fieldLabel
10484
10485                 },
10486                 {
10487                     cls : "", 
10488                     cn: [
10489                         combobox
10490                     ]
10491                 }
10492
10493             ];
10494             
10495             var labelCfg = cfg.cn[1];
10496             var contentCfg = cfg.cn[2];
10497             
10498             if(this.indicatorpos == 'right'){
10499                 cfg.cn = [
10500                     {
10501                         tag: 'label',
10502                         'for' :  id,
10503                         cls : 'control-label',
10504                         cn : [
10505                             {
10506                                 tag : 'span',
10507                                 html : this.fieldLabel
10508                             },
10509                             {
10510                                 tag : 'i',
10511                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10512                                 tooltip : 'This field is required'
10513                             }
10514                         ]
10515                     },
10516                     {
10517                         cls : "", 
10518                         cn: [
10519                             combobox
10520                         ]
10521                     }
10522
10523                 ];
10524                 
10525                 labelCfg = cfg.cn[0];
10526                 contentCfg = cfg.cn[1];
10527             }
10528             
10529             if(this.labelWidth > 12){
10530                 labelCfg.style = "width: " + this.labelWidth + 'px';
10531             }
10532             
10533             if(this.labelWidth < 13 && this.labelmd == 0){
10534                 this.labelmd = this.labelWidth;
10535             }
10536             
10537             if(this.labellg > 0){
10538                 labelCfg.cls += ' col-lg-' + this.labellg;
10539                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10540             }
10541             
10542             if(this.labelmd > 0){
10543                 labelCfg.cls += ' col-md-' + this.labelmd;
10544                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10545             }
10546             
10547             if(this.labelsm > 0){
10548                 labelCfg.cls += ' col-sm-' + this.labelsm;
10549                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10550             }
10551             
10552             if(this.labelxs > 0){
10553                 labelCfg.cls += ' col-xs-' + this.labelxs;
10554                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10555             }
10556             
10557         } else if ( this.fieldLabel.length) {
10558 //                Roo.log(" label");
10559             cfg.cn = [
10560                 {
10561                    tag : 'i',
10562                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10563                    tooltip : 'This field is required'
10564                },
10565                {
10566                    tag: 'label',
10567                    //cls : 'input-group-addon',
10568                    html : this.fieldLabel
10569
10570                },
10571
10572                combobox
10573
10574             ];
10575             
10576             if(this.indicatorpos == 'right'){
10577                 
10578                 cfg.cn = [
10579                     {
10580                        tag: 'label',
10581                        cn : [
10582                            {
10583                                tag : 'span',
10584                                html : this.fieldLabel
10585                            },
10586                            {
10587                               tag : 'i',
10588                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10589                               tooltip : 'This field is required'
10590                            }
10591                        ]
10592
10593                     },
10594                     combobox
10595
10596                 ];
10597
10598             }
10599
10600         } else {
10601             
10602 //                Roo.log(" no label && no align");
10603                 cfg = combobox
10604                      
10605                 
10606         }
10607         
10608         var settings=this;
10609         ['xs','sm','md','lg'].map(function(size){
10610             if (settings[size]) {
10611                 cfg.cls += ' col-' + size + '-' + settings[size];
10612             }
10613         });
10614         
10615         return cfg;
10616         
10617     },
10618     
10619     
10620     
10621     // private
10622     onResize : function(w, h){
10623 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10624 //        if(typeof w == 'number'){
10625 //            var x = w - this.trigger.getWidth();
10626 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10627 //            this.trigger.setStyle('left', x+'px');
10628 //        }
10629     },
10630
10631     // private
10632     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10633
10634     // private
10635     getResizeEl : function(){
10636         return this.inputEl();
10637     },
10638
10639     // private
10640     getPositionEl : function(){
10641         return this.inputEl();
10642     },
10643
10644     // private
10645     alignErrorIcon : function(){
10646         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10647     },
10648
10649     // private
10650     initEvents : function(){
10651         
10652         this.createList();
10653         
10654         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10655         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10656         if(!this.multiple && this.showToggleBtn){
10657             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10658             if(this.hideTrigger){
10659                 this.trigger.setDisplayed(false);
10660             }
10661             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10662         }
10663         
10664         if(this.multiple){
10665             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10666         }
10667         
10668         if(this.removable && !this.editable && !this.tickable){
10669             var close = this.closeTriggerEl();
10670             
10671             if(close){
10672                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10673                 close.on('click', this.removeBtnClick, this, close);
10674             }
10675         }
10676         
10677         //this.trigger.addClassOnOver('x-form-trigger-over');
10678         //this.trigger.addClassOnClick('x-form-trigger-click');
10679         
10680         //if(!this.width){
10681         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10682         //}
10683     },
10684     
10685     closeTriggerEl : function()
10686     {
10687         var close = this.el.select('.roo-combo-removable-btn', true).first();
10688         return close ? close : false;
10689     },
10690     
10691     removeBtnClick : function(e, h, el)
10692     {
10693         e.preventDefault();
10694         
10695         if(this.fireEvent("remove", this) !== false){
10696             this.reset();
10697             this.fireEvent("afterremove", this)
10698         }
10699     },
10700     
10701     createList : function()
10702     {
10703         this.list = Roo.get(document.body).createChild({
10704             tag: 'ul',
10705             cls: 'typeahead typeahead-long dropdown-menu',
10706             style: 'display:none'
10707         });
10708         
10709         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10710         
10711     },
10712
10713     // private
10714     initTrigger : function(){
10715        
10716     },
10717
10718     // private
10719     onDestroy : function(){
10720         if(this.trigger){
10721             this.trigger.removeAllListeners();
10722           //  this.trigger.remove();
10723         }
10724         //if(this.wrap){
10725         //    this.wrap.remove();
10726         //}
10727         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10728     },
10729
10730     // private
10731     onFocus : function(){
10732         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10733         /*
10734         if(!this.mimicing){
10735             this.wrap.addClass('x-trigger-wrap-focus');
10736             this.mimicing = true;
10737             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10738             if(this.monitorTab){
10739                 this.el.on("keydown", this.checkTab, this);
10740             }
10741         }
10742         */
10743     },
10744
10745     // private
10746     checkTab : function(e){
10747         if(e.getKey() == e.TAB){
10748             this.triggerBlur();
10749         }
10750     },
10751
10752     // private
10753     onBlur : function(){
10754         // do nothing
10755     },
10756
10757     // private
10758     mimicBlur : function(e, t){
10759         /*
10760         if(!this.wrap.contains(t) && this.validateBlur()){
10761             this.triggerBlur();
10762         }
10763         */
10764     },
10765
10766     // private
10767     triggerBlur : function(){
10768         this.mimicing = false;
10769         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10770         if(this.monitorTab){
10771             this.el.un("keydown", this.checkTab, this);
10772         }
10773         //this.wrap.removeClass('x-trigger-wrap-focus');
10774         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10775     },
10776
10777     // private
10778     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10779     validateBlur : function(e, t){
10780         return true;
10781     },
10782
10783     // private
10784     onDisable : function(){
10785         this.inputEl().dom.disabled = true;
10786         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10787         //if(this.wrap){
10788         //    this.wrap.addClass('x-item-disabled');
10789         //}
10790     },
10791
10792     // private
10793     onEnable : function(){
10794         this.inputEl().dom.disabled = false;
10795         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10796         //if(this.wrap){
10797         //    this.el.removeClass('x-item-disabled');
10798         //}
10799     },
10800
10801     // private
10802     onShow : function(){
10803         var ae = this.getActionEl();
10804         
10805         if(ae){
10806             ae.dom.style.display = '';
10807             ae.dom.style.visibility = 'visible';
10808         }
10809     },
10810
10811     // private
10812     
10813     onHide : function(){
10814         var ae = this.getActionEl();
10815         ae.dom.style.display = 'none';
10816     },
10817
10818     /**
10819      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10820      * by an implementing function.
10821      * @method
10822      * @param {EventObject} e
10823      */
10824     onTriggerClick : Roo.emptyFn
10825 });
10826  /*
10827  * Based on:
10828  * Ext JS Library 1.1.1
10829  * Copyright(c) 2006-2007, Ext JS, LLC.
10830  *
10831  * Originally Released Under LGPL - original licence link has changed is not relivant.
10832  *
10833  * Fork - LGPL
10834  * <script type="text/javascript">
10835  */
10836
10837
10838 /**
10839  * @class Roo.data.SortTypes
10840  * @singleton
10841  * Defines the default sorting (casting?) comparison functions used when sorting data.
10842  */
10843 Roo.data.SortTypes = {
10844     /**
10845      * Default sort that does nothing
10846      * @param {Mixed} s The value being converted
10847      * @return {Mixed} The comparison value
10848      */
10849     none : function(s){
10850         return s;
10851     },
10852     
10853     /**
10854      * The regular expression used to strip tags
10855      * @type {RegExp}
10856      * @property
10857      */
10858     stripTagsRE : /<\/?[^>]+>/gi,
10859     
10860     /**
10861      * Strips all HTML tags to sort on text only
10862      * @param {Mixed} s The value being converted
10863      * @return {String} The comparison value
10864      */
10865     asText : function(s){
10866         return String(s).replace(this.stripTagsRE, "");
10867     },
10868     
10869     /**
10870      * Strips all HTML tags to sort on text only - Case insensitive
10871      * @param {Mixed} s The value being converted
10872      * @return {String} The comparison value
10873      */
10874     asUCText : function(s){
10875         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10876     },
10877     
10878     /**
10879      * Case insensitive string
10880      * @param {Mixed} s The value being converted
10881      * @return {String} The comparison value
10882      */
10883     asUCString : function(s) {
10884         return String(s).toUpperCase();
10885     },
10886     
10887     /**
10888      * Date sorting
10889      * @param {Mixed} s The value being converted
10890      * @return {Number} The comparison value
10891      */
10892     asDate : function(s) {
10893         if(!s){
10894             return 0;
10895         }
10896         if(s instanceof Date){
10897             return s.getTime();
10898         }
10899         return Date.parse(String(s));
10900     },
10901     
10902     /**
10903      * Float sorting
10904      * @param {Mixed} s The value being converted
10905      * @return {Float} The comparison value
10906      */
10907     asFloat : function(s) {
10908         var val = parseFloat(String(s).replace(/,/g, ""));
10909         if(isNaN(val)) {
10910             val = 0;
10911         }
10912         return val;
10913     },
10914     
10915     /**
10916      * Integer sorting
10917      * @param {Mixed} s The value being converted
10918      * @return {Number} The comparison value
10919      */
10920     asInt : function(s) {
10921         var val = parseInt(String(s).replace(/,/g, ""));
10922         if(isNaN(val)) {
10923             val = 0;
10924         }
10925         return val;
10926     }
10927 };/*
10928  * Based on:
10929  * Ext JS Library 1.1.1
10930  * Copyright(c) 2006-2007, Ext JS, LLC.
10931  *
10932  * Originally Released Under LGPL - original licence link has changed is not relivant.
10933  *
10934  * Fork - LGPL
10935  * <script type="text/javascript">
10936  */
10937
10938 /**
10939 * @class Roo.data.Record
10940  * Instances of this class encapsulate both record <em>definition</em> information, and record
10941  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10942  * to access Records cached in an {@link Roo.data.Store} object.<br>
10943  * <p>
10944  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10945  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10946  * objects.<br>
10947  * <p>
10948  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10949  * @constructor
10950  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10951  * {@link #create}. The parameters are the same.
10952  * @param {Array} data An associative Array of data values keyed by the field name.
10953  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10954  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10955  * not specified an integer id is generated.
10956  */
10957 Roo.data.Record = function(data, id){
10958     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10959     this.data = data;
10960 };
10961
10962 /**
10963  * Generate a constructor for a specific record layout.
10964  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10965  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10966  * Each field definition object may contain the following properties: <ul>
10967  * <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,
10968  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10969  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10970  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10971  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10972  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10973  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10974  * this may be omitted.</p></li>
10975  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10976  * <ul><li>auto (Default, implies no conversion)</li>
10977  * <li>string</li>
10978  * <li>int</li>
10979  * <li>float</li>
10980  * <li>boolean</li>
10981  * <li>date</li></ul></p></li>
10982  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10983  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10984  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10985  * by the Reader into an object that will be stored in the Record. It is passed the
10986  * following parameters:<ul>
10987  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10988  * </ul></p></li>
10989  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10990  * </ul>
10991  * <br>usage:<br><pre><code>
10992 var TopicRecord = Roo.data.Record.create(
10993     {name: 'title', mapping: 'topic_title'},
10994     {name: 'author', mapping: 'username'},
10995     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10996     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10997     {name: 'lastPoster', mapping: 'user2'},
10998     {name: 'excerpt', mapping: 'post_text'}
10999 );
11000
11001 var myNewRecord = new TopicRecord({
11002     title: 'Do my job please',
11003     author: 'noobie',
11004     totalPosts: 1,
11005     lastPost: new Date(),
11006     lastPoster: 'Animal',
11007     excerpt: 'No way dude!'
11008 });
11009 myStore.add(myNewRecord);
11010 </code></pre>
11011  * @method create
11012  * @static
11013  */
11014 Roo.data.Record.create = function(o){
11015     var f = function(){
11016         f.superclass.constructor.apply(this, arguments);
11017     };
11018     Roo.extend(f, Roo.data.Record);
11019     var p = f.prototype;
11020     p.fields = new Roo.util.MixedCollection(false, function(field){
11021         return field.name;
11022     });
11023     for(var i = 0, len = o.length; i < len; i++){
11024         p.fields.add(new Roo.data.Field(o[i]));
11025     }
11026     f.getField = function(name){
11027         return p.fields.get(name);  
11028     };
11029     return f;
11030 };
11031
11032 Roo.data.Record.AUTO_ID = 1000;
11033 Roo.data.Record.EDIT = 'edit';
11034 Roo.data.Record.REJECT = 'reject';
11035 Roo.data.Record.COMMIT = 'commit';
11036
11037 Roo.data.Record.prototype = {
11038     /**
11039      * Readonly flag - true if this record has been modified.
11040      * @type Boolean
11041      */
11042     dirty : false,
11043     editing : false,
11044     error: null,
11045     modified: null,
11046
11047     // private
11048     join : function(store){
11049         this.store = store;
11050     },
11051
11052     /**
11053      * Set the named field to the specified value.
11054      * @param {String} name The name of the field to set.
11055      * @param {Object} value The value to set the field to.
11056      */
11057     set : function(name, value){
11058         if(this.data[name] == value){
11059             return;
11060         }
11061         this.dirty = true;
11062         if(!this.modified){
11063             this.modified = {};
11064         }
11065         if(typeof this.modified[name] == 'undefined'){
11066             this.modified[name] = this.data[name];
11067         }
11068         this.data[name] = value;
11069         if(!this.editing && this.store){
11070             this.store.afterEdit(this);
11071         }       
11072     },
11073
11074     /**
11075      * Get the value of the named field.
11076      * @param {String} name The name of the field to get the value of.
11077      * @return {Object} The value of the field.
11078      */
11079     get : function(name){
11080         return this.data[name]; 
11081     },
11082
11083     // private
11084     beginEdit : function(){
11085         this.editing = true;
11086         this.modified = {}; 
11087     },
11088
11089     // private
11090     cancelEdit : function(){
11091         this.editing = false;
11092         delete this.modified;
11093     },
11094
11095     // private
11096     endEdit : function(){
11097         this.editing = false;
11098         if(this.dirty && this.store){
11099             this.store.afterEdit(this);
11100         }
11101     },
11102
11103     /**
11104      * Usually called by the {@link Roo.data.Store} which owns the Record.
11105      * Rejects all changes made to the Record since either creation, or the last commit operation.
11106      * Modified fields are reverted to their original values.
11107      * <p>
11108      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11109      * of reject operations.
11110      */
11111     reject : function(){
11112         var m = this.modified;
11113         for(var n in m){
11114             if(typeof m[n] != "function"){
11115                 this.data[n] = m[n];
11116             }
11117         }
11118         this.dirty = false;
11119         delete this.modified;
11120         this.editing = false;
11121         if(this.store){
11122             this.store.afterReject(this);
11123         }
11124     },
11125
11126     /**
11127      * Usually called by the {@link Roo.data.Store} which owns the Record.
11128      * Commits all changes made to the Record since either creation, or the last commit operation.
11129      * <p>
11130      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11131      * of commit operations.
11132      */
11133     commit : function(){
11134         this.dirty = false;
11135         delete this.modified;
11136         this.editing = false;
11137         if(this.store){
11138             this.store.afterCommit(this);
11139         }
11140     },
11141
11142     // private
11143     hasError : function(){
11144         return this.error != null;
11145     },
11146
11147     // private
11148     clearError : function(){
11149         this.error = null;
11150     },
11151
11152     /**
11153      * Creates a copy of this record.
11154      * @param {String} id (optional) A new record id if you don't want to use this record's id
11155      * @return {Record}
11156      */
11157     copy : function(newId) {
11158         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11159     }
11160 };/*
11161  * Based on:
11162  * Ext JS Library 1.1.1
11163  * Copyright(c) 2006-2007, Ext JS, LLC.
11164  *
11165  * Originally Released Under LGPL - original licence link has changed is not relivant.
11166  *
11167  * Fork - LGPL
11168  * <script type="text/javascript">
11169  */
11170
11171
11172
11173 /**
11174  * @class Roo.data.Store
11175  * @extends Roo.util.Observable
11176  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11177  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11178  * <p>
11179  * 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
11180  * has no knowledge of the format of the data returned by the Proxy.<br>
11181  * <p>
11182  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11183  * instances from the data object. These records are cached and made available through accessor functions.
11184  * @constructor
11185  * Creates a new Store.
11186  * @param {Object} config A config object containing the objects needed for the Store to access data,
11187  * and read the data into Records.
11188  */
11189 Roo.data.Store = function(config){
11190     this.data = new Roo.util.MixedCollection(false);
11191     this.data.getKey = function(o){
11192         return o.id;
11193     };
11194     this.baseParams = {};
11195     // private
11196     this.paramNames = {
11197         "start" : "start",
11198         "limit" : "limit",
11199         "sort" : "sort",
11200         "dir" : "dir",
11201         "multisort" : "_multisort"
11202     };
11203
11204     if(config && config.data){
11205         this.inlineData = config.data;
11206         delete config.data;
11207     }
11208
11209     Roo.apply(this, config);
11210     
11211     if(this.reader){ // reader passed
11212         this.reader = Roo.factory(this.reader, Roo.data);
11213         this.reader.xmodule = this.xmodule || false;
11214         if(!this.recordType){
11215             this.recordType = this.reader.recordType;
11216         }
11217         if(this.reader.onMetaChange){
11218             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11219         }
11220     }
11221
11222     if(this.recordType){
11223         this.fields = this.recordType.prototype.fields;
11224     }
11225     this.modified = [];
11226
11227     this.addEvents({
11228         /**
11229          * @event datachanged
11230          * Fires when the data cache has changed, and a widget which is using this Store
11231          * as a Record cache should refresh its view.
11232          * @param {Store} this
11233          */
11234         datachanged : true,
11235         /**
11236          * @event metachange
11237          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11238          * @param {Store} this
11239          * @param {Object} meta The JSON metadata
11240          */
11241         metachange : true,
11242         /**
11243          * @event add
11244          * Fires when Records have been added to the Store
11245          * @param {Store} this
11246          * @param {Roo.data.Record[]} records The array of Records added
11247          * @param {Number} index The index at which the record(s) were added
11248          */
11249         add : true,
11250         /**
11251          * @event remove
11252          * Fires when a Record has been removed from the Store
11253          * @param {Store} this
11254          * @param {Roo.data.Record} record The Record that was removed
11255          * @param {Number} index The index at which the record was removed
11256          */
11257         remove : true,
11258         /**
11259          * @event update
11260          * Fires when a Record has been updated
11261          * @param {Store} this
11262          * @param {Roo.data.Record} record The Record that was updated
11263          * @param {String} operation The update operation being performed.  Value may be one of:
11264          * <pre><code>
11265  Roo.data.Record.EDIT
11266  Roo.data.Record.REJECT
11267  Roo.data.Record.COMMIT
11268          * </code></pre>
11269          */
11270         update : true,
11271         /**
11272          * @event clear
11273          * Fires when the data cache has been cleared.
11274          * @param {Store} this
11275          */
11276         clear : true,
11277         /**
11278          * @event beforeload
11279          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11280          * the load action will be canceled.
11281          * @param {Store} this
11282          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11283          */
11284         beforeload : true,
11285         /**
11286          * @event beforeloadadd
11287          * Fires after a new set of Records has been loaded.
11288          * @param {Store} this
11289          * @param {Roo.data.Record[]} records The Records that were loaded
11290          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11291          */
11292         beforeloadadd : true,
11293         /**
11294          * @event load
11295          * Fires after a new set of Records has been loaded, before they are added to the store.
11296          * @param {Store} this
11297          * @param {Roo.data.Record[]} records The Records that were loaded
11298          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11299          * @params {Object} return from reader
11300          */
11301         load : true,
11302         /**
11303          * @event loadexception
11304          * Fires if an exception occurs in the Proxy during loading.
11305          * Called with the signature of the Proxy's "loadexception" event.
11306          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11307          * 
11308          * @param {Proxy} 
11309          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11310          * @param {Object} load options 
11311          * @param {Object} jsonData from your request (normally this contains the Exception)
11312          */
11313         loadexception : true
11314     });
11315     
11316     if(this.proxy){
11317         this.proxy = Roo.factory(this.proxy, Roo.data);
11318         this.proxy.xmodule = this.xmodule || false;
11319         this.relayEvents(this.proxy,  ["loadexception"]);
11320     }
11321     this.sortToggle = {};
11322     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11323
11324     Roo.data.Store.superclass.constructor.call(this);
11325
11326     if(this.inlineData){
11327         this.loadData(this.inlineData);
11328         delete this.inlineData;
11329     }
11330 };
11331
11332 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11333      /**
11334     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11335     * without a remote query - used by combo/forms at present.
11336     */
11337     
11338     /**
11339     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11340     */
11341     /**
11342     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11343     */
11344     /**
11345     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11346     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11347     */
11348     /**
11349     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11350     * on any HTTP request
11351     */
11352     /**
11353     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11354     */
11355     /**
11356     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11357     */
11358     multiSort: false,
11359     /**
11360     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11361     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11362     */
11363     remoteSort : false,
11364
11365     /**
11366     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11367      * loaded or when a record is removed. (defaults to false).
11368     */
11369     pruneModifiedRecords : false,
11370
11371     // private
11372     lastOptions : null,
11373
11374     /**
11375      * Add Records to the Store and fires the add event.
11376      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11377      */
11378     add : function(records){
11379         records = [].concat(records);
11380         for(var i = 0, len = records.length; i < len; i++){
11381             records[i].join(this);
11382         }
11383         var index = this.data.length;
11384         this.data.addAll(records);
11385         this.fireEvent("add", this, records, index);
11386     },
11387
11388     /**
11389      * Remove a Record from the Store and fires the remove event.
11390      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11391      */
11392     remove : function(record){
11393         var index = this.data.indexOf(record);
11394         this.data.removeAt(index);
11395  
11396         if(this.pruneModifiedRecords){
11397             this.modified.remove(record);
11398         }
11399         this.fireEvent("remove", this, record, index);
11400     },
11401
11402     /**
11403      * Remove all Records from the Store and fires the clear event.
11404      */
11405     removeAll : function(){
11406         this.data.clear();
11407         if(this.pruneModifiedRecords){
11408             this.modified = [];
11409         }
11410         this.fireEvent("clear", this);
11411     },
11412
11413     /**
11414      * Inserts Records to the Store at the given index and fires the add event.
11415      * @param {Number} index The start index at which to insert the passed Records.
11416      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11417      */
11418     insert : function(index, records){
11419         records = [].concat(records);
11420         for(var i = 0, len = records.length; i < len; i++){
11421             this.data.insert(index, records[i]);
11422             records[i].join(this);
11423         }
11424         this.fireEvent("add", this, records, index);
11425     },
11426
11427     /**
11428      * Get the index within the cache of the passed Record.
11429      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11430      * @return {Number} The index of the passed Record. Returns -1 if not found.
11431      */
11432     indexOf : function(record){
11433         return this.data.indexOf(record);
11434     },
11435
11436     /**
11437      * Get the index within the cache of the Record with the passed id.
11438      * @param {String} id The id of the Record to find.
11439      * @return {Number} The index of the Record. Returns -1 if not found.
11440      */
11441     indexOfId : function(id){
11442         return this.data.indexOfKey(id);
11443     },
11444
11445     /**
11446      * Get the Record with the specified id.
11447      * @param {String} id The id of the Record to find.
11448      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11449      */
11450     getById : function(id){
11451         return this.data.key(id);
11452     },
11453
11454     /**
11455      * Get the Record at the specified index.
11456      * @param {Number} index The index of the Record to find.
11457      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11458      */
11459     getAt : function(index){
11460         return this.data.itemAt(index);
11461     },
11462
11463     /**
11464      * Returns a range of Records between specified indices.
11465      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11466      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11467      * @return {Roo.data.Record[]} An array of Records
11468      */
11469     getRange : function(start, end){
11470         return this.data.getRange(start, end);
11471     },
11472
11473     // private
11474     storeOptions : function(o){
11475         o = Roo.apply({}, o);
11476         delete o.callback;
11477         delete o.scope;
11478         this.lastOptions = o;
11479     },
11480
11481     /**
11482      * Loads the Record cache from the configured Proxy using the configured Reader.
11483      * <p>
11484      * If using remote paging, then the first load call must specify the <em>start</em>
11485      * and <em>limit</em> properties in the options.params property to establish the initial
11486      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11487      * <p>
11488      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11489      * and this call will return before the new data has been loaded. Perform any post-processing
11490      * in a callback function, or in a "load" event handler.</strong>
11491      * <p>
11492      * @param {Object} options An object containing properties which control loading options:<ul>
11493      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11494      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11495      * passed the following arguments:<ul>
11496      * <li>r : Roo.data.Record[]</li>
11497      * <li>options: Options object from the load call</li>
11498      * <li>success: Boolean success indicator</li></ul></li>
11499      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11500      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11501      * </ul>
11502      */
11503     load : function(options){
11504         options = options || {};
11505         if(this.fireEvent("beforeload", this, options) !== false){
11506             this.storeOptions(options);
11507             var p = Roo.apply(options.params || {}, this.baseParams);
11508             // if meta was not loaded from remote source.. try requesting it.
11509             if (!this.reader.metaFromRemote) {
11510                 p._requestMeta = 1;
11511             }
11512             if(this.sortInfo && this.remoteSort){
11513                 var pn = this.paramNames;
11514                 p[pn["sort"]] = this.sortInfo.field;
11515                 p[pn["dir"]] = this.sortInfo.direction;
11516             }
11517             if (this.multiSort) {
11518                 var pn = this.paramNames;
11519                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11520             }
11521             
11522             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11523         }
11524     },
11525
11526     /**
11527      * Reloads the Record cache from the configured Proxy using the configured Reader and
11528      * the options from the last load operation performed.
11529      * @param {Object} options (optional) An object containing properties which may override the options
11530      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11531      * the most recently used options are reused).
11532      */
11533     reload : function(options){
11534         this.load(Roo.applyIf(options||{}, this.lastOptions));
11535     },
11536
11537     // private
11538     // Called as a callback by the Reader during a load operation.
11539     loadRecords : function(o, options, success){
11540         if(!o || success === false){
11541             if(success !== false){
11542                 this.fireEvent("load", this, [], options, o);
11543             }
11544             if(options.callback){
11545                 options.callback.call(options.scope || this, [], options, false);
11546             }
11547             return;
11548         }
11549         // if data returned failure - throw an exception.
11550         if (o.success === false) {
11551             // show a message if no listener is registered.
11552             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11553                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11554             }
11555             // loadmask wil be hooked into this..
11556             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11557             return;
11558         }
11559         var r = o.records, t = o.totalRecords || r.length;
11560         
11561         this.fireEvent("beforeloadadd", this, r, options, o);
11562         
11563         if(!options || options.add !== true){
11564             if(this.pruneModifiedRecords){
11565                 this.modified = [];
11566             }
11567             for(var i = 0, len = r.length; i < len; i++){
11568                 r[i].join(this);
11569             }
11570             if(this.snapshot){
11571                 this.data = this.snapshot;
11572                 delete this.snapshot;
11573             }
11574             this.data.clear();
11575             this.data.addAll(r);
11576             this.totalLength = t;
11577             this.applySort();
11578             this.fireEvent("datachanged", this);
11579         }else{
11580             this.totalLength = Math.max(t, this.data.length+r.length);
11581             this.add(r);
11582         }
11583         
11584         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11585                 
11586             var e = new Roo.data.Record({});
11587
11588             e.set(this.parent.displayField, this.parent.emptyTitle);
11589             e.set(this.parent.valueField, '');
11590
11591             this.insert(0, e);
11592         }
11593             
11594         this.fireEvent("load", this, r, options, o);
11595         if(options.callback){
11596             options.callback.call(options.scope || this, r, options, true);
11597         }
11598     },
11599
11600
11601     /**
11602      * Loads data from a passed data block. A Reader which understands the format of the data
11603      * must have been configured in the constructor.
11604      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11605      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11606      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11607      */
11608     loadData : function(o, append){
11609         var r = this.reader.readRecords(o);
11610         this.loadRecords(r, {add: append}, true);
11611     },
11612
11613     /**
11614      * Gets the number of cached records.
11615      * <p>
11616      * <em>If using paging, this may not be the total size of the dataset. If the data object
11617      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11618      * the data set size</em>
11619      */
11620     getCount : function(){
11621         return this.data.length || 0;
11622     },
11623
11624     /**
11625      * Gets the total number of records in the dataset as returned by the server.
11626      * <p>
11627      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11628      * the dataset size</em>
11629      */
11630     getTotalCount : function(){
11631         return this.totalLength || 0;
11632     },
11633
11634     /**
11635      * Returns the sort state of the Store as an object with two properties:
11636      * <pre><code>
11637  field {String} The name of the field by which the Records are sorted
11638  direction {String} The sort order, "ASC" or "DESC"
11639      * </code></pre>
11640      */
11641     getSortState : function(){
11642         return this.sortInfo;
11643     },
11644
11645     // private
11646     applySort : function(){
11647         if(this.sortInfo && !this.remoteSort){
11648             var s = this.sortInfo, f = s.field;
11649             var st = this.fields.get(f).sortType;
11650             var fn = function(r1, r2){
11651                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11652                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11653             };
11654             this.data.sort(s.direction, fn);
11655             if(this.snapshot && this.snapshot != this.data){
11656                 this.snapshot.sort(s.direction, fn);
11657             }
11658         }
11659     },
11660
11661     /**
11662      * Sets the default sort column and order to be used by the next load operation.
11663      * @param {String} fieldName The name of the field to sort by.
11664      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11665      */
11666     setDefaultSort : function(field, dir){
11667         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11668     },
11669
11670     /**
11671      * Sort the Records.
11672      * If remote sorting is used, the sort is performed on the server, and the cache is
11673      * reloaded. If local sorting is used, the cache is sorted internally.
11674      * @param {String} fieldName The name of the field to sort by.
11675      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11676      */
11677     sort : function(fieldName, dir){
11678         var f = this.fields.get(fieldName);
11679         if(!dir){
11680             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11681             
11682             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11683                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11684             }else{
11685                 dir = f.sortDir;
11686             }
11687         }
11688         this.sortToggle[f.name] = dir;
11689         this.sortInfo = {field: f.name, direction: dir};
11690         if(!this.remoteSort){
11691             this.applySort();
11692             this.fireEvent("datachanged", this);
11693         }else{
11694             this.load(this.lastOptions);
11695         }
11696     },
11697
11698     /**
11699      * Calls the specified function for each of the Records in the cache.
11700      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11701      * Returning <em>false</em> aborts and exits the iteration.
11702      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11703      */
11704     each : function(fn, scope){
11705         this.data.each(fn, scope);
11706     },
11707
11708     /**
11709      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11710      * (e.g., during paging).
11711      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11712      */
11713     getModifiedRecords : function(){
11714         return this.modified;
11715     },
11716
11717     // private
11718     createFilterFn : function(property, value, anyMatch){
11719         if(!value.exec){ // not a regex
11720             value = String(value);
11721             if(value.length == 0){
11722                 return false;
11723             }
11724             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11725         }
11726         return function(r){
11727             return value.test(r.data[property]);
11728         };
11729     },
11730
11731     /**
11732      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11733      * @param {String} property A field on your records
11734      * @param {Number} start The record index to start at (defaults to 0)
11735      * @param {Number} end The last record index to include (defaults to length - 1)
11736      * @return {Number} The sum
11737      */
11738     sum : function(property, start, end){
11739         var rs = this.data.items, v = 0;
11740         start = start || 0;
11741         end = (end || end === 0) ? end : rs.length-1;
11742
11743         for(var i = start; i <= end; i++){
11744             v += (rs[i].data[property] || 0);
11745         }
11746         return v;
11747     },
11748
11749     /**
11750      * Filter the records by a specified property.
11751      * @param {String} field A field on your records
11752      * @param {String/RegExp} value Either a string that the field
11753      * should start with or a RegExp to test against the field
11754      * @param {Boolean} anyMatch True to match any part not just the beginning
11755      */
11756     filter : function(property, value, anyMatch){
11757         var fn = this.createFilterFn(property, value, anyMatch);
11758         return fn ? this.filterBy(fn) : this.clearFilter();
11759     },
11760
11761     /**
11762      * Filter by a function. The specified function will be called with each
11763      * record in this data source. If the function returns true the record is included,
11764      * otherwise it is filtered.
11765      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11766      * @param {Object} scope (optional) The scope of the function (defaults to this)
11767      */
11768     filterBy : function(fn, scope){
11769         this.snapshot = this.snapshot || this.data;
11770         this.data = this.queryBy(fn, scope||this);
11771         this.fireEvent("datachanged", this);
11772     },
11773
11774     /**
11775      * Query the records by a specified property.
11776      * @param {String} field A field on your records
11777      * @param {String/RegExp} value Either a string that the field
11778      * should start with or a RegExp to test against the field
11779      * @param {Boolean} anyMatch True to match any part not just the beginning
11780      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11781      */
11782     query : function(property, value, anyMatch){
11783         var fn = this.createFilterFn(property, value, anyMatch);
11784         return fn ? this.queryBy(fn) : this.data.clone();
11785     },
11786
11787     /**
11788      * Query by a function. The specified function will be called with each
11789      * record in this data source. If the function returns true the record is included
11790      * in the results.
11791      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11792      * @param {Object} scope (optional) The scope of the function (defaults to this)
11793       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11794      **/
11795     queryBy : function(fn, scope){
11796         var data = this.snapshot || this.data;
11797         return data.filterBy(fn, scope||this);
11798     },
11799
11800     /**
11801      * Collects unique values for a particular dataIndex from this store.
11802      * @param {String} dataIndex The property to collect
11803      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11804      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11805      * @return {Array} An array of the unique values
11806      **/
11807     collect : function(dataIndex, allowNull, bypassFilter){
11808         var d = (bypassFilter === true && this.snapshot) ?
11809                 this.snapshot.items : this.data.items;
11810         var v, sv, r = [], l = {};
11811         for(var i = 0, len = d.length; i < len; i++){
11812             v = d[i].data[dataIndex];
11813             sv = String(v);
11814             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11815                 l[sv] = true;
11816                 r[r.length] = v;
11817             }
11818         }
11819         return r;
11820     },
11821
11822     /**
11823      * Revert to a view of the Record cache with no filtering applied.
11824      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11825      */
11826     clearFilter : function(suppressEvent){
11827         if(this.snapshot && this.snapshot != this.data){
11828             this.data = this.snapshot;
11829             delete this.snapshot;
11830             if(suppressEvent !== true){
11831                 this.fireEvent("datachanged", this);
11832             }
11833         }
11834     },
11835
11836     // private
11837     afterEdit : function(record){
11838         if(this.modified.indexOf(record) == -1){
11839             this.modified.push(record);
11840         }
11841         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11842     },
11843     
11844     // private
11845     afterReject : function(record){
11846         this.modified.remove(record);
11847         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11848     },
11849
11850     // private
11851     afterCommit : function(record){
11852         this.modified.remove(record);
11853         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11854     },
11855
11856     /**
11857      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11858      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11859      */
11860     commitChanges : function(){
11861         var m = this.modified.slice(0);
11862         this.modified = [];
11863         for(var i = 0, len = m.length; i < len; i++){
11864             m[i].commit();
11865         }
11866     },
11867
11868     /**
11869      * Cancel outstanding changes on all changed records.
11870      */
11871     rejectChanges : function(){
11872         var m = this.modified.slice(0);
11873         this.modified = [];
11874         for(var i = 0, len = m.length; i < len; i++){
11875             m[i].reject();
11876         }
11877     },
11878
11879     onMetaChange : function(meta, rtype, o){
11880         this.recordType = rtype;
11881         this.fields = rtype.prototype.fields;
11882         delete this.snapshot;
11883         this.sortInfo = meta.sortInfo || this.sortInfo;
11884         this.modified = [];
11885         this.fireEvent('metachange', this, this.reader.meta);
11886     },
11887     
11888     moveIndex : function(data, type)
11889     {
11890         var index = this.indexOf(data);
11891         
11892         var newIndex = index + type;
11893         
11894         this.remove(data);
11895         
11896         this.insert(newIndex, data);
11897         
11898     }
11899 });/*
11900  * Based on:
11901  * Ext JS Library 1.1.1
11902  * Copyright(c) 2006-2007, Ext JS, LLC.
11903  *
11904  * Originally Released Under LGPL - original licence link has changed is not relivant.
11905  *
11906  * Fork - LGPL
11907  * <script type="text/javascript">
11908  */
11909
11910 /**
11911  * @class Roo.data.SimpleStore
11912  * @extends Roo.data.Store
11913  * Small helper class to make creating Stores from Array data easier.
11914  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11915  * @cfg {Array} fields An array of field definition objects, or field name strings.
11916  * @cfg {Array} data The multi-dimensional array of data
11917  * @constructor
11918  * @param {Object} config
11919  */
11920 Roo.data.SimpleStore = function(config){
11921     Roo.data.SimpleStore.superclass.constructor.call(this, {
11922         isLocal : true,
11923         reader: new Roo.data.ArrayReader({
11924                 id: config.id
11925             },
11926             Roo.data.Record.create(config.fields)
11927         ),
11928         proxy : new Roo.data.MemoryProxy(config.data)
11929     });
11930     this.load();
11931 };
11932 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11933  * Based on:
11934  * Ext JS Library 1.1.1
11935  * Copyright(c) 2006-2007, Ext JS, LLC.
11936  *
11937  * Originally Released Under LGPL - original licence link has changed is not relivant.
11938  *
11939  * Fork - LGPL
11940  * <script type="text/javascript">
11941  */
11942
11943 /**
11944 /**
11945  * @extends Roo.data.Store
11946  * @class Roo.data.JsonStore
11947  * Small helper class to make creating Stores for JSON data easier. <br/>
11948 <pre><code>
11949 var store = new Roo.data.JsonStore({
11950     url: 'get-images.php',
11951     root: 'images',
11952     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11953 });
11954 </code></pre>
11955  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11956  * JsonReader and HttpProxy (unless inline data is provided).</b>
11957  * @cfg {Array} fields An array of field definition objects, or field name strings.
11958  * @constructor
11959  * @param {Object} config
11960  */
11961 Roo.data.JsonStore = function(c){
11962     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11963         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11964         reader: new Roo.data.JsonReader(c, c.fields)
11965     }));
11966 };
11967 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11968  * Based on:
11969  * Ext JS Library 1.1.1
11970  * Copyright(c) 2006-2007, Ext JS, LLC.
11971  *
11972  * Originally Released Under LGPL - original licence link has changed is not relivant.
11973  *
11974  * Fork - LGPL
11975  * <script type="text/javascript">
11976  */
11977
11978  
11979 Roo.data.Field = function(config){
11980     if(typeof config == "string"){
11981         config = {name: config};
11982     }
11983     Roo.apply(this, config);
11984     
11985     if(!this.type){
11986         this.type = "auto";
11987     }
11988     
11989     var st = Roo.data.SortTypes;
11990     // named sortTypes are supported, here we look them up
11991     if(typeof this.sortType == "string"){
11992         this.sortType = st[this.sortType];
11993     }
11994     
11995     // set default sortType for strings and dates
11996     if(!this.sortType){
11997         switch(this.type){
11998             case "string":
11999                 this.sortType = st.asUCString;
12000                 break;
12001             case "date":
12002                 this.sortType = st.asDate;
12003                 break;
12004             default:
12005                 this.sortType = st.none;
12006         }
12007     }
12008
12009     // define once
12010     var stripRe = /[\$,%]/g;
12011
12012     // prebuilt conversion function for this field, instead of
12013     // switching every time we're reading a value
12014     if(!this.convert){
12015         var cv, dateFormat = this.dateFormat;
12016         switch(this.type){
12017             case "":
12018             case "auto":
12019             case undefined:
12020                 cv = function(v){ return v; };
12021                 break;
12022             case "string":
12023                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12024                 break;
12025             case "int":
12026                 cv = function(v){
12027                     return v !== undefined && v !== null && v !== '' ?
12028                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12029                     };
12030                 break;
12031             case "float":
12032                 cv = function(v){
12033                     return v !== undefined && v !== null && v !== '' ?
12034                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12035                     };
12036                 break;
12037             case "bool":
12038             case "boolean":
12039                 cv = function(v){ return v === true || v === "true" || v == 1; };
12040                 break;
12041             case "date":
12042                 cv = function(v){
12043                     if(!v){
12044                         return '';
12045                     }
12046                     if(v instanceof Date){
12047                         return v;
12048                     }
12049                     if(dateFormat){
12050                         if(dateFormat == "timestamp"){
12051                             return new Date(v*1000);
12052                         }
12053                         return Date.parseDate(v, dateFormat);
12054                     }
12055                     var parsed = Date.parse(v);
12056                     return parsed ? new Date(parsed) : null;
12057                 };
12058              break;
12059             
12060         }
12061         this.convert = cv;
12062     }
12063 };
12064
12065 Roo.data.Field.prototype = {
12066     dateFormat: null,
12067     defaultValue: "",
12068     mapping: null,
12069     sortType : null,
12070     sortDir : "ASC"
12071 };/*
12072  * Based on:
12073  * Ext JS Library 1.1.1
12074  * Copyright(c) 2006-2007, Ext JS, LLC.
12075  *
12076  * Originally Released Under LGPL - original licence link has changed is not relivant.
12077  *
12078  * Fork - LGPL
12079  * <script type="text/javascript">
12080  */
12081  
12082 // Base class for reading structured data from a data source.  This class is intended to be
12083 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12084
12085 /**
12086  * @class Roo.data.DataReader
12087  * Base class for reading structured data from a data source.  This class is intended to be
12088  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12089  */
12090
12091 Roo.data.DataReader = function(meta, recordType){
12092     
12093     this.meta = meta;
12094     
12095     this.recordType = recordType instanceof Array ? 
12096         Roo.data.Record.create(recordType) : recordType;
12097 };
12098
12099 Roo.data.DataReader.prototype = {
12100      /**
12101      * Create an empty record
12102      * @param {Object} data (optional) - overlay some values
12103      * @return {Roo.data.Record} record created.
12104      */
12105     newRow :  function(d) {
12106         var da =  {};
12107         this.recordType.prototype.fields.each(function(c) {
12108             switch( c.type) {
12109                 case 'int' : da[c.name] = 0; break;
12110                 case 'date' : da[c.name] = new Date(); break;
12111                 case 'float' : da[c.name] = 0.0; break;
12112                 case 'boolean' : da[c.name] = false; break;
12113                 default : da[c.name] = ""; break;
12114             }
12115             
12116         });
12117         return new this.recordType(Roo.apply(da, d));
12118     }
12119     
12120 };/*
12121  * Based on:
12122  * Ext JS Library 1.1.1
12123  * Copyright(c) 2006-2007, Ext JS, LLC.
12124  *
12125  * Originally Released Under LGPL - original licence link has changed is not relivant.
12126  *
12127  * Fork - LGPL
12128  * <script type="text/javascript">
12129  */
12130
12131 /**
12132  * @class Roo.data.DataProxy
12133  * @extends Roo.data.Observable
12134  * This class is an abstract base class for implementations which provide retrieval of
12135  * unformatted data objects.<br>
12136  * <p>
12137  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12138  * (of the appropriate type which knows how to parse the data object) to provide a block of
12139  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12140  * <p>
12141  * Custom implementations must implement the load method as described in
12142  * {@link Roo.data.HttpProxy#load}.
12143  */
12144 Roo.data.DataProxy = function(){
12145     this.addEvents({
12146         /**
12147          * @event beforeload
12148          * Fires before a network request is made to retrieve a data object.
12149          * @param {Object} This DataProxy object.
12150          * @param {Object} params The params parameter to the load function.
12151          */
12152         beforeload : true,
12153         /**
12154          * @event load
12155          * Fires before the load method's callback is called.
12156          * @param {Object} This DataProxy object.
12157          * @param {Object} o The data object.
12158          * @param {Object} arg The callback argument object passed to the load function.
12159          */
12160         load : true,
12161         /**
12162          * @event loadexception
12163          * Fires if an Exception occurs during data retrieval.
12164          * @param {Object} This DataProxy object.
12165          * @param {Object} o The data object.
12166          * @param {Object} arg The callback argument object passed to the load function.
12167          * @param {Object} e The Exception.
12168          */
12169         loadexception : true
12170     });
12171     Roo.data.DataProxy.superclass.constructor.call(this);
12172 };
12173
12174 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12175
12176     /**
12177      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12178      */
12179 /*
12180  * Based on:
12181  * Ext JS Library 1.1.1
12182  * Copyright(c) 2006-2007, Ext JS, LLC.
12183  *
12184  * Originally Released Under LGPL - original licence link has changed is not relivant.
12185  *
12186  * Fork - LGPL
12187  * <script type="text/javascript">
12188  */
12189 /**
12190  * @class Roo.data.MemoryProxy
12191  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12192  * to the Reader when its load method is called.
12193  * @constructor
12194  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12195  */
12196 Roo.data.MemoryProxy = function(data){
12197     if (data.data) {
12198         data = data.data;
12199     }
12200     Roo.data.MemoryProxy.superclass.constructor.call(this);
12201     this.data = data;
12202 };
12203
12204 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12205     
12206     /**
12207      * Load data from the requested source (in this case an in-memory
12208      * data object passed to the constructor), read the data object into
12209      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12210      * process that block using the passed callback.
12211      * @param {Object} params This parameter is not used by the MemoryProxy class.
12212      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12213      * object into a block of Roo.data.Records.
12214      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12215      * The function must be passed <ul>
12216      * <li>The Record block object</li>
12217      * <li>The "arg" argument from the load function</li>
12218      * <li>A boolean success indicator</li>
12219      * </ul>
12220      * @param {Object} scope The scope in which to call the callback
12221      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12222      */
12223     load : function(params, reader, callback, scope, arg){
12224         params = params || {};
12225         var result;
12226         try {
12227             result = reader.readRecords(this.data);
12228         }catch(e){
12229             this.fireEvent("loadexception", this, arg, null, e);
12230             callback.call(scope, null, arg, false);
12231             return;
12232         }
12233         callback.call(scope, result, arg, true);
12234     },
12235     
12236     // private
12237     update : function(params, records){
12238         
12239     }
12240 });/*
12241  * Based on:
12242  * Ext JS Library 1.1.1
12243  * Copyright(c) 2006-2007, Ext JS, LLC.
12244  *
12245  * Originally Released Under LGPL - original licence link has changed is not relivant.
12246  *
12247  * Fork - LGPL
12248  * <script type="text/javascript">
12249  */
12250 /**
12251  * @class Roo.data.HttpProxy
12252  * @extends Roo.data.DataProxy
12253  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12254  * configured to reference a certain URL.<br><br>
12255  * <p>
12256  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12257  * from which the running page was served.<br><br>
12258  * <p>
12259  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12260  * <p>
12261  * Be aware that to enable the browser to parse an XML document, the server must set
12262  * the Content-Type header in the HTTP response to "text/xml".
12263  * @constructor
12264  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12265  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12266  * will be used to make the request.
12267  */
12268 Roo.data.HttpProxy = function(conn){
12269     Roo.data.HttpProxy.superclass.constructor.call(this);
12270     // is conn a conn config or a real conn?
12271     this.conn = conn;
12272     this.useAjax = !conn || !conn.events;
12273   
12274 };
12275
12276 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12277     // thse are take from connection...
12278     
12279     /**
12280      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12281      */
12282     /**
12283      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12284      * extra parameters to each request made by this object. (defaults to undefined)
12285      */
12286     /**
12287      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12288      *  to each request made by this object. (defaults to undefined)
12289      */
12290     /**
12291      * @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)
12292      */
12293     /**
12294      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12295      */
12296      /**
12297      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12298      * @type Boolean
12299      */
12300   
12301
12302     /**
12303      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12304      * @type Boolean
12305      */
12306     /**
12307      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12308      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12309      * a finer-grained basis than the DataProxy events.
12310      */
12311     getConnection : function(){
12312         return this.useAjax ? Roo.Ajax : this.conn;
12313     },
12314
12315     /**
12316      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12317      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12318      * process that block using the passed callback.
12319      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12320      * for the request to the remote server.
12321      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12322      * object into a block of Roo.data.Records.
12323      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12324      * The function must be passed <ul>
12325      * <li>The Record block object</li>
12326      * <li>The "arg" argument from the load function</li>
12327      * <li>A boolean success indicator</li>
12328      * </ul>
12329      * @param {Object} scope The scope in which to call the callback
12330      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12331      */
12332     load : function(params, reader, callback, scope, arg){
12333         if(this.fireEvent("beforeload", this, params) !== false){
12334             var  o = {
12335                 params : params || {},
12336                 request: {
12337                     callback : callback,
12338                     scope : scope,
12339                     arg : arg
12340                 },
12341                 reader: reader,
12342                 callback : this.loadResponse,
12343                 scope: this
12344             };
12345             if(this.useAjax){
12346                 Roo.applyIf(o, this.conn);
12347                 if(this.activeRequest){
12348                     Roo.Ajax.abort(this.activeRequest);
12349                 }
12350                 this.activeRequest = Roo.Ajax.request(o);
12351             }else{
12352                 this.conn.request(o);
12353             }
12354         }else{
12355             callback.call(scope||this, null, arg, false);
12356         }
12357     },
12358
12359     // private
12360     loadResponse : function(o, success, response){
12361         delete this.activeRequest;
12362         if(!success){
12363             this.fireEvent("loadexception", this, o, response);
12364             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12365             return;
12366         }
12367         var result;
12368         try {
12369             result = o.reader.read(response);
12370         }catch(e){
12371             this.fireEvent("loadexception", this, o, response, e);
12372             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12373             return;
12374         }
12375         
12376         this.fireEvent("load", this, o, o.request.arg);
12377         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12378     },
12379
12380     // private
12381     update : function(dataSet){
12382
12383     },
12384
12385     // private
12386     updateResponse : function(dataSet){
12387
12388     }
12389 });/*
12390  * Based on:
12391  * Ext JS Library 1.1.1
12392  * Copyright(c) 2006-2007, Ext JS, LLC.
12393  *
12394  * Originally Released Under LGPL - original licence link has changed is not relivant.
12395  *
12396  * Fork - LGPL
12397  * <script type="text/javascript">
12398  */
12399
12400 /**
12401  * @class Roo.data.ScriptTagProxy
12402  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12403  * other than the originating domain of the running page.<br><br>
12404  * <p>
12405  * <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
12406  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12407  * <p>
12408  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12409  * source code that is used as the source inside a &lt;script> tag.<br><br>
12410  * <p>
12411  * In order for the browser to process the returned data, the server must wrap the data object
12412  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12413  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12414  * depending on whether the callback name was passed:
12415  * <p>
12416  * <pre><code>
12417 boolean scriptTag = false;
12418 String cb = request.getParameter("callback");
12419 if (cb != null) {
12420     scriptTag = true;
12421     response.setContentType("text/javascript");
12422 } else {
12423     response.setContentType("application/x-json");
12424 }
12425 Writer out = response.getWriter();
12426 if (scriptTag) {
12427     out.write(cb + "(");
12428 }
12429 out.print(dataBlock.toJsonString());
12430 if (scriptTag) {
12431     out.write(");");
12432 }
12433 </pre></code>
12434  *
12435  * @constructor
12436  * @param {Object} config A configuration object.
12437  */
12438 Roo.data.ScriptTagProxy = function(config){
12439     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12440     Roo.apply(this, config);
12441     this.head = document.getElementsByTagName("head")[0];
12442 };
12443
12444 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12445
12446 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12447     /**
12448      * @cfg {String} url The URL from which to request the data object.
12449      */
12450     /**
12451      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12452      */
12453     timeout : 30000,
12454     /**
12455      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12456      * the server the name of the callback function set up by the load call to process the returned data object.
12457      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12458      * javascript output which calls this named function passing the data object as its only parameter.
12459      */
12460     callbackParam : "callback",
12461     /**
12462      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12463      * name to the request.
12464      */
12465     nocache : true,
12466
12467     /**
12468      * Load data from the configured URL, read the data object into
12469      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12470      * process that block using the passed callback.
12471      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12472      * for the request to the remote server.
12473      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12474      * object into a block of Roo.data.Records.
12475      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12476      * The function must be passed <ul>
12477      * <li>The Record block object</li>
12478      * <li>The "arg" argument from the load function</li>
12479      * <li>A boolean success indicator</li>
12480      * </ul>
12481      * @param {Object} scope The scope in which to call the callback
12482      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12483      */
12484     load : function(params, reader, callback, scope, arg){
12485         if(this.fireEvent("beforeload", this, params) !== false){
12486
12487             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12488
12489             var url = this.url;
12490             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12491             if(this.nocache){
12492                 url += "&_dc=" + (new Date().getTime());
12493             }
12494             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12495             var trans = {
12496                 id : transId,
12497                 cb : "stcCallback"+transId,
12498                 scriptId : "stcScript"+transId,
12499                 params : params,
12500                 arg : arg,
12501                 url : url,
12502                 callback : callback,
12503                 scope : scope,
12504                 reader : reader
12505             };
12506             var conn = this;
12507
12508             window[trans.cb] = function(o){
12509                 conn.handleResponse(o, trans);
12510             };
12511
12512             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12513
12514             if(this.autoAbort !== false){
12515                 this.abort();
12516             }
12517
12518             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12519
12520             var script = document.createElement("script");
12521             script.setAttribute("src", url);
12522             script.setAttribute("type", "text/javascript");
12523             script.setAttribute("id", trans.scriptId);
12524             this.head.appendChild(script);
12525
12526             this.trans = trans;
12527         }else{
12528             callback.call(scope||this, null, arg, false);
12529         }
12530     },
12531
12532     // private
12533     isLoading : function(){
12534         return this.trans ? true : false;
12535     },
12536
12537     /**
12538      * Abort the current server request.
12539      */
12540     abort : function(){
12541         if(this.isLoading()){
12542             this.destroyTrans(this.trans);
12543         }
12544     },
12545
12546     // private
12547     destroyTrans : function(trans, isLoaded){
12548         this.head.removeChild(document.getElementById(trans.scriptId));
12549         clearTimeout(trans.timeoutId);
12550         if(isLoaded){
12551             window[trans.cb] = undefined;
12552             try{
12553                 delete window[trans.cb];
12554             }catch(e){}
12555         }else{
12556             // if hasn't been loaded, wait for load to remove it to prevent script error
12557             window[trans.cb] = function(){
12558                 window[trans.cb] = undefined;
12559                 try{
12560                     delete window[trans.cb];
12561                 }catch(e){}
12562             };
12563         }
12564     },
12565
12566     // private
12567     handleResponse : function(o, trans){
12568         this.trans = false;
12569         this.destroyTrans(trans, true);
12570         var result;
12571         try {
12572             result = trans.reader.readRecords(o);
12573         }catch(e){
12574             this.fireEvent("loadexception", this, o, trans.arg, e);
12575             trans.callback.call(trans.scope||window, null, trans.arg, false);
12576             return;
12577         }
12578         this.fireEvent("load", this, o, trans.arg);
12579         trans.callback.call(trans.scope||window, result, trans.arg, true);
12580     },
12581
12582     // private
12583     handleFailure : function(trans){
12584         this.trans = false;
12585         this.destroyTrans(trans, false);
12586         this.fireEvent("loadexception", this, null, trans.arg);
12587         trans.callback.call(trans.scope||window, null, trans.arg, false);
12588     }
12589 });/*
12590  * Based on:
12591  * Ext JS Library 1.1.1
12592  * Copyright(c) 2006-2007, Ext JS, LLC.
12593  *
12594  * Originally Released Under LGPL - original licence link has changed is not relivant.
12595  *
12596  * Fork - LGPL
12597  * <script type="text/javascript">
12598  */
12599
12600 /**
12601  * @class Roo.data.JsonReader
12602  * @extends Roo.data.DataReader
12603  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12604  * based on mappings in a provided Roo.data.Record constructor.
12605  * 
12606  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12607  * in the reply previously. 
12608  * 
12609  * <p>
12610  * Example code:
12611  * <pre><code>
12612 var RecordDef = Roo.data.Record.create([
12613     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12614     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12615 ]);
12616 var myReader = new Roo.data.JsonReader({
12617     totalProperty: "results",    // The property which contains the total dataset size (optional)
12618     root: "rows",                // The property which contains an Array of row objects
12619     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12620 }, RecordDef);
12621 </code></pre>
12622  * <p>
12623  * This would consume a JSON file like this:
12624  * <pre><code>
12625 { 'results': 2, 'rows': [
12626     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12627     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12628 }
12629 </code></pre>
12630  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12631  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12632  * paged from the remote server.
12633  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12634  * @cfg {String} root name of the property which contains the Array of row objects.
12635  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12636  * @cfg {Array} fields Array of field definition objects
12637  * @constructor
12638  * Create a new JsonReader
12639  * @param {Object} meta Metadata configuration options
12640  * @param {Object} recordType Either an Array of field definition objects,
12641  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12642  */
12643 Roo.data.JsonReader = function(meta, recordType){
12644     
12645     meta = meta || {};
12646     // set some defaults:
12647     Roo.applyIf(meta, {
12648         totalProperty: 'total',
12649         successProperty : 'success',
12650         root : 'data',
12651         id : 'id'
12652     });
12653     
12654     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12655 };
12656 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12657     
12658     /**
12659      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12660      * Used by Store query builder to append _requestMeta to params.
12661      * 
12662      */
12663     metaFromRemote : false,
12664     /**
12665      * This method is only used by a DataProxy which has retrieved data from a remote server.
12666      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12667      * @return {Object} data A data block which is used by an Roo.data.Store object as
12668      * a cache of Roo.data.Records.
12669      */
12670     read : function(response){
12671         var json = response.responseText;
12672        
12673         var o = /* eval:var:o */ eval("("+json+")");
12674         if(!o) {
12675             throw {message: "JsonReader.read: Json object not found"};
12676         }
12677         
12678         if(o.metaData){
12679             
12680             delete this.ef;
12681             this.metaFromRemote = true;
12682             this.meta = o.metaData;
12683             this.recordType = Roo.data.Record.create(o.metaData.fields);
12684             this.onMetaChange(this.meta, this.recordType, o);
12685         }
12686         return this.readRecords(o);
12687     },
12688
12689     // private function a store will implement
12690     onMetaChange : function(meta, recordType, o){
12691
12692     },
12693
12694     /**
12695          * @ignore
12696          */
12697     simpleAccess: function(obj, subsc) {
12698         return obj[subsc];
12699     },
12700
12701         /**
12702          * @ignore
12703          */
12704     getJsonAccessor: function(){
12705         var re = /[\[\.]/;
12706         return function(expr) {
12707             try {
12708                 return(re.test(expr))
12709                     ? new Function("obj", "return obj." + expr)
12710                     : function(obj){
12711                         return obj[expr];
12712                     };
12713             } catch(e){}
12714             return Roo.emptyFn;
12715         };
12716     }(),
12717
12718     /**
12719      * Create a data block containing Roo.data.Records from an XML document.
12720      * @param {Object} o An object which contains an Array of row objects in the property specified
12721      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12722      * which contains the total size of the dataset.
12723      * @return {Object} data A data block which is used by an Roo.data.Store object as
12724      * a cache of Roo.data.Records.
12725      */
12726     readRecords : function(o){
12727         /**
12728          * After any data loads, the raw JSON data is available for further custom processing.
12729          * @type Object
12730          */
12731         this.o = o;
12732         var s = this.meta, Record = this.recordType,
12733             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12734
12735 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12736         if (!this.ef) {
12737             if(s.totalProperty) {
12738                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12739                 }
12740                 if(s.successProperty) {
12741                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12742                 }
12743                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12744                 if (s.id) {
12745                         var g = this.getJsonAccessor(s.id);
12746                         this.getId = function(rec) {
12747                                 var r = g(rec);  
12748                                 return (r === undefined || r === "") ? null : r;
12749                         };
12750                 } else {
12751                         this.getId = function(){return null;};
12752                 }
12753             this.ef = [];
12754             for(var jj = 0; jj < fl; jj++){
12755                 f = fi[jj];
12756                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12757                 this.ef[jj] = this.getJsonAccessor(map);
12758             }
12759         }
12760
12761         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12762         if(s.totalProperty){
12763             var vt = parseInt(this.getTotal(o), 10);
12764             if(!isNaN(vt)){
12765                 totalRecords = vt;
12766             }
12767         }
12768         if(s.successProperty){
12769             var vs = this.getSuccess(o);
12770             if(vs === false || vs === 'false'){
12771                 success = false;
12772             }
12773         }
12774         var records = [];
12775         for(var i = 0; i < c; i++){
12776                 var n = root[i];
12777             var values = {};
12778             var id = this.getId(n);
12779             for(var j = 0; j < fl; j++){
12780                 f = fi[j];
12781             var v = this.ef[j](n);
12782             if (!f.convert) {
12783                 Roo.log('missing convert for ' + f.name);
12784                 Roo.log(f);
12785                 continue;
12786             }
12787             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12788             }
12789             var record = new Record(values, id);
12790             record.json = n;
12791             records[i] = record;
12792         }
12793         return {
12794             raw : o,
12795             success : success,
12796             records : records,
12797             totalRecords : totalRecords
12798         };
12799     }
12800 });/*
12801  * Based on:
12802  * Ext JS Library 1.1.1
12803  * Copyright(c) 2006-2007, Ext JS, LLC.
12804  *
12805  * Originally Released Under LGPL - original licence link has changed is not relivant.
12806  *
12807  * Fork - LGPL
12808  * <script type="text/javascript">
12809  */
12810
12811 /**
12812  * @class Roo.data.ArrayReader
12813  * @extends Roo.data.DataReader
12814  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12815  * Each element of that Array represents a row of data fields. The
12816  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12817  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12818  * <p>
12819  * Example code:.
12820  * <pre><code>
12821 var RecordDef = Roo.data.Record.create([
12822     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12823     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12824 ]);
12825 var myReader = new Roo.data.ArrayReader({
12826     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12827 }, RecordDef);
12828 </code></pre>
12829  * <p>
12830  * This would consume an Array like this:
12831  * <pre><code>
12832 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12833   </code></pre>
12834  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12835  * @constructor
12836  * Create a new JsonReader
12837  * @param {Object} meta Metadata configuration options.
12838  * @param {Object} recordType Either an Array of field definition objects
12839  * as specified to {@link Roo.data.Record#create},
12840  * or an {@link Roo.data.Record} object
12841  * created using {@link Roo.data.Record#create}.
12842  */
12843 Roo.data.ArrayReader = function(meta, recordType){
12844     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12845 };
12846
12847 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12848     /**
12849      * Create a data block containing Roo.data.Records from an XML document.
12850      * @param {Object} o An Array of row objects which represents the dataset.
12851      * @return {Object} data A data block which is used by an Roo.data.Store object as
12852      * a cache of Roo.data.Records.
12853      */
12854     readRecords : function(o){
12855         var sid = this.meta ? this.meta.id : null;
12856         var recordType = this.recordType, fields = recordType.prototype.fields;
12857         var records = [];
12858         var root = o;
12859             for(var i = 0; i < root.length; i++){
12860                     var n = root[i];
12861                 var values = {};
12862                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12863                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12864                 var f = fields.items[j];
12865                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12866                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12867                 v = f.convert(v);
12868                 values[f.name] = v;
12869             }
12870                 var record = new recordType(values, id);
12871                 record.json = n;
12872                 records[records.length] = record;
12873             }
12874             return {
12875                 records : records,
12876                 totalRecords : records.length
12877             };
12878     }
12879 });/*
12880  * - LGPL
12881  * * 
12882  */
12883
12884 /**
12885  * @class Roo.bootstrap.ComboBox
12886  * @extends Roo.bootstrap.TriggerField
12887  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12888  * @cfg {Boolean} append (true|false) default false
12889  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12890  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12891  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12892  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12893  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12894  * @cfg {Boolean} animate default true
12895  * @cfg {Boolean} emptyResultText only for touch device
12896  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12897  * @cfg {String} emptyTitle default ''
12898  * @constructor
12899  * Create a new ComboBox.
12900  * @param {Object} config Configuration options
12901  */
12902 Roo.bootstrap.ComboBox = function(config){
12903     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12904     this.addEvents({
12905         /**
12906          * @event expand
12907          * Fires when the dropdown list is expanded
12908         * @param {Roo.bootstrap.ComboBox} combo This combo box
12909         */
12910         'expand' : true,
12911         /**
12912          * @event collapse
12913          * Fires when the dropdown list is collapsed
12914         * @param {Roo.bootstrap.ComboBox} combo This combo box
12915         */
12916         'collapse' : true,
12917         /**
12918          * @event beforeselect
12919          * Fires before a list item is selected. Return false to cancel the selection.
12920         * @param {Roo.bootstrap.ComboBox} combo This combo box
12921         * @param {Roo.data.Record} record The data record returned from the underlying store
12922         * @param {Number} index The index of the selected item in the dropdown list
12923         */
12924         'beforeselect' : true,
12925         /**
12926          * @event select
12927          * Fires when a list item is selected
12928         * @param {Roo.bootstrap.ComboBox} combo This combo box
12929         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12930         * @param {Number} index The index of the selected item in the dropdown list
12931         */
12932         'select' : true,
12933         /**
12934          * @event beforequery
12935          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12936          * The event object passed has these properties:
12937         * @param {Roo.bootstrap.ComboBox} combo This combo box
12938         * @param {String} query The query
12939         * @param {Boolean} forceAll true to force "all" query
12940         * @param {Boolean} cancel true to cancel the query
12941         * @param {Object} e The query event object
12942         */
12943         'beforequery': true,
12944          /**
12945          * @event add
12946          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12947         * @param {Roo.bootstrap.ComboBox} combo This combo box
12948         */
12949         'add' : true,
12950         /**
12951          * @event edit
12952          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12953         * @param {Roo.bootstrap.ComboBox} combo This combo box
12954         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12955         */
12956         'edit' : true,
12957         /**
12958          * @event remove
12959          * Fires when the remove value from the combobox array
12960         * @param {Roo.bootstrap.ComboBox} combo This combo box
12961         */
12962         'remove' : true,
12963         /**
12964          * @event afterremove
12965          * Fires when the remove value from the combobox array
12966         * @param {Roo.bootstrap.ComboBox} combo This combo box
12967         */
12968         'afterremove' : true,
12969         /**
12970          * @event specialfilter
12971          * Fires when specialfilter
12972             * @param {Roo.bootstrap.ComboBox} combo This combo box
12973             */
12974         'specialfilter' : true,
12975         /**
12976          * @event tick
12977          * Fires when tick the element
12978             * @param {Roo.bootstrap.ComboBox} combo This combo box
12979             */
12980         'tick' : true,
12981         /**
12982          * @event touchviewdisplay
12983          * Fires when touch view require special display (default is using displayField)
12984             * @param {Roo.bootstrap.ComboBox} combo This combo box
12985             * @param {Object} cfg set html .
12986             */
12987         'touchviewdisplay' : true
12988         
12989     });
12990     
12991     this.item = [];
12992     this.tickItems = [];
12993     
12994     this.selectedIndex = -1;
12995     if(this.mode == 'local'){
12996         if(config.queryDelay === undefined){
12997             this.queryDelay = 10;
12998         }
12999         if(config.minChars === undefined){
13000             this.minChars = 0;
13001         }
13002     }
13003 };
13004
13005 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13006      
13007     /**
13008      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13009      * rendering into an Roo.Editor, defaults to false)
13010      */
13011     /**
13012      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13013      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13014      */
13015     /**
13016      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13017      */
13018     /**
13019      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13020      * the dropdown list (defaults to undefined, with no header element)
13021      */
13022
13023      /**
13024      * @cfg {String/Roo.Template} tpl The template to use to render the output
13025      */
13026      
13027      /**
13028      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13029      */
13030     listWidth: undefined,
13031     /**
13032      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13033      * mode = 'remote' or 'text' if mode = 'local')
13034      */
13035     displayField: undefined,
13036     
13037     /**
13038      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13039      * mode = 'remote' or 'value' if mode = 'local'). 
13040      * Note: use of a valueField requires the user make a selection
13041      * in order for a value to be mapped.
13042      */
13043     valueField: undefined,
13044     /**
13045      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13046      */
13047     modalTitle : '',
13048     
13049     /**
13050      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13051      * field's data value (defaults to the underlying DOM element's name)
13052      */
13053     hiddenName: undefined,
13054     /**
13055      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13056      */
13057     listClass: '',
13058     /**
13059      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13060      */
13061     selectedClass: 'active',
13062     
13063     /**
13064      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13065      */
13066     shadow:'sides',
13067     /**
13068      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13069      * anchor positions (defaults to 'tl-bl')
13070      */
13071     listAlign: 'tl-bl?',
13072     /**
13073      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13074      */
13075     maxHeight: 300,
13076     /**
13077      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13078      * query specified by the allQuery config option (defaults to 'query')
13079      */
13080     triggerAction: 'query',
13081     /**
13082      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13083      * (defaults to 4, does not apply if editable = false)
13084      */
13085     minChars : 4,
13086     /**
13087      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13088      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13089      */
13090     typeAhead: false,
13091     /**
13092      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13093      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13094      */
13095     queryDelay: 500,
13096     /**
13097      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13098      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13099      */
13100     pageSize: 0,
13101     /**
13102      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13103      * when editable = true (defaults to false)
13104      */
13105     selectOnFocus:false,
13106     /**
13107      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13108      */
13109     queryParam: 'query',
13110     /**
13111      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13112      * when mode = 'remote' (defaults to 'Loading...')
13113      */
13114     loadingText: 'Loading...',
13115     /**
13116      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13117      */
13118     resizable: false,
13119     /**
13120      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13121      */
13122     handleHeight : 8,
13123     /**
13124      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13125      * traditional select (defaults to true)
13126      */
13127     editable: true,
13128     /**
13129      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13130      */
13131     allQuery: '',
13132     /**
13133      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13134      */
13135     mode: 'remote',
13136     /**
13137      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13138      * listWidth has a higher value)
13139      */
13140     minListWidth : 70,
13141     /**
13142      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13143      * allow the user to set arbitrary text into the field (defaults to false)
13144      */
13145     forceSelection:false,
13146     /**
13147      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13148      * if typeAhead = true (defaults to 250)
13149      */
13150     typeAheadDelay : 250,
13151     /**
13152      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13153      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13154      */
13155     valueNotFoundText : undefined,
13156     /**
13157      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13158      */
13159     blockFocus : false,
13160     
13161     /**
13162      * @cfg {Boolean} disableClear Disable showing of clear button.
13163      */
13164     disableClear : false,
13165     /**
13166      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13167      */
13168     alwaysQuery : false,
13169     
13170     /**
13171      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13172      */
13173     multiple : false,
13174     
13175     /**
13176      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13177      */
13178     invalidClass : "has-warning",
13179     
13180     /**
13181      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13182      */
13183     validClass : "has-success",
13184     
13185     /**
13186      * @cfg {Boolean} specialFilter (true|false) special filter default false
13187      */
13188     specialFilter : false,
13189     
13190     /**
13191      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13192      */
13193     mobileTouchView : true,
13194     
13195     /**
13196      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13197      */
13198     useNativeIOS : false,
13199     
13200     /**
13201      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13202      */
13203     mobile_restrict_height : false,
13204     
13205     ios_options : false,
13206     
13207     //private
13208     addicon : false,
13209     editicon: false,
13210     
13211     page: 0,
13212     hasQuery: false,
13213     append: false,
13214     loadNext: false,
13215     autoFocus : true,
13216     tickable : false,
13217     btnPosition : 'right',
13218     triggerList : true,
13219     showToggleBtn : true,
13220     animate : true,
13221     emptyResultText: 'Empty',
13222     triggerText : 'Select',
13223     emptyTitle : '',
13224     
13225     // element that contains real text value.. (when hidden is used..)
13226     
13227     getAutoCreate : function()
13228     {   
13229         var cfg = false;
13230         //render
13231         /*
13232          * Render classic select for iso
13233          */
13234         
13235         if(Roo.isIOS && this.useNativeIOS){
13236             cfg = this.getAutoCreateNativeIOS();
13237             return cfg;
13238         }
13239         
13240         /*
13241          * Touch Devices
13242          */
13243         
13244         if(Roo.isTouch && this.mobileTouchView){
13245             cfg = this.getAutoCreateTouchView();
13246             return cfg;;
13247         }
13248         
13249         /*
13250          *  Normal ComboBox
13251          */
13252         if(!this.tickable){
13253             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13254             return cfg;
13255         }
13256         
13257         /*
13258          *  ComboBox with tickable selections
13259          */
13260              
13261         var align = this.labelAlign || this.parentLabelAlign();
13262         
13263         cfg = {
13264             cls : 'form-group roo-combobox-tickable' //input-group
13265         };
13266         
13267         var btn_text_select = '';
13268         var btn_text_done = '';
13269         var btn_text_cancel = '';
13270         
13271         if (this.btn_text_show) {
13272             btn_text_select = 'Select';
13273             btn_text_done = 'Done';
13274             btn_text_cancel = 'Cancel'; 
13275         }
13276         
13277         var buttons = {
13278             tag : 'div',
13279             cls : 'tickable-buttons',
13280             cn : [
13281                 {
13282                     tag : 'button',
13283                     type : 'button',
13284                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13285                     //html : this.triggerText
13286                     html: btn_text_select
13287                 },
13288                 {
13289                     tag : 'button',
13290                     type : 'button',
13291                     name : 'ok',
13292                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13293                     //html : 'Done'
13294                     html: btn_text_done
13295                 },
13296                 {
13297                     tag : 'button',
13298                     type : 'button',
13299                     name : 'cancel',
13300                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13301                     //html : 'Cancel'
13302                     html: btn_text_cancel
13303                 }
13304             ]
13305         };
13306         
13307         if(this.editable){
13308             buttons.cn.unshift({
13309                 tag: 'input',
13310                 cls: 'roo-select2-search-field-input'
13311             });
13312         }
13313         
13314         var _this = this;
13315         
13316         Roo.each(buttons.cn, function(c){
13317             if (_this.size) {
13318                 c.cls += ' btn-' + _this.size;
13319             }
13320
13321             if (_this.disabled) {
13322                 c.disabled = true;
13323             }
13324         });
13325         
13326         var box = {
13327             tag: 'div',
13328             cn: [
13329                 {
13330                     tag: 'input',
13331                     type : 'hidden',
13332                     cls: 'form-hidden-field'
13333                 },
13334                 {
13335                     tag: 'ul',
13336                     cls: 'roo-select2-choices',
13337                     cn:[
13338                         {
13339                             tag: 'li',
13340                             cls: 'roo-select2-search-field',
13341                             cn: [
13342                                 buttons
13343                             ]
13344                         }
13345                     ]
13346                 }
13347             ]
13348         };
13349         
13350         var combobox = {
13351             cls: 'roo-select2-container input-group roo-select2-container-multi',
13352             cn: [
13353                 box
13354 //                {
13355 //                    tag: 'ul',
13356 //                    cls: 'typeahead typeahead-long dropdown-menu',
13357 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13358 //                }
13359             ]
13360         };
13361         
13362         if(this.hasFeedback && !this.allowBlank){
13363             
13364             var feedback = {
13365                 tag: 'span',
13366                 cls: 'glyphicon form-control-feedback'
13367             };
13368
13369             combobox.cn.push(feedback);
13370         }
13371         
13372         
13373         if (align ==='left' && this.fieldLabel.length) {
13374             
13375             cfg.cls += ' roo-form-group-label-left';
13376             
13377             cfg.cn = [
13378                 {
13379                     tag : 'i',
13380                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13381                     tooltip : 'This field is required'
13382                 },
13383                 {
13384                     tag: 'label',
13385                     'for' :  id,
13386                     cls : 'control-label',
13387                     html : this.fieldLabel
13388
13389                 },
13390                 {
13391                     cls : "", 
13392                     cn: [
13393                         combobox
13394                     ]
13395                 }
13396
13397             ];
13398             
13399             var labelCfg = cfg.cn[1];
13400             var contentCfg = cfg.cn[2];
13401             
13402
13403             if(this.indicatorpos == 'right'){
13404                 
13405                 cfg.cn = [
13406                     {
13407                         tag: 'label',
13408                         'for' :  id,
13409                         cls : 'control-label',
13410                         cn : [
13411                             {
13412                                 tag : 'span',
13413                                 html : this.fieldLabel
13414                             },
13415                             {
13416                                 tag : 'i',
13417                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13418                                 tooltip : 'This field is required'
13419                             }
13420                         ]
13421                     },
13422                     {
13423                         cls : "",
13424                         cn: [
13425                             combobox
13426                         ]
13427                     }
13428
13429                 ];
13430                 
13431                 
13432                 
13433                 labelCfg = cfg.cn[0];
13434                 contentCfg = cfg.cn[1];
13435             
13436             }
13437             
13438             if(this.labelWidth > 12){
13439                 labelCfg.style = "width: " + this.labelWidth + 'px';
13440             }
13441             
13442             if(this.labelWidth < 13 && this.labelmd == 0){
13443                 this.labelmd = this.labelWidth;
13444             }
13445             
13446             if(this.labellg > 0){
13447                 labelCfg.cls += ' col-lg-' + this.labellg;
13448                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13449             }
13450             
13451             if(this.labelmd > 0){
13452                 labelCfg.cls += ' col-md-' + this.labelmd;
13453                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13454             }
13455             
13456             if(this.labelsm > 0){
13457                 labelCfg.cls += ' col-sm-' + this.labelsm;
13458                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13459             }
13460             
13461             if(this.labelxs > 0){
13462                 labelCfg.cls += ' col-xs-' + this.labelxs;
13463                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13464             }
13465                 
13466                 
13467         } else if ( this.fieldLabel.length) {
13468 //                Roo.log(" label");
13469                  cfg.cn = [
13470                     {
13471                         tag : 'i',
13472                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13473                         tooltip : 'This field is required'
13474                     },
13475                     {
13476                         tag: 'label',
13477                         //cls : 'input-group-addon',
13478                         html : this.fieldLabel
13479                     },
13480                     combobox
13481                 ];
13482                 
13483                 if(this.indicatorpos == 'right'){
13484                     cfg.cn = [
13485                         {
13486                             tag: 'label',
13487                             //cls : 'input-group-addon',
13488                             html : this.fieldLabel
13489                         },
13490                         {
13491                             tag : 'i',
13492                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13493                             tooltip : 'This field is required'
13494                         },
13495                         combobox
13496                     ];
13497                     
13498                 }
13499
13500         } else {
13501             
13502 //                Roo.log(" no label && no align");
13503                 cfg = combobox
13504                      
13505                 
13506         }
13507          
13508         var settings=this;
13509         ['xs','sm','md','lg'].map(function(size){
13510             if (settings[size]) {
13511                 cfg.cls += ' col-' + size + '-' + settings[size];
13512             }
13513         });
13514         
13515         return cfg;
13516         
13517     },
13518     
13519     _initEventsCalled : false,
13520     
13521     // private
13522     initEvents: function()
13523     {   
13524         if (this._initEventsCalled) { // as we call render... prevent looping...
13525             return;
13526         }
13527         this._initEventsCalled = true;
13528         
13529         if (!this.store) {
13530             throw "can not find store for combo";
13531         }
13532         
13533         this.indicator = this.indicatorEl();
13534         
13535         this.store = Roo.factory(this.store, Roo.data);
13536         this.store.parent = this;
13537         
13538         // if we are building from html. then this element is so complex, that we can not really
13539         // use the rendered HTML.
13540         // so we have to trash and replace the previous code.
13541         if (Roo.XComponent.build_from_html) {
13542             // remove this element....
13543             var e = this.el.dom, k=0;
13544             while (e ) { e = e.previousSibling;  ++k;}
13545
13546             this.el.remove();
13547             
13548             this.el=false;
13549             this.rendered = false;
13550             
13551             this.render(this.parent().getChildContainer(true), k);
13552         }
13553         
13554         if(Roo.isIOS && this.useNativeIOS){
13555             this.initIOSView();
13556             return;
13557         }
13558         
13559         /*
13560          * Touch Devices
13561          */
13562         
13563         if(Roo.isTouch && this.mobileTouchView){
13564             this.initTouchView();
13565             return;
13566         }
13567         
13568         if(this.tickable){
13569             this.initTickableEvents();
13570             return;
13571         }
13572         
13573         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13574         
13575         if(this.hiddenName){
13576             
13577             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13578             
13579             this.hiddenField.dom.value =
13580                 this.hiddenValue !== undefined ? this.hiddenValue :
13581                 this.value !== undefined ? this.value : '';
13582
13583             // prevent input submission
13584             this.el.dom.removeAttribute('name');
13585             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13586              
13587              
13588         }
13589         //if(Roo.isGecko){
13590         //    this.el.dom.setAttribute('autocomplete', 'off');
13591         //}
13592         
13593         var cls = 'x-combo-list';
13594         
13595         //this.list = new Roo.Layer({
13596         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13597         //});
13598         
13599         var _this = this;
13600         
13601         (function(){
13602             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13603             _this.list.setWidth(lw);
13604         }).defer(100);
13605         
13606         this.list.on('mouseover', this.onViewOver, this);
13607         this.list.on('mousemove', this.onViewMove, this);
13608         this.list.on('scroll', this.onViewScroll, this);
13609         
13610         /*
13611         this.list.swallowEvent('mousewheel');
13612         this.assetHeight = 0;
13613
13614         if(this.title){
13615             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13616             this.assetHeight += this.header.getHeight();
13617         }
13618
13619         this.innerList = this.list.createChild({cls:cls+'-inner'});
13620         this.innerList.on('mouseover', this.onViewOver, this);
13621         this.innerList.on('mousemove', this.onViewMove, this);
13622         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13623         
13624         if(this.allowBlank && !this.pageSize && !this.disableClear){
13625             this.footer = this.list.createChild({cls:cls+'-ft'});
13626             this.pageTb = new Roo.Toolbar(this.footer);
13627            
13628         }
13629         if(this.pageSize){
13630             this.footer = this.list.createChild({cls:cls+'-ft'});
13631             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13632                     {pageSize: this.pageSize});
13633             
13634         }
13635         
13636         if (this.pageTb && this.allowBlank && !this.disableClear) {
13637             var _this = this;
13638             this.pageTb.add(new Roo.Toolbar.Fill(), {
13639                 cls: 'x-btn-icon x-btn-clear',
13640                 text: '&#160;',
13641                 handler: function()
13642                 {
13643                     _this.collapse();
13644                     _this.clearValue();
13645                     _this.onSelect(false, -1);
13646                 }
13647             });
13648         }
13649         if (this.footer) {
13650             this.assetHeight += this.footer.getHeight();
13651         }
13652         */
13653             
13654         if(!this.tpl){
13655             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13656         }
13657
13658         this.view = new Roo.View(this.list, this.tpl, {
13659             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13660         });
13661         //this.view.wrapEl.setDisplayed(false);
13662         this.view.on('click', this.onViewClick, this);
13663         
13664         
13665         this.store.on('beforeload', this.onBeforeLoad, this);
13666         this.store.on('load', this.onLoad, this);
13667         this.store.on('loadexception', this.onLoadException, this);
13668         /*
13669         if(this.resizable){
13670             this.resizer = new Roo.Resizable(this.list,  {
13671                pinned:true, handles:'se'
13672             });
13673             this.resizer.on('resize', function(r, w, h){
13674                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13675                 this.listWidth = w;
13676                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13677                 this.restrictHeight();
13678             }, this);
13679             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13680         }
13681         */
13682         if(!this.editable){
13683             this.editable = true;
13684             this.setEditable(false);
13685         }
13686         
13687         /*
13688         
13689         if (typeof(this.events.add.listeners) != 'undefined') {
13690             
13691             this.addicon = this.wrap.createChild(
13692                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13693        
13694             this.addicon.on('click', function(e) {
13695                 this.fireEvent('add', this);
13696             }, this);
13697         }
13698         if (typeof(this.events.edit.listeners) != 'undefined') {
13699             
13700             this.editicon = this.wrap.createChild(
13701                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13702             if (this.addicon) {
13703                 this.editicon.setStyle('margin-left', '40px');
13704             }
13705             this.editicon.on('click', function(e) {
13706                 
13707                 // we fire even  if inothing is selected..
13708                 this.fireEvent('edit', this, this.lastData );
13709                 
13710             }, this);
13711         }
13712         */
13713         
13714         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13715             "up" : function(e){
13716                 this.inKeyMode = true;
13717                 this.selectPrev();
13718             },
13719
13720             "down" : function(e){
13721                 if(!this.isExpanded()){
13722                     this.onTriggerClick();
13723                 }else{
13724                     this.inKeyMode = true;
13725                     this.selectNext();
13726                 }
13727             },
13728
13729             "enter" : function(e){
13730 //                this.onViewClick();
13731                 //return true;
13732                 this.collapse();
13733                 
13734                 if(this.fireEvent("specialkey", this, e)){
13735                     this.onViewClick(false);
13736                 }
13737                 
13738                 return true;
13739             },
13740
13741             "esc" : function(e){
13742                 this.collapse();
13743             },
13744
13745             "tab" : function(e){
13746                 this.collapse();
13747                 
13748                 if(this.fireEvent("specialkey", this, e)){
13749                     this.onViewClick(false);
13750                 }
13751                 
13752                 return true;
13753             },
13754
13755             scope : this,
13756
13757             doRelay : function(foo, bar, hname){
13758                 if(hname == 'down' || this.scope.isExpanded()){
13759                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13760                 }
13761                 return true;
13762             },
13763
13764             forceKeyDown: true
13765         });
13766         
13767         
13768         this.queryDelay = Math.max(this.queryDelay || 10,
13769                 this.mode == 'local' ? 10 : 250);
13770         
13771         
13772         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13773         
13774         if(this.typeAhead){
13775             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13776         }
13777         if(this.editable !== false){
13778             this.inputEl().on("keyup", this.onKeyUp, this);
13779         }
13780         if(this.forceSelection){
13781             this.inputEl().on('blur', this.doForce, this);
13782         }
13783         
13784         if(this.multiple){
13785             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13786             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13787         }
13788     },
13789     
13790     initTickableEvents: function()
13791     {   
13792         this.createList();
13793         
13794         if(this.hiddenName){
13795             
13796             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13797             
13798             this.hiddenField.dom.value =
13799                 this.hiddenValue !== undefined ? this.hiddenValue :
13800                 this.value !== undefined ? this.value : '';
13801
13802             // prevent input submission
13803             this.el.dom.removeAttribute('name');
13804             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13805              
13806              
13807         }
13808         
13809 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13810         
13811         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13812         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13813         if(this.triggerList){
13814             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13815         }
13816          
13817         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13818         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13819         
13820         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13821         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13822         
13823         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13824         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13825         
13826         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13827         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13828         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13829         
13830         this.okBtn.hide();
13831         this.cancelBtn.hide();
13832         
13833         var _this = this;
13834         
13835         (function(){
13836             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13837             _this.list.setWidth(lw);
13838         }).defer(100);
13839         
13840         this.list.on('mouseover', this.onViewOver, this);
13841         this.list.on('mousemove', this.onViewMove, this);
13842         
13843         this.list.on('scroll', this.onViewScroll, this);
13844         
13845         if(!this.tpl){
13846             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13847                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13848         }
13849
13850         this.view = new Roo.View(this.list, this.tpl, {
13851             singleSelect:true,
13852             tickable:true,
13853             parent:this,
13854             store: this.store,
13855             selectedClass: this.selectedClass
13856         });
13857         
13858         //this.view.wrapEl.setDisplayed(false);
13859         this.view.on('click', this.onViewClick, this);
13860         
13861         
13862         
13863         this.store.on('beforeload', this.onBeforeLoad, this);
13864         this.store.on('load', this.onLoad, this);
13865         this.store.on('loadexception', this.onLoadException, this);
13866         
13867         if(this.editable){
13868             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13869                 "up" : function(e){
13870                     this.inKeyMode = true;
13871                     this.selectPrev();
13872                 },
13873
13874                 "down" : function(e){
13875                     this.inKeyMode = true;
13876                     this.selectNext();
13877                 },
13878
13879                 "enter" : function(e){
13880                     if(this.fireEvent("specialkey", this, e)){
13881                         this.onViewClick(false);
13882                     }
13883                     
13884                     return true;
13885                 },
13886
13887                 "esc" : function(e){
13888                     this.onTickableFooterButtonClick(e, false, false);
13889                 },
13890
13891                 "tab" : function(e){
13892                     this.fireEvent("specialkey", this, e);
13893                     
13894                     this.onTickableFooterButtonClick(e, false, false);
13895                     
13896                     return true;
13897                 },
13898
13899                 scope : this,
13900
13901                 doRelay : function(e, fn, key){
13902                     if(this.scope.isExpanded()){
13903                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13904                     }
13905                     return true;
13906                 },
13907
13908                 forceKeyDown: true
13909             });
13910         }
13911         
13912         this.queryDelay = Math.max(this.queryDelay || 10,
13913                 this.mode == 'local' ? 10 : 250);
13914         
13915         
13916         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13917         
13918         if(this.typeAhead){
13919             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13920         }
13921         
13922         if(this.editable !== false){
13923             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13924         }
13925         
13926         this.indicator = this.indicatorEl();
13927         
13928         if(this.indicator){
13929             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13930             this.indicator.hide();
13931         }
13932         
13933     },
13934
13935     onDestroy : function(){
13936         if(this.view){
13937             this.view.setStore(null);
13938             this.view.el.removeAllListeners();
13939             this.view.el.remove();
13940             this.view.purgeListeners();
13941         }
13942         if(this.list){
13943             this.list.dom.innerHTML  = '';
13944         }
13945         
13946         if(this.store){
13947             this.store.un('beforeload', this.onBeforeLoad, this);
13948             this.store.un('load', this.onLoad, this);
13949             this.store.un('loadexception', this.onLoadException, this);
13950         }
13951         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13952     },
13953
13954     // private
13955     fireKey : function(e){
13956         if(e.isNavKeyPress() && !this.list.isVisible()){
13957             this.fireEvent("specialkey", this, e);
13958         }
13959     },
13960
13961     // private
13962     onResize: function(w, h){
13963 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13964 //        
13965 //        if(typeof w != 'number'){
13966 //            // we do not handle it!?!?
13967 //            return;
13968 //        }
13969 //        var tw = this.trigger.getWidth();
13970 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13971 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13972 //        var x = w - tw;
13973 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13974 //            
13975 //        //this.trigger.setStyle('left', x+'px');
13976 //        
13977 //        if(this.list && this.listWidth === undefined){
13978 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13979 //            this.list.setWidth(lw);
13980 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13981 //        }
13982         
13983     
13984         
13985     },
13986
13987     /**
13988      * Allow or prevent the user from directly editing the field text.  If false is passed,
13989      * the user will only be able to select from the items defined in the dropdown list.  This method
13990      * is the runtime equivalent of setting the 'editable' config option at config time.
13991      * @param {Boolean} value True to allow the user to directly edit the field text
13992      */
13993     setEditable : function(value){
13994         if(value == this.editable){
13995             return;
13996         }
13997         this.editable = value;
13998         if(!value){
13999             this.inputEl().dom.setAttribute('readOnly', true);
14000             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14001             this.inputEl().addClass('x-combo-noedit');
14002         }else{
14003             this.inputEl().dom.setAttribute('readOnly', false);
14004             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14005             this.inputEl().removeClass('x-combo-noedit');
14006         }
14007     },
14008
14009     // private
14010     
14011     onBeforeLoad : function(combo,opts){
14012         if(!this.hasFocus){
14013             return;
14014         }
14015          if (!opts.add) {
14016             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14017          }
14018         this.restrictHeight();
14019         this.selectedIndex = -1;
14020     },
14021
14022     // private
14023     onLoad : function(){
14024         
14025         this.hasQuery = false;
14026         
14027         if(!this.hasFocus){
14028             return;
14029         }
14030         
14031         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14032             this.loading.hide();
14033         }
14034         
14035         if(this.store.getCount() > 0){
14036             
14037             this.expand();
14038             this.restrictHeight();
14039             if(this.lastQuery == this.allQuery){
14040                 if(this.editable && !this.tickable){
14041                     this.inputEl().dom.select();
14042                 }
14043                 
14044                 if(
14045                     !this.selectByValue(this.value, true) &&
14046                     this.autoFocus && 
14047                     (
14048                         !this.store.lastOptions ||
14049                         typeof(this.store.lastOptions.add) == 'undefined' || 
14050                         this.store.lastOptions.add != true
14051                     )
14052                 ){
14053                     this.select(0, true);
14054                 }
14055             }else{
14056                 if(this.autoFocus){
14057                     this.selectNext();
14058                 }
14059                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14060                     this.taTask.delay(this.typeAheadDelay);
14061                 }
14062             }
14063         }else{
14064             this.onEmptyResults();
14065         }
14066         
14067         //this.el.focus();
14068     },
14069     // private
14070     onLoadException : function()
14071     {
14072         this.hasQuery = false;
14073         
14074         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14075             this.loading.hide();
14076         }
14077         
14078         if(this.tickable && this.editable){
14079             return;
14080         }
14081         
14082         this.collapse();
14083         // only causes errors at present
14084         //Roo.log(this.store.reader.jsonData);
14085         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14086             // fixme
14087             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14088         //}
14089         
14090         
14091     },
14092     // private
14093     onTypeAhead : function(){
14094         if(this.store.getCount() > 0){
14095             var r = this.store.getAt(0);
14096             var newValue = r.data[this.displayField];
14097             var len = newValue.length;
14098             var selStart = this.getRawValue().length;
14099             
14100             if(selStart != len){
14101                 this.setRawValue(newValue);
14102                 this.selectText(selStart, newValue.length);
14103             }
14104         }
14105     },
14106
14107     // private
14108     onSelect : function(record, index){
14109         
14110         if(this.fireEvent('beforeselect', this, record, index) !== false){
14111         
14112             this.setFromData(index > -1 ? record.data : false);
14113             
14114             this.collapse();
14115             this.fireEvent('select', this, record, index);
14116         }
14117     },
14118
14119     /**
14120      * Returns the currently selected field value or empty string if no value is set.
14121      * @return {String} value The selected value
14122      */
14123     getValue : function()
14124     {
14125         if(Roo.isIOS && this.useNativeIOS){
14126             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14127         }
14128         
14129         if(this.multiple){
14130             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14131         }
14132         
14133         if(this.valueField){
14134             return typeof this.value != 'undefined' ? this.value : '';
14135         }else{
14136             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14137         }
14138     },
14139     
14140     getRawValue : function()
14141     {
14142         if(Roo.isIOS && this.useNativeIOS){
14143             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14144         }
14145         
14146         var v = this.inputEl().getValue();
14147         
14148         return v;
14149     },
14150
14151     /**
14152      * Clears any text/value currently set in the field
14153      */
14154     clearValue : function(){
14155         
14156         if(this.hiddenField){
14157             this.hiddenField.dom.value = '';
14158         }
14159         this.value = '';
14160         this.setRawValue('');
14161         this.lastSelectionText = '';
14162         this.lastData = false;
14163         
14164         var close = this.closeTriggerEl();
14165         
14166         if(close){
14167             close.hide();
14168         }
14169         
14170         this.validate();
14171         
14172     },
14173
14174     /**
14175      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14176      * will be displayed in the field.  If the value does not match the data value of an existing item,
14177      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14178      * Otherwise the field will be blank (although the value will still be set).
14179      * @param {String} value The value to match
14180      */
14181     setValue : function(v)
14182     {
14183         if(Roo.isIOS && this.useNativeIOS){
14184             this.setIOSValue(v);
14185             return;
14186         }
14187         
14188         if(this.multiple){
14189             this.syncValue();
14190             return;
14191         }
14192         
14193         var text = v;
14194         if(this.valueField){
14195             var r = this.findRecord(this.valueField, v);
14196             if(r){
14197                 text = r.data[this.displayField];
14198             }else if(this.valueNotFoundText !== undefined){
14199                 text = this.valueNotFoundText;
14200             }
14201         }
14202         this.lastSelectionText = text;
14203         if(this.hiddenField){
14204             this.hiddenField.dom.value = v;
14205         }
14206         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14207         this.value = v;
14208         
14209         var close = this.closeTriggerEl();
14210         
14211         if(close){
14212             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14213         }
14214         
14215         this.validate();
14216     },
14217     /**
14218      * @property {Object} the last set data for the element
14219      */
14220     
14221     lastData : false,
14222     /**
14223      * Sets the value of the field based on a object which is related to the record format for the store.
14224      * @param {Object} value the value to set as. or false on reset?
14225      */
14226     setFromData : function(o){
14227         
14228         if(this.multiple){
14229             this.addItem(o);
14230             return;
14231         }
14232             
14233         var dv = ''; // display value
14234         var vv = ''; // value value..
14235         this.lastData = o;
14236         if (this.displayField) {
14237             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14238         } else {
14239             // this is an error condition!!!
14240             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14241         }
14242         
14243         if(this.valueField){
14244             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14245         }
14246         
14247         var close = this.closeTriggerEl();
14248         
14249         if(close){
14250             if(dv.length || vv * 1 > 0){
14251                 close.show() ;
14252                 this.blockFocus=true;
14253             } else {
14254                 close.hide();
14255             }             
14256         }
14257         
14258         if(this.hiddenField){
14259             this.hiddenField.dom.value = vv;
14260             
14261             this.lastSelectionText = dv;
14262             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14263             this.value = vv;
14264             return;
14265         }
14266         // no hidden field.. - we store the value in 'value', but still display
14267         // display field!!!!
14268         this.lastSelectionText = dv;
14269         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14270         this.value = vv;
14271         
14272         
14273         
14274     },
14275     // private
14276     reset : function(){
14277         // overridden so that last data is reset..
14278         
14279         if(this.multiple){
14280             this.clearItem();
14281             return;
14282         }
14283         
14284         this.setValue(this.originalValue);
14285         //this.clearInvalid();
14286         this.lastData = false;
14287         if (this.view) {
14288             this.view.clearSelections();
14289         }
14290         
14291         this.validate();
14292     },
14293     // private
14294     findRecord : function(prop, value){
14295         var record;
14296         if(this.store.getCount() > 0){
14297             this.store.each(function(r){
14298                 if(r.data[prop] == value){
14299                     record = r;
14300                     return false;
14301                 }
14302                 return true;
14303             });
14304         }
14305         return record;
14306     },
14307     
14308     getName: function()
14309     {
14310         // returns hidden if it's set..
14311         if (!this.rendered) {return ''};
14312         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14313         
14314     },
14315     // private
14316     onViewMove : function(e, t){
14317         this.inKeyMode = false;
14318     },
14319
14320     // private
14321     onViewOver : function(e, t){
14322         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14323             return;
14324         }
14325         var item = this.view.findItemFromChild(t);
14326         
14327         if(item){
14328             var index = this.view.indexOf(item);
14329             this.select(index, false);
14330         }
14331     },
14332
14333     // private
14334     onViewClick : function(view, doFocus, el, e)
14335     {
14336         var index = this.view.getSelectedIndexes()[0];
14337         
14338         var r = this.store.getAt(index);
14339         
14340         if(this.tickable){
14341             
14342             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14343                 return;
14344             }
14345             
14346             var rm = false;
14347             var _this = this;
14348             
14349             Roo.each(this.tickItems, function(v,k){
14350                 
14351                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14352                     Roo.log(v);
14353                     _this.tickItems.splice(k, 1);
14354                     
14355                     if(typeof(e) == 'undefined' && view == false){
14356                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14357                     }
14358                     
14359                     rm = true;
14360                     return;
14361                 }
14362             });
14363             
14364             if(rm){
14365                 return;
14366             }
14367             
14368             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14369                 this.tickItems.push(r.data);
14370             }
14371             
14372             if(typeof(e) == 'undefined' && view == false){
14373                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14374             }
14375                     
14376             return;
14377         }
14378         
14379         if(r){
14380             this.onSelect(r, index);
14381         }
14382         if(doFocus !== false && !this.blockFocus){
14383             this.inputEl().focus();
14384         }
14385     },
14386
14387     // private
14388     restrictHeight : function(){
14389         //this.innerList.dom.style.height = '';
14390         //var inner = this.innerList.dom;
14391         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14392         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14393         //this.list.beginUpdate();
14394         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14395         this.list.alignTo(this.inputEl(), this.listAlign);
14396         this.list.alignTo(this.inputEl(), this.listAlign);
14397         //this.list.endUpdate();
14398     },
14399
14400     // private
14401     onEmptyResults : function(){
14402         
14403         if(this.tickable && this.editable){
14404             this.hasFocus = false;
14405             this.restrictHeight();
14406             return;
14407         }
14408         
14409         this.collapse();
14410     },
14411
14412     /**
14413      * Returns true if the dropdown list is expanded, else false.
14414      */
14415     isExpanded : function(){
14416         return this.list.isVisible();
14417     },
14418
14419     /**
14420      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14421      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14422      * @param {String} value The data value of the item to select
14423      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14424      * selected item if it is not currently in view (defaults to true)
14425      * @return {Boolean} True if the value matched an item in the list, else false
14426      */
14427     selectByValue : function(v, scrollIntoView){
14428         if(v !== undefined && v !== null){
14429             var r = this.findRecord(this.valueField || this.displayField, v);
14430             if(r){
14431                 this.select(this.store.indexOf(r), scrollIntoView);
14432                 return true;
14433             }
14434         }
14435         return false;
14436     },
14437
14438     /**
14439      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14440      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14441      * @param {Number} index The zero-based index of the list item to select
14442      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14443      * selected item if it is not currently in view (defaults to true)
14444      */
14445     select : function(index, scrollIntoView){
14446         this.selectedIndex = index;
14447         this.view.select(index);
14448         if(scrollIntoView !== false){
14449             var el = this.view.getNode(index);
14450             /*
14451              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14452              */
14453             if(el){
14454                 this.list.scrollChildIntoView(el, false);
14455             }
14456         }
14457     },
14458
14459     // private
14460     selectNext : function(){
14461         var ct = this.store.getCount();
14462         if(ct > 0){
14463             if(this.selectedIndex == -1){
14464                 this.select(0);
14465             }else if(this.selectedIndex < ct-1){
14466                 this.select(this.selectedIndex+1);
14467             }
14468         }
14469     },
14470
14471     // private
14472     selectPrev : function(){
14473         var ct = this.store.getCount();
14474         if(ct > 0){
14475             if(this.selectedIndex == -1){
14476                 this.select(0);
14477             }else if(this.selectedIndex != 0){
14478                 this.select(this.selectedIndex-1);
14479             }
14480         }
14481     },
14482
14483     // private
14484     onKeyUp : function(e){
14485         if(this.editable !== false && !e.isSpecialKey()){
14486             this.lastKey = e.getKey();
14487             this.dqTask.delay(this.queryDelay);
14488         }
14489     },
14490
14491     // private
14492     validateBlur : function(){
14493         return !this.list || !this.list.isVisible();   
14494     },
14495
14496     // private
14497     initQuery : function(){
14498         
14499         var v = this.getRawValue();
14500         
14501         if(this.tickable && this.editable){
14502             v = this.tickableInputEl().getValue();
14503         }
14504         
14505         this.doQuery(v);
14506     },
14507
14508     // private
14509     doForce : function(){
14510         if(this.inputEl().dom.value.length > 0){
14511             this.inputEl().dom.value =
14512                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14513              
14514         }
14515     },
14516
14517     /**
14518      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14519      * query allowing the query action to be canceled if needed.
14520      * @param {String} query The SQL query to execute
14521      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14522      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14523      * saved in the current store (defaults to false)
14524      */
14525     doQuery : function(q, forceAll){
14526         
14527         if(q === undefined || q === null){
14528             q = '';
14529         }
14530         var qe = {
14531             query: q,
14532             forceAll: forceAll,
14533             combo: this,
14534             cancel:false
14535         };
14536         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14537             return false;
14538         }
14539         q = qe.query;
14540         
14541         forceAll = qe.forceAll;
14542         if(forceAll === true || (q.length >= this.minChars)){
14543             
14544             this.hasQuery = true;
14545             
14546             if(this.lastQuery != q || this.alwaysQuery){
14547                 this.lastQuery = q;
14548                 if(this.mode == 'local'){
14549                     this.selectedIndex = -1;
14550                     if(forceAll){
14551                         this.store.clearFilter();
14552                     }else{
14553                         
14554                         if(this.specialFilter){
14555                             this.fireEvent('specialfilter', this);
14556                             this.onLoad();
14557                             return;
14558                         }
14559                         
14560                         this.store.filter(this.displayField, q);
14561                     }
14562                     
14563                     this.store.fireEvent("datachanged", this.store);
14564                     
14565                     this.onLoad();
14566                     
14567                     
14568                 }else{
14569                     
14570                     this.store.baseParams[this.queryParam] = q;
14571                     
14572                     var options = {params : this.getParams(q)};
14573                     
14574                     if(this.loadNext){
14575                         options.add = true;
14576                         options.params.start = this.page * this.pageSize;
14577                     }
14578                     
14579                     this.store.load(options);
14580                     
14581                     /*
14582                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14583                      *  we should expand the list on onLoad
14584                      *  so command out it
14585                      */
14586 //                    this.expand();
14587                 }
14588             }else{
14589                 this.selectedIndex = -1;
14590                 this.onLoad();   
14591             }
14592         }
14593         
14594         this.loadNext = false;
14595     },
14596     
14597     // private
14598     getParams : function(q){
14599         var p = {};
14600         //p[this.queryParam] = q;
14601         
14602         if(this.pageSize){
14603             p.start = 0;
14604             p.limit = this.pageSize;
14605         }
14606         return p;
14607     },
14608
14609     /**
14610      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14611      */
14612     collapse : function(){
14613         if(!this.isExpanded()){
14614             return;
14615         }
14616         
14617         this.list.hide();
14618         
14619         this.hasFocus = false;
14620         
14621         if(this.tickable){
14622             this.okBtn.hide();
14623             this.cancelBtn.hide();
14624             this.trigger.show();
14625             
14626             if(this.editable){
14627                 this.tickableInputEl().dom.value = '';
14628                 this.tickableInputEl().blur();
14629             }
14630             
14631         }
14632         
14633         Roo.get(document).un('mousedown', this.collapseIf, this);
14634         Roo.get(document).un('mousewheel', this.collapseIf, this);
14635         if (!this.editable) {
14636             Roo.get(document).un('keydown', this.listKeyPress, this);
14637         }
14638         this.fireEvent('collapse', this);
14639         
14640         this.validate();
14641     },
14642
14643     // private
14644     collapseIf : function(e){
14645         var in_combo  = e.within(this.el);
14646         var in_list =  e.within(this.list);
14647         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14648         
14649         if (in_combo || in_list || is_list) {
14650             //e.stopPropagation();
14651             return;
14652         }
14653         
14654         if(this.tickable){
14655             this.onTickableFooterButtonClick(e, false, false);
14656         }
14657
14658         this.collapse();
14659         
14660     },
14661
14662     /**
14663      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14664      */
14665     expand : function(){
14666        
14667         if(this.isExpanded() || !this.hasFocus){
14668             return;
14669         }
14670         
14671         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14672         this.list.setWidth(lw);
14673         
14674         Roo.log('expand');
14675         
14676         this.list.show();
14677         
14678         this.restrictHeight();
14679         
14680         if(this.tickable){
14681             
14682             this.tickItems = Roo.apply([], this.item);
14683             
14684             this.okBtn.show();
14685             this.cancelBtn.show();
14686             this.trigger.hide();
14687             
14688             if(this.editable){
14689                 this.tickableInputEl().focus();
14690             }
14691             
14692         }
14693         
14694         Roo.get(document).on('mousedown', this.collapseIf, this);
14695         Roo.get(document).on('mousewheel', this.collapseIf, this);
14696         if (!this.editable) {
14697             Roo.get(document).on('keydown', this.listKeyPress, this);
14698         }
14699         
14700         this.fireEvent('expand', this);
14701     },
14702
14703     // private
14704     // Implements the default empty TriggerField.onTriggerClick function
14705     onTriggerClick : function(e)
14706     {
14707         Roo.log('trigger click');
14708         
14709         if(this.disabled || !this.triggerList){
14710             return;
14711         }
14712         
14713         this.page = 0;
14714         this.loadNext = false;
14715         
14716         if(this.isExpanded()){
14717             this.collapse();
14718             if (!this.blockFocus) {
14719                 this.inputEl().focus();
14720             }
14721             
14722         }else {
14723             this.hasFocus = true;
14724             if(this.triggerAction == 'all') {
14725                 this.doQuery(this.allQuery, true);
14726             } else {
14727                 this.doQuery(this.getRawValue());
14728             }
14729             if (!this.blockFocus) {
14730                 this.inputEl().focus();
14731             }
14732         }
14733     },
14734     
14735     onTickableTriggerClick : function(e)
14736     {
14737         if(this.disabled){
14738             return;
14739         }
14740         
14741         this.page = 0;
14742         this.loadNext = false;
14743         this.hasFocus = true;
14744         
14745         if(this.triggerAction == 'all') {
14746             this.doQuery(this.allQuery, true);
14747         } else {
14748             this.doQuery(this.getRawValue());
14749         }
14750     },
14751     
14752     onSearchFieldClick : function(e)
14753     {
14754         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14755             this.onTickableFooterButtonClick(e, false, false);
14756             return;
14757         }
14758         
14759         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14760             return;
14761         }
14762         
14763         this.page = 0;
14764         this.loadNext = false;
14765         this.hasFocus = true;
14766         
14767         if(this.triggerAction == 'all') {
14768             this.doQuery(this.allQuery, true);
14769         } else {
14770             this.doQuery(this.getRawValue());
14771         }
14772     },
14773     
14774     listKeyPress : function(e)
14775     {
14776         //Roo.log('listkeypress');
14777         // scroll to first matching element based on key pres..
14778         if (e.isSpecialKey()) {
14779             return false;
14780         }
14781         var k = String.fromCharCode(e.getKey()).toUpperCase();
14782         //Roo.log(k);
14783         var match  = false;
14784         var csel = this.view.getSelectedNodes();
14785         var cselitem = false;
14786         if (csel.length) {
14787             var ix = this.view.indexOf(csel[0]);
14788             cselitem  = this.store.getAt(ix);
14789             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14790                 cselitem = false;
14791             }
14792             
14793         }
14794         
14795         this.store.each(function(v) { 
14796             if (cselitem) {
14797                 // start at existing selection.
14798                 if (cselitem.id == v.id) {
14799                     cselitem = false;
14800                 }
14801                 return true;
14802             }
14803                 
14804             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14805                 match = this.store.indexOf(v);
14806                 return false;
14807             }
14808             return true;
14809         }, this);
14810         
14811         if (match === false) {
14812             return true; // no more action?
14813         }
14814         // scroll to?
14815         this.view.select(match);
14816         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14817         sn.scrollIntoView(sn.dom.parentNode, false);
14818     },
14819     
14820     onViewScroll : function(e, t){
14821         
14822         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){
14823             return;
14824         }
14825         
14826         this.hasQuery = true;
14827         
14828         this.loading = this.list.select('.loading', true).first();
14829         
14830         if(this.loading === null){
14831             this.list.createChild({
14832                 tag: 'div',
14833                 cls: 'loading roo-select2-more-results roo-select2-active',
14834                 html: 'Loading more results...'
14835             });
14836             
14837             this.loading = this.list.select('.loading', true).first();
14838             
14839             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14840             
14841             this.loading.hide();
14842         }
14843         
14844         this.loading.show();
14845         
14846         var _combo = this;
14847         
14848         this.page++;
14849         this.loadNext = true;
14850         
14851         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14852         
14853         return;
14854     },
14855     
14856     addItem : function(o)
14857     {   
14858         var dv = ''; // display value
14859         
14860         if (this.displayField) {
14861             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14862         } else {
14863             // this is an error condition!!!
14864             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14865         }
14866         
14867         if(!dv.length){
14868             return;
14869         }
14870         
14871         var choice = this.choices.createChild({
14872             tag: 'li',
14873             cls: 'roo-select2-search-choice',
14874             cn: [
14875                 {
14876                     tag: 'div',
14877                     html: dv
14878                 },
14879                 {
14880                     tag: 'a',
14881                     href: '#',
14882                     cls: 'roo-select2-search-choice-close fa fa-times',
14883                     tabindex: '-1'
14884                 }
14885             ]
14886             
14887         }, this.searchField);
14888         
14889         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14890         
14891         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14892         
14893         this.item.push(o);
14894         
14895         this.lastData = o;
14896         
14897         this.syncValue();
14898         
14899         this.inputEl().dom.value = '';
14900         
14901         this.validate();
14902     },
14903     
14904     onRemoveItem : function(e, _self, o)
14905     {
14906         e.preventDefault();
14907         
14908         this.lastItem = Roo.apply([], this.item);
14909         
14910         var index = this.item.indexOf(o.data) * 1;
14911         
14912         if( index < 0){
14913             Roo.log('not this item?!');
14914             return;
14915         }
14916         
14917         this.item.splice(index, 1);
14918         o.item.remove();
14919         
14920         this.syncValue();
14921         
14922         this.fireEvent('remove', this, e);
14923         
14924         this.validate();
14925         
14926     },
14927     
14928     syncValue : function()
14929     {
14930         if(!this.item.length){
14931             this.clearValue();
14932             return;
14933         }
14934             
14935         var value = [];
14936         var _this = this;
14937         Roo.each(this.item, function(i){
14938             if(_this.valueField){
14939                 value.push(i[_this.valueField]);
14940                 return;
14941             }
14942
14943             value.push(i);
14944         });
14945
14946         this.value = value.join(',');
14947
14948         if(this.hiddenField){
14949             this.hiddenField.dom.value = this.value;
14950         }
14951         
14952         this.store.fireEvent("datachanged", this.store);
14953         
14954         this.validate();
14955     },
14956     
14957     clearItem : function()
14958     {
14959         if(!this.multiple){
14960             return;
14961         }
14962         
14963         this.item = [];
14964         
14965         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14966            c.remove();
14967         });
14968         
14969         this.syncValue();
14970         
14971         this.validate();
14972         
14973         if(this.tickable && !Roo.isTouch){
14974             this.view.refresh();
14975         }
14976     },
14977     
14978     inputEl: function ()
14979     {
14980         if(Roo.isIOS && this.useNativeIOS){
14981             return this.el.select('select.roo-ios-select', true).first();
14982         }
14983         
14984         if(Roo.isTouch && this.mobileTouchView){
14985             return this.el.select('input.form-control',true).first();
14986         }
14987         
14988         if(this.tickable){
14989             return this.searchField;
14990         }
14991         
14992         return this.el.select('input.form-control',true).first();
14993     },
14994     
14995     onTickableFooterButtonClick : function(e, btn, el)
14996     {
14997         e.preventDefault();
14998         
14999         this.lastItem = Roo.apply([], this.item);
15000         
15001         if(btn && btn.name == 'cancel'){
15002             this.tickItems = Roo.apply([], this.item);
15003             this.collapse();
15004             return;
15005         }
15006         
15007         this.clearItem();
15008         
15009         var _this = this;
15010         
15011         Roo.each(this.tickItems, function(o){
15012             _this.addItem(o);
15013         });
15014         
15015         this.collapse();
15016         
15017     },
15018     
15019     validate : function()
15020     {
15021         if(this.getVisibilityEl().hasClass('hidden')){
15022             return true;
15023         }
15024         
15025         var v = this.getRawValue();
15026         
15027         if(this.multiple){
15028             v = this.getValue();
15029         }
15030         
15031         if(this.disabled || this.allowBlank || v.length){
15032             this.markValid();
15033             return true;
15034         }
15035         
15036         this.markInvalid();
15037         return false;
15038     },
15039     
15040     tickableInputEl : function()
15041     {
15042         if(!this.tickable || !this.editable){
15043             return this.inputEl();
15044         }
15045         
15046         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15047     },
15048     
15049     
15050     getAutoCreateTouchView : function()
15051     {
15052         var id = Roo.id();
15053         
15054         var cfg = {
15055             cls: 'form-group' //input-group
15056         };
15057         
15058         var input =  {
15059             tag: 'input',
15060             id : id,
15061             type : this.inputType,
15062             cls : 'form-control x-combo-noedit',
15063             autocomplete: 'new-password',
15064             placeholder : this.placeholder || '',
15065             readonly : true
15066         };
15067         
15068         if (this.name) {
15069             input.name = this.name;
15070         }
15071         
15072         if (this.size) {
15073             input.cls += ' input-' + this.size;
15074         }
15075         
15076         if (this.disabled) {
15077             input.disabled = true;
15078         }
15079         
15080         var inputblock = {
15081             cls : '',
15082             cn : [
15083                 input
15084             ]
15085         };
15086         
15087         if(this.before){
15088             inputblock.cls += ' input-group';
15089             
15090             inputblock.cn.unshift({
15091                 tag :'span',
15092                 cls : 'input-group-addon',
15093                 html : this.before
15094             });
15095         }
15096         
15097         if(this.removable && !this.multiple){
15098             inputblock.cls += ' roo-removable';
15099             
15100             inputblock.cn.push({
15101                 tag: 'button',
15102                 html : 'x',
15103                 cls : 'roo-combo-removable-btn close'
15104             });
15105         }
15106
15107         if(this.hasFeedback && !this.allowBlank){
15108             
15109             inputblock.cls += ' has-feedback';
15110             
15111             inputblock.cn.push({
15112                 tag: 'span',
15113                 cls: 'glyphicon form-control-feedback'
15114             });
15115             
15116         }
15117         
15118         if (this.after) {
15119             
15120             inputblock.cls += (this.before) ? '' : ' input-group';
15121             
15122             inputblock.cn.push({
15123                 tag :'span',
15124                 cls : 'input-group-addon',
15125                 html : this.after
15126             });
15127         }
15128
15129         var box = {
15130             tag: 'div',
15131             cn: [
15132                 {
15133                     tag: 'input',
15134                     type : 'hidden',
15135                     cls: 'form-hidden-field'
15136                 },
15137                 inputblock
15138             ]
15139             
15140         };
15141         
15142         if(this.multiple){
15143             box = {
15144                 tag: 'div',
15145                 cn: [
15146                     {
15147                         tag: 'input',
15148                         type : 'hidden',
15149                         cls: 'form-hidden-field'
15150                     },
15151                     {
15152                         tag: 'ul',
15153                         cls: 'roo-select2-choices',
15154                         cn:[
15155                             {
15156                                 tag: 'li',
15157                                 cls: 'roo-select2-search-field',
15158                                 cn: [
15159
15160                                     inputblock
15161                                 ]
15162                             }
15163                         ]
15164                     }
15165                 ]
15166             }
15167         };
15168         
15169         var combobox = {
15170             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15171             cn: [
15172                 box
15173             ]
15174         };
15175         
15176         if(!this.multiple && this.showToggleBtn){
15177             
15178             var caret = {
15179                         tag: 'span',
15180                         cls: 'caret'
15181             };
15182             
15183             if (this.caret != false) {
15184                 caret = {
15185                      tag: 'i',
15186                      cls: 'fa fa-' + this.caret
15187                 };
15188                 
15189             }
15190             
15191             combobox.cn.push({
15192                 tag :'span',
15193                 cls : 'input-group-addon btn dropdown-toggle',
15194                 cn : [
15195                     caret,
15196                     {
15197                         tag: 'span',
15198                         cls: 'combobox-clear',
15199                         cn  : [
15200                             {
15201                                 tag : 'i',
15202                                 cls: 'icon-remove'
15203                             }
15204                         ]
15205                     }
15206                 ]
15207
15208             })
15209         }
15210         
15211         if(this.multiple){
15212             combobox.cls += ' roo-select2-container-multi';
15213         }
15214         
15215         var align = this.labelAlign || this.parentLabelAlign();
15216         
15217         if (align ==='left' && this.fieldLabel.length) {
15218
15219             cfg.cn = [
15220                 {
15221                    tag : 'i',
15222                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15223                    tooltip : 'This field is required'
15224                 },
15225                 {
15226                     tag: 'label',
15227                     cls : 'control-label',
15228                     html : this.fieldLabel
15229
15230                 },
15231                 {
15232                     cls : '', 
15233                     cn: [
15234                         combobox
15235                     ]
15236                 }
15237             ];
15238             
15239             var labelCfg = cfg.cn[1];
15240             var contentCfg = cfg.cn[2];
15241             
15242
15243             if(this.indicatorpos == 'right'){
15244                 cfg.cn = [
15245                     {
15246                         tag: 'label',
15247                         'for' :  id,
15248                         cls : 'control-label',
15249                         cn : [
15250                             {
15251                                 tag : 'span',
15252                                 html : this.fieldLabel
15253                             },
15254                             {
15255                                 tag : 'i',
15256                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15257                                 tooltip : 'This field is required'
15258                             }
15259                         ]
15260                     },
15261                     {
15262                         cls : "",
15263                         cn: [
15264                             combobox
15265                         ]
15266                     }
15267
15268                 ];
15269                 
15270                 labelCfg = cfg.cn[0];
15271                 contentCfg = cfg.cn[1];
15272             }
15273             
15274            
15275             
15276             if(this.labelWidth > 12){
15277                 labelCfg.style = "width: " + this.labelWidth + 'px';
15278             }
15279             
15280             if(this.labelWidth < 13 && this.labelmd == 0){
15281                 this.labelmd = this.labelWidth;
15282             }
15283             
15284             if(this.labellg > 0){
15285                 labelCfg.cls += ' col-lg-' + this.labellg;
15286                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15287             }
15288             
15289             if(this.labelmd > 0){
15290                 labelCfg.cls += ' col-md-' + this.labelmd;
15291                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15292             }
15293             
15294             if(this.labelsm > 0){
15295                 labelCfg.cls += ' col-sm-' + this.labelsm;
15296                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15297             }
15298             
15299             if(this.labelxs > 0){
15300                 labelCfg.cls += ' col-xs-' + this.labelxs;
15301                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15302             }
15303                 
15304                 
15305         } else if ( this.fieldLabel.length) {
15306             cfg.cn = [
15307                 {
15308                    tag : 'i',
15309                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15310                    tooltip : 'This field is required'
15311                 },
15312                 {
15313                     tag: 'label',
15314                     cls : 'control-label',
15315                     html : this.fieldLabel
15316
15317                 },
15318                 {
15319                     cls : '', 
15320                     cn: [
15321                         combobox
15322                     ]
15323                 }
15324             ];
15325             
15326             if(this.indicatorpos == 'right'){
15327                 cfg.cn = [
15328                     {
15329                         tag: 'label',
15330                         cls : 'control-label',
15331                         html : this.fieldLabel,
15332                         cn : [
15333                             {
15334                                tag : 'i',
15335                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15336                                tooltip : 'This field is required'
15337                             }
15338                         ]
15339                     },
15340                     {
15341                         cls : '', 
15342                         cn: [
15343                             combobox
15344                         ]
15345                     }
15346                 ];
15347             }
15348         } else {
15349             cfg.cn = combobox;    
15350         }
15351         
15352         
15353         var settings = this;
15354         
15355         ['xs','sm','md','lg'].map(function(size){
15356             if (settings[size]) {
15357                 cfg.cls += ' col-' + size + '-' + settings[size];
15358             }
15359         });
15360         
15361         return cfg;
15362     },
15363     
15364     initTouchView : function()
15365     {
15366         this.renderTouchView();
15367         
15368         this.touchViewEl.on('scroll', function(){
15369             this.el.dom.scrollTop = 0;
15370         }, this);
15371         
15372         this.originalValue = this.getValue();
15373         
15374         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15375         
15376         this.inputEl().on("click", this.showTouchView, this);
15377         if (this.triggerEl) {
15378             this.triggerEl.on("click", this.showTouchView, this);
15379         }
15380         
15381         
15382         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15383         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15384         
15385         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15386         
15387         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15388         this.store.on('load', this.onTouchViewLoad, this);
15389         this.store.on('loadexception', this.onTouchViewLoadException, this);
15390         
15391         if(this.hiddenName){
15392             
15393             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15394             
15395             this.hiddenField.dom.value =
15396                 this.hiddenValue !== undefined ? this.hiddenValue :
15397                 this.value !== undefined ? this.value : '';
15398         
15399             this.el.dom.removeAttribute('name');
15400             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15401         }
15402         
15403         if(this.multiple){
15404             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15405             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15406         }
15407         
15408         if(this.removable && !this.multiple){
15409             var close = this.closeTriggerEl();
15410             if(close){
15411                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15412                 close.on('click', this.removeBtnClick, this, close);
15413             }
15414         }
15415         /*
15416          * fix the bug in Safari iOS8
15417          */
15418         this.inputEl().on("focus", function(e){
15419             document.activeElement.blur();
15420         }, this);
15421         
15422         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15423         
15424         return;
15425         
15426         
15427     },
15428     
15429     renderTouchView : function()
15430     {
15431         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15432         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15433         
15434         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15435         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15436         
15437         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15438         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15439         this.touchViewBodyEl.setStyle('overflow', 'auto');
15440         
15441         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15442         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15443         
15444         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15445         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15446         
15447     },
15448     
15449     showTouchView : function()
15450     {
15451         if(this.disabled){
15452             return;
15453         }
15454         
15455         this.touchViewHeaderEl.hide();
15456
15457         if(this.modalTitle.length){
15458             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15459             this.touchViewHeaderEl.show();
15460         }
15461
15462         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15463         this.touchViewEl.show();
15464
15465         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15466         
15467         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15468         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15469
15470         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15471
15472         if(this.modalTitle.length){
15473             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15474         }
15475         
15476         this.touchViewBodyEl.setHeight(bodyHeight);
15477
15478         if(this.animate){
15479             var _this = this;
15480             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15481         }else{
15482             this.touchViewEl.addClass('in');
15483         }
15484         
15485         if(this._touchViewMask){
15486             Roo.get(document.body).addClass("x-body-masked");
15487             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15488             this._touchViewMask.setStyle('z-index', 10000);
15489             this._touchViewMask.addClass('show');
15490         }
15491         
15492         this.doTouchViewQuery();
15493         
15494     },
15495     
15496     hideTouchView : function()
15497     {
15498         this.touchViewEl.removeClass('in');
15499
15500         if(this.animate){
15501             var _this = this;
15502             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15503         }else{
15504             this.touchViewEl.setStyle('display', 'none');
15505         }
15506         
15507         if(this._touchViewMask){
15508             this._touchViewMask.removeClass('show');
15509             Roo.get(document.body).removeClass("x-body-masked");
15510         }
15511     },
15512     
15513     setTouchViewValue : function()
15514     {
15515         if(this.multiple){
15516             this.clearItem();
15517         
15518             var _this = this;
15519
15520             Roo.each(this.tickItems, function(o){
15521                 this.addItem(o);
15522             }, this);
15523         }
15524         
15525         this.hideTouchView();
15526     },
15527     
15528     doTouchViewQuery : function()
15529     {
15530         var qe = {
15531             query: '',
15532             forceAll: true,
15533             combo: this,
15534             cancel:false
15535         };
15536         
15537         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15538             return false;
15539         }
15540         
15541         if(!this.alwaysQuery || this.mode == 'local'){
15542             this.onTouchViewLoad();
15543             return;
15544         }
15545         
15546         this.store.load();
15547     },
15548     
15549     onTouchViewBeforeLoad : function(combo,opts)
15550     {
15551         return;
15552     },
15553
15554     // private
15555     onTouchViewLoad : function()
15556     {
15557         if(this.store.getCount() < 1){
15558             this.onTouchViewEmptyResults();
15559             return;
15560         }
15561         
15562         this.clearTouchView();
15563         
15564         var rawValue = this.getRawValue();
15565         
15566         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15567         
15568         this.tickItems = [];
15569         
15570         this.store.data.each(function(d, rowIndex){
15571             var row = this.touchViewListGroup.createChild(template);
15572             
15573             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15574                 row.addClass(d.data.cls);
15575             }
15576             
15577             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15578                 var cfg = {
15579                     data : d.data,
15580                     html : d.data[this.displayField]
15581                 };
15582                 
15583                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15584                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15585                 }
15586             }
15587             row.removeClass('selected');
15588             if(!this.multiple && this.valueField &&
15589                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15590             {
15591                 // radio buttons..
15592                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15593                 row.addClass('selected');
15594             }
15595             
15596             if(this.multiple && this.valueField &&
15597                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15598             {
15599                 
15600                 // checkboxes...
15601                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15602                 this.tickItems.push(d.data);
15603             }
15604             
15605             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15606             
15607         }, this);
15608         
15609         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15610         
15611         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15612
15613         if(this.modalTitle.length){
15614             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15615         }
15616
15617         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15618         
15619         if(this.mobile_restrict_height && listHeight < bodyHeight){
15620             this.touchViewBodyEl.setHeight(listHeight);
15621         }
15622         
15623         var _this = this;
15624         
15625         if(firstChecked && listHeight > bodyHeight){
15626             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15627         }
15628         
15629     },
15630     
15631     onTouchViewLoadException : function()
15632     {
15633         this.hideTouchView();
15634     },
15635     
15636     onTouchViewEmptyResults : function()
15637     {
15638         this.clearTouchView();
15639         
15640         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15641         
15642         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15643         
15644     },
15645     
15646     clearTouchView : function()
15647     {
15648         this.touchViewListGroup.dom.innerHTML = '';
15649     },
15650     
15651     onTouchViewClick : function(e, el, o)
15652     {
15653         e.preventDefault();
15654         
15655         var row = o.row;
15656         var rowIndex = o.rowIndex;
15657         
15658         var r = this.store.getAt(rowIndex);
15659         
15660         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15661             
15662             if(!this.multiple){
15663                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15664                     c.dom.removeAttribute('checked');
15665                 }, this);
15666
15667                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15668
15669                 this.setFromData(r.data);
15670
15671                 var close = this.closeTriggerEl();
15672
15673                 if(close){
15674                     close.show();
15675                 }
15676
15677                 this.hideTouchView();
15678
15679                 this.fireEvent('select', this, r, rowIndex);
15680
15681                 return;
15682             }
15683
15684             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15685                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15686                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15687                 return;
15688             }
15689
15690             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15691             this.addItem(r.data);
15692             this.tickItems.push(r.data);
15693         }
15694     },
15695     
15696     getAutoCreateNativeIOS : function()
15697     {
15698         var cfg = {
15699             cls: 'form-group' //input-group,
15700         };
15701         
15702         var combobox =  {
15703             tag: 'select',
15704             cls : 'roo-ios-select'
15705         };
15706         
15707         if (this.name) {
15708             combobox.name = this.name;
15709         }
15710         
15711         if (this.disabled) {
15712             combobox.disabled = true;
15713         }
15714         
15715         var settings = this;
15716         
15717         ['xs','sm','md','lg'].map(function(size){
15718             if (settings[size]) {
15719                 cfg.cls += ' col-' + size + '-' + settings[size];
15720             }
15721         });
15722         
15723         cfg.cn = combobox;
15724         
15725         return cfg;
15726         
15727     },
15728     
15729     initIOSView : function()
15730     {
15731         this.store.on('load', this.onIOSViewLoad, this);
15732         
15733         return;
15734     },
15735     
15736     onIOSViewLoad : function()
15737     {
15738         if(this.store.getCount() < 1){
15739             return;
15740         }
15741         
15742         this.clearIOSView();
15743         
15744         if(this.allowBlank) {
15745             
15746             var default_text = '-- SELECT --';
15747             
15748             if(this.placeholder.length){
15749                 default_text = this.placeholder;
15750             }
15751             
15752             if(this.emptyTitle.length){
15753                 default_text += ' - ' + this.emptyTitle + ' -';
15754             }
15755             
15756             var opt = this.inputEl().createChild({
15757                 tag: 'option',
15758                 value : 0,
15759                 html : default_text
15760             });
15761             
15762             var o = {};
15763             o[this.valueField] = 0;
15764             o[this.displayField] = default_text;
15765             
15766             this.ios_options.push({
15767                 data : o,
15768                 el : opt
15769             });
15770             
15771         }
15772         
15773         this.store.data.each(function(d, rowIndex){
15774             
15775             var html = '';
15776             
15777             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15778                 html = d.data[this.displayField];
15779             }
15780             
15781             var value = '';
15782             
15783             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15784                 value = d.data[this.valueField];
15785             }
15786             
15787             var option = {
15788                 tag: 'option',
15789                 value : value,
15790                 html : html
15791             };
15792             
15793             if(this.value == d.data[this.valueField]){
15794                 option['selected'] = true;
15795             }
15796             
15797             var opt = this.inputEl().createChild(option);
15798             
15799             this.ios_options.push({
15800                 data : d.data,
15801                 el : opt
15802             });
15803             
15804         }, this);
15805         
15806         this.inputEl().on('change', function(){
15807            this.fireEvent('select', this);
15808         }, this);
15809         
15810     },
15811     
15812     clearIOSView: function()
15813     {
15814         this.inputEl().dom.innerHTML = '';
15815         
15816         this.ios_options = [];
15817     },
15818     
15819     setIOSValue: function(v)
15820     {
15821         this.value = v;
15822         
15823         if(!this.ios_options){
15824             return;
15825         }
15826         
15827         Roo.each(this.ios_options, function(opts){
15828            
15829            opts.el.dom.removeAttribute('selected');
15830            
15831            if(opts.data[this.valueField] != v){
15832                return;
15833            }
15834            
15835            opts.el.dom.setAttribute('selected', true);
15836            
15837         }, this);
15838     }
15839
15840     /** 
15841     * @cfg {Boolean} grow 
15842     * @hide 
15843     */
15844     /** 
15845     * @cfg {Number} growMin 
15846     * @hide 
15847     */
15848     /** 
15849     * @cfg {Number} growMax 
15850     * @hide 
15851     */
15852     /**
15853      * @hide
15854      * @method autoSize
15855      */
15856 });
15857
15858 Roo.apply(Roo.bootstrap.ComboBox,  {
15859     
15860     header : {
15861         tag: 'div',
15862         cls: 'modal-header',
15863         cn: [
15864             {
15865                 tag: 'h4',
15866                 cls: 'modal-title'
15867             }
15868         ]
15869     },
15870     
15871     body : {
15872         tag: 'div',
15873         cls: 'modal-body',
15874         cn: [
15875             {
15876                 tag: 'ul',
15877                 cls: 'list-group'
15878             }
15879         ]
15880     },
15881     
15882     listItemRadio : {
15883         tag: 'li',
15884         cls: 'list-group-item',
15885         cn: [
15886             {
15887                 tag: 'span',
15888                 cls: 'roo-combobox-list-group-item-value'
15889             },
15890             {
15891                 tag: 'div',
15892                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15893                 cn: [
15894                     {
15895                         tag: 'input',
15896                         type: 'radio'
15897                     },
15898                     {
15899                         tag: 'label'
15900                     }
15901                 ]
15902             }
15903         ]
15904     },
15905     
15906     listItemCheckbox : {
15907         tag: 'li',
15908         cls: 'list-group-item',
15909         cn: [
15910             {
15911                 tag: 'span',
15912                 cls: 'roo-combobox-list-group-item-value'
15913             },
15914             {
15915                 tag: 'div',
15916                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15917                 cn: [
15918                     {
15919                         tag: 'input',
15920                         type: 'checkbox'
15921                     },
15922                     {
15923                         tag: 'label'
15924                     }
15925                 ]
15926             }
15927         ]
15928     },
15929     
15930     emptyResult : {
15931         tag: 'div',
15932         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15933     },
15934     
15935     footer : {
15936         tag: 'div',
15937         cls: 'modal-footer',
15938         cn: [
15939             {
15940                 tag: 'div',
15941                 cls: 'row',
15942                 cn: [
15943                     {
15944                         tag: 'div',
15945                         cls: 'col-xs-6 text-left',
15946                         cn: {
15947                             tag: 'button',
15948                             cls: 'btn btn-danger roo-touch-view-cancel',
15949                             html: 'Cancel'
15950                         }
15951                     },
15952                     {
15953                         tag: 'div',
15954                         cls: 'col-xs-6 text-right',
15955                         cn: {
15956                             tag: 'button',
15957                             cls: 'btn btn-success roo-touch-view-ok',
15958                             html: 'OK'
15959                         }
15960                     }
15961                 ]
15962             }
15963         ]
15964         
15965     }
15966 });
15967
15968 Roo.apply(Roo.bootstrap.ComboBox,  {
15969     
15970     touchViewTemplate : {
15971         tag: 'div',
15972         cls: 'modal fade roo-combobox-touch-view',
15973         cn: [
15974             {
15975                 tag: 'div',
15976                 cls: 'modal-dialog',
15977                 style : 'position:fixed', // we have to fix position....
15978                 cn: [
15979                     {
15980                         tag: 'div',
15981                         cls: 'modal-content',
15982                         cn: [
15983                             Roo.bootstrap.ComboBox.header,
15984                             Roo.bootstrap.ComboBox.body,
15985                             Roo.bootstrap.ComboBox.footer
15986                         ]
15987                     }
15988                 ]
15989             }
15990         ]
15991     }
15992 });/*
15993  * Based on:
15994  * Ext JS Library 1.1.1
15995  * Copyright(c) 2006-2007, Ext JS, LLC.
15996  *
15997  * Originally Released Under LGPL - original licence link has changed is not relivant.
15998  *
15999  * Fork - LGPL
16000  * <script type="text/javascript">
16001  */
16002
16003 /**
16004  * @class Roo.View
16005  * @extends Roo.util.Observable
16006  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16007  * This class also supports single and multi selection modes. <br>
16008  * Create a data model bound view:
16009  <pre><code>
16010  var store = new Roo.data.Store(...);
16011
16012  var view = new Roo.View({
16013     el : "my-element",
16014     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16015  
16016     singleSelect: true,
16017     selectedClass: "ydataview-selected",
16018     store: store
16019  });
16020
16021  // listen for node click?
16022  view.on("click", function(vw, index, node, e){
16023  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16024  });
16025
16026  // load XML data
16027  dataModel.load("foobar.xml");
16028  </code></pre>
16029  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16030  * <br><br>
16031  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16032  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16033  * 
16034  * Note: old style constructor is still suported (container, template, config)
16035  * 
16036  * @constructor
16037  * Create a new View
16038  * @param {Object} config The config object
16039  * 
16040  */
16041 Roo.View = function(config, depreciated_tpl, depreciated_config){
16042     
16043     this.parent = false;
16044     
16045     if (typeof(depreciated_tpl) == 'undefined') {
16046         // new way.. - universal constructor.
16047         Roo.apply(this, config);
16048         this.el  = Roo.get(this.el);
16049     } else {
16050         // old format..
16051         this.el  = Roo.get(config);
16052         this.tpl = depreciated_tpl;
16053         Roo.apply(this, depreciated_config);
16054     }
16055     this.wrapEl  = this.el.wrap().wrap();
16056     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16057     
16058     
16059     if(typeof(this.tpl) == "string"){
16060         this.tpl = new Roo.Template(this.tpl);
16061     } else {
16062         // support xtype ctors..
16063         this.tpl = new Roo.factory(this.tpl, Roo);
16064     }
16065     
16066     
16067     this.tpl.compile();
16068     
16069     /** @private */
16070     this.addEvents({
16071         /**
16072          * @event beforeclick
16073          * Fires before a click is processed. Returns false to cancel the default action.
16074          * @param {Roo.View} this
16075          * @param {Number} index The index of the target node
16076          * @param {HTMLElement} node The target node
16077          * @param {Roo.EventObject} e The raw event object
16078          */
16079             "beforeclick" : true,
16080         /**
16081          * @event click
16082          * Fires when a template node is clicked.
16083          * @param {Roo.View} this
16084          * @param {Number} index The index of the target node
16085          * @param {HTMLElement} node The target node
16086          * @param {Roo.EventObject} e The raw event object
16087          */
16088             "click" : true,
16089         /**
16090          * @event dblclick
16091          * Fires when a template node is double clicked.
16092          * @param {Roo.View} this
16093          * @param {Number} index The index of the target node
16094          * @param {HTMLElement} node The target node
16095          * @param {Roo.EventObject} e The raw event object
16096          */
16097             "dblclick" : true,
16098         /**
16099          * @event contextmenu
16100          * Fires when a template node is right clicked.
16101          * @param {Roo.View} this
16102          * @param {Number} index The index of the target node
16103          * @param {HTMLElement} node The target node
16104          * @param {Roo.EventObject} e The raw event object
16105          */
16106             "contextmenu" : true,
16107         /**
16108          * @event selectionchange
16109          * Fires when the selected nodes change.
16110          * @param {Roo.View} this
16111          * @param {Array} selections Array of the selected nodes
16112          */
16113             "selectionchange" : true,
16114     
16115         /**
16116          * @event beforeselect
16117          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16118          * @param {Roo.View} this
16119          * @param {HTMLElement} node The node to be selected
16120          * @param {Array} selections Array of currently selected nodes
16121          */
16122             "beforeselect" : true,
16123         /**
16124          * @event preparedata
16125          * Fires on every row to render, to allow you to change the data.
16126          * @param {Roo.View} this
16127          * @param {Object} data to be rendered (change this)
16128          */
16129           "preparedata" : true
16130           
16131           
16132         });
16133
16134
16135
16136     this.el.on({
16137         "click": this.onClick,
16138         "dblclick": this.onDblClick,
16139         "contextmenu": this.onContextMenu,
16140         scope:this
16141     });
16142
16143     this.selections = [];
16144     this.nodes = [];
16145     this.cmp = new Roo.CompositeElementLite([]);
16146     if(this.store){
16147         this.store = Roo.factory(this.store, Roo.data);
16148         this.setStore(this.store, true);
16149     }
16150     
16151     if ( this.footer && this.footer.xtype) {
16152            
16153          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16154         
16155         this.footer.dataSource = this.store;
16156         this.footer.container = fctr;
16157         this.footer = Roo.factory(this.footer, Roo);
16158         fctr.insertFirst(this.el);
16159         
16160         // this is a bit insane - as the paging toolbar seems to detach the el..
16161 //        dom.parentNode.parentNode.parentNode
16162          // they get detached?
16163     }
16164     
16165     
16166     Roo.View.superclass.constructor.call(this);
16167     
16168     
16169 };
16170
16171 Roo.extend(Roo.View, Roo.util.Observable, {
16172     
16173      /**
16174      * @cfg {Roo.data.Store} store Data store to load data from.
16175      */
16176     store : false,
16177     
16178     /**
16179      * @cfg {String|Roo.Element} el The container element.
16180      */
16181     el : '',
16182     
16183     /**
16184      * @cfg {String|Roo.Template} tpl The template used by this View 
16185      */
16186     tpl : false,
16187     /**
16188      * @cfg {String} dataName the named area of the template to use as the data area
16189      *                          Works with domtemplates roo-name="name"
16190      */
16191     dataName: false,
16192     /**
16193      * @cfg {String} selectedClass The css class to add to selected nodes
16194      */
16195     selectedClass : "x-view-selected",
16196      /**
16197      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16198      */
16199     emptyText : "",
16200     
16201     /**
16202      * @cfg {String} text to display on mask (default Loading)
16203      */
16204     mask : false,
16205     /**
16206      * @cfg {Boolean} multiSelect Allow multiple selection
16207      */
16208     multiSelect : false,
16209     /**
16210      * @cfg {Boolean} singleSelect Allow single selection
16211      */
16212     singleSelect:  false,
16213     
16214     /**
16215      * @cfg {Boolean} toggleSelect - selecting 
16216      */
16217     toggleSelect : false,
16218     
16219     /**
16220      * @cfg {Boolean} tickable - selecting 
16221      */
16222     tickable : false,
16223     
16224     /**
16225      * Returns the element this view is bound to.
16226      * @return {Roo.Element}
16227      */
16228     getEl : function(){
16229         return this.wrapEl;
16230     },
16231     
16232     
16233
16234     /**
16235      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16236      */
16237     refresh : function(){
16238         //Roo.log('refresh');
16239         var t = this.tpl;
16240         
16241         // if we are using something like 'domtemplate', then
16242         // the what gets used is:
16243         // t.applySubtemplate(NAME, data, wrapping data..)
16244         // the outer template then get' applied with
16245         //     the store 'extra data'
16246         // and the body get's added to the
16247         //      roo-name="data" node?
16248         //      <span class='roo-tpl-{name}'></span> ?????
16249         
16250         
16251         
16252         this.clearSelections();
16253         this.el.update("");
16254         var html = [];
16255         var records = this.store.getRange();
16256         if(records.length < 1) {
16257             
16258             // is this valid??  = should it render a template??
16259             
16260             this.el.update(this.emptyText);
16261             return;
16262         }
16263         var el = this.el;
16264         if (this.dataName) {
16265             this.el.update(t.apply(this.store.meta)); //????
16266             el = this.el.child('.roo-tpl-' + this.dataName);
16267         }
16268         
16269         for(var i = 0, len = records.length; i < len; i++){
16270             var data = this.prepareData(records[i].data, i, records[i]);
16271             this.fireEvent("preparedata", this, data, i, records[i]);
16272             
16273             var d = Roo.apply({}, data);
16274             
16275             if(this.tickable){
16276                 Roo.apply(d, {'roo-id' : Roo.id()});
16277                 
16278                 var _this = this;
16279             
16280                 Roo.each(this.parent.item, function(item){
16281                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16282                         return;
16283                     }
16284                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16285                 });
16286             }
16287             
16288             html[html.length] = Roo.util.Format.trim(
16289                 this.dataName ?
16290                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16291                     t.apply(d)
16292             );
16293         }
16294         
16295         
16296         
16297         el.update(html.join(""));
16298         this.nodes = el.dom.childNodes;
16299         this.updateIndexes(0);
16300     },
16301     
16302
16303     /**
16304      * Function to override to reformat the data that is sent to
16305      * the template for each node.
16306      * DEPRICATED - use the preparedata event handler.
16307      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16308      * a JSON object for an UpdateManager bound view).
16309      */
16310     prepareData : function(data, index, record)
16311     {
16312         this.fireEvent("preparedata", this, data, index, record);
16313         return data;
16314     },
16315
16316     onUpdate : function(ds, record){
16317         // Roo.log('on update');   
16318         this.clearSelections();
16319         var index = this.store.indexOf(record);
16320         var n = this.nodes[index];
16321         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16322         n.parentNode.removeChild(n);
16323         this.updateIndexes(index, index);
16324     },
16325
16326     
16327     
16328 // --------- FIXME     
16329     onAdd : function(ds, records, index)
16330     {
16331         //Roo.log(['on Add', ds, records, index] );        
16332         this.clearSelections();
16333         if(this.nodes.length == 0){
16334             this.refresh();
16335             return;
16336         }
16337         var n = this.nodes[index];
16338         for(var i = 0, len = records.length; i < len; i++){
16339             var d = this.prepareData(records[i].data, i, records[i]);
16340             if(n){
16341                 this.tpl.insertBefore(n, d);
16342             }else{
16343                 
16344                 this.tpl.append(this.el, d);
16345             }
16346         }
16347         this.updateIndexes(index);
16348     },
16349
16350     onRemove : function(ds, record, index){
16351        // Roo.log('onRemove');
16352         this.clearSelections();
16353         var el = this.dataName  ?
16354             this.el.child('.roo-tpl-' + this.dataName) :
16355             this.el; 
16356         
16357         el.dom.removeChild(this.nodes[index]);
16358         this.updateIndexes(index);
16359     },
16360
16361     /**
16362      * Refresh an individual node.
16363      * @param {Number} index
16364      */
16365     refreshNode : function(index){
16366         this.onUpdate(this.store, this.store.getAt(index));
16367     },
16368
16369     updateIndexes : function(startIndex, endIndex){
16370         var ns = this.nodes;
16371         startIndex = startIndex || 0;
16372         endIndex = endIndex || ns.length - 1;
16373         for(var i = startIndex; i <= endIndex; i++){
16374             ns[i].nodeIndex = i;
16375         }
16376     },
16377
16378     /**
16379      * Changes the data store this view uses and refresh the view.
16380      * @param {Store} store
16381      */
16382     setStore : function(store, initial){
16383         if(!initial && this.store){
16384             this.store.un("datachanged", this.refresh);
16385             this.store.un("add", this.onAdd);
16386             this.store.un("remove", this.onRemove);
16387             this.store.un("update", this.onUpdate);
16388             this.store.un("clear", this.refresh);
16389             this.store.un("beforeload", this.onBeforeLoad);
16390             this.store.un("load", this.onLoad);
16391             this.store.un("loadexception", this.onLoad);
16392         }
16393         if(store){
16394           
16395             store.on("datachanged", this.refresh, this);
16396             store.on("add", this.onAdd, this);
16397             store.on("remove", this.onRemove, this);
16398             store.on("update", this.onUpdate, this);
16399             store.on("clear", this.refresh, this);
16400             store.on("beforeload", this.onBeforeLoad, this);
16401             store.on("load", this.onLoad, this);
16402             store.on("loadexception", this.onLoad, this);
16403         }
16404         
16405         if(store){
16406             this.refresh();
16407         }
16408     },
16409     /**
16410      * onbeforeLoad - masks the loading area.
16411      *
16412      */
16413     onBeforeLoad : function(store,opts)
16414     {
16415          //Roo.log('onBeforeLoad');   
16416         if (!opts.add) {
16417             this.el.update("");
16418         }
16419         this.el.mask(this.mask ? this.mask : "Loading" ); 
16420     },
16421     onLoad : function ()
16422     {
16423         this.el.unmask();
16424     },
16425     
16426
16427     /**
16428      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16429      * @param {HTMLElement} node
16430      * @return {HTMLElement} The template node
16431      */
16432     findItemFromChild : function(node){
16433         var el = this.dataName  ?
16434             this.el.child('.roo-tpl-' + this.dataName,true) :
16435             this.el.dom; 
16436         
16437         if(!node || node.parentNode == el){
16438                     return node;
16439             }
16440             var p = node.parentNode;
16441             while(p && p != el){
16442             if(p.parentNode == el){
16443                 return p;
16444             }
16445             p = p.parentNode;
16446         }
16447             return null;
16448     },
16449
16450     /** @ignore */
16451     onClick : function(e){
16452         var item = this.findItemFromChild(e.getTarget());
16453         if(item){
16454             var index = this.indexOf(item);
16455             if(this.onItemClick(item, index, e) !== false){
16456                 this.fireEvent("click", this, index, item, e);
16457             }
16458         }else{
16459             this.clearSelections();
16460         }
16461     },
16462
16463     /** @ignore */
16464     onContextMenu : function(e){
16465         var item = this.findItemFromChild(e.getTarget());
16466         if(item){
16467             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16468         }
16469     },
16470
16471     /** @ignore */
16472     onDblClick : function(e){
16473         var item = this.findItemFromChild(e.getTarget());
16474         if(item){
16475             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16476         }
16477     },
16478
16479     onItemClick : function(item, index, e)
16480     {
16481         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16482             return false;
16483         }
16484         if (this.toggleSelect) {
16485             var m = this.isSelected(item) ? 'unselect' : 'select';
16486             //Roo.log(m);
16487             var _t = this;
16488             _t[m](item, true, false);
16489             return true;
16490         }
16491         if(this.multiSelect || this.singleSelect){
16492             if(this.multiSelect && e.shiftKey && this.lastSelection){
16493                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16494             }else{
16495                 this.select(item, this.multiSelect && e.ctrlKey);
16496                 this.lastSelection = item;
16497             }
16498             
16499             if(!this.tickable){
16500                 e.preventDefault();
16501             }
16502             
16503         }
16504         return true;
16505     },
16506
16507     /**
16508      * Get the number of selected nodes.
16509      * @return {Number}
16510      */
16511     getSelectionCount : function(){
16512         return this.selections.length;
16513     },
16514
16515     /**
16516      * Get the currently selected nodes.
16517      * @return {Array} An array of HTMLElements
16518      */
16519     getSelectedNodes : function(){
16520         return this.selections;
16521     },
16522
16523     /**
16524      * Get the indexes of the selected nodes.
16525      * @return {Array}
16526      */
16527     getSelectedIndexes : function(){
16528         var indexes = [], s = this.selections;
16529         for(var i = 0, len = s.length; i < len; i++){
16530             indexes.push(s[i].nodeIndex);
16531         }
16532         return indexes;
16533     },
16534
16535     /**
16536      * Clear all selections
16537      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16538      */
16539     clearSelections : function(suppressEvent){
16540         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16541             this.cmp.elements = this.selections;
16542             this.cmp.removeClass(this.selectedClass);
16543             this.selections = [];
16544             if(!suppressEvent){
16545                 this.fireEvent("selectionchange", this, this.selections);
16546             }
16547         }
16548     },
16549
16550     /**
16551      * Returns true if the passed node is selected
16552      * @param {HTMLElement/Number} node The node or node index
16553      * @return {Boolean}
16554      */
16555     isSelected : function(node){
16556         var s = this.selections;
16557         if(s.length < 1){
16558             return false;
16559         }
16560         node = this.getNode(node);
16561         return s.indexOf(node) !== -1;
16562     },
16563
16564     /**
16565      * Selects nodes.
16566      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16567      * @param {Boolean} keepExisting (optional) true to keep existing selections
16568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16569      */
16570     select : function(nodeInfo, keepExisting, suppressEvent){
16571         if(nodeInfo instanceof Array){
16572             if(!keepExisting){
16573                 this.clearSelections(true);
16574             }
16575             for(var i = 0, len = nodeInfo.length; i < len; i++){
16576                 this.select(nodeInfo[i], true, true);
16577             }
16578             return;
16579         } 
16580         var node = this.getNode(nodeInfo);
16581         if(!node || this.isSelected(node)){
16582             return; // already selected.
16583         }
16584         if(!keepExisting){
16585             this.clearSelections(true);
16586         }
16587         
16588         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16589             Roo.fly(node).addClass(this.selectedClass);
16590             this.selections.push(node);
16591             if(!suppressEvent){
16592                 this.fireEvent("selectionchange", this, this.selections);
16593             }
16594         }
16595         
16596         
16597     },
16598       /**
16599      * Unselects nodes.
16600      * @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
16601      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16602      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16603      */
16604     unselect : function(nodeInfo, keepExisting, suppressEvent)
16605     {
16606         if(nodeInfo instanceof Array){
16607             Roo.each(this.selections, function(s) {
16608                 this.unselect(s, nodeInfo);
16609             }, this);
16610             return;
16611         }
16612         var node = this.getNode(nodeInfo);
16613         if(!node || !this.isSelected(node)){
16614             //Roo.log("not selected");
16615             return; // not selected.
16616         }
16617         // fireevent???
16618         var ns = [];
16619         Roo.each(this.selections, function(s) {
16620             if (s == node ) {
16621                 Roo.fly(node).removeClass(this.selectedClass);
16622
16623                 return;
16624             }
16625             ns.push(s);
16626         },this);
16627         
16628         this.selections= ns;
16629         this.fireEvent("selectionchange", this, this.selections);
16630     },
16631
16632     /**
16633      * Gets a template node.
16634      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16635      * @return {HTMLElement} The node or null if it wasn't found
16636      */
16637     getNode : function(nodeInfo){
16638         if(typeof nodeInfo == "string"){
16639             return document.getElementById(nodeInfo);
16640         }else if(typeof nodeInfo == "number"){
16641             return this.nodes[nodeInfo];
16642         }
16643         return nodeInfo;
16644     },
16645
16646     /**
16647      * Gets a range template nodes.
16648      * @param {Number} startIndex
16649      * @param {Number} endIndex
16650      * @return {Array} An array of nodes
16651      */
16652     getNodes : function(start, end){
16653         var ns = this.nodes;
16654         start = start || 0;
16655         end = typeof end == "undefined" ? ns.length - 1 : end;
16656         var nodes = [];
16657         if(start <= end){
16658             for(var i = start; i <= end; i++){
16659                 nodes.push(ns[i]);
16660             }
16661         } else{
16662             for(var i = start; i >= end; i--){
16663                 nodes.push(ns[i]);
16664             }
16665         }
16666         return nodes;
16667     },
16668
16669     /**
16670      * Finds the index of the passed node
16671      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16672      * @return {Number} The index of the node or -1
16673      */
16674     indexOf : function(node){
16675         node = this.getNode(node);
16676         if(typeof node.nodeIndex == "number"){
16677             return node.nodeIndex;
16678         }
16679         var ns = this.nodes;
16680         for(var i = 0, len = ns.length; i < len; i++){
16681             if(ns[i] == node){
16682                 return i;
16683             }
16684         }
16685         return -1;
16686     }
16687 });
16688 /*
16689  * - LGPL
16690  *
16691  * based on jquery fullcalendar
16692  * 
16693  */
16694
16695 Roo.bootstrap = Roo.bootstrap || {};
16696 /**
16697  * @class Roo.bootstrap.Calendar
16698  * @extends Roo.bootstrap.Component
16699  * Bootstrap Calendar class
16700  * @cfg {Boolean} loadMask (true|false) default false
16701  * @cfg {Object} header generate the user specific header of the calendar, default false
16702
16703  * @constructor
16704  * Create a new Container
16705  * @param {Object} config The config object
16706  */
16707
16708
16709
16710 Roo.bootstrap.Calendar = function(config){
16711     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16712      this.addEvents({
16713         /**
16714              * @event select
16715              * Fires when a date is selected
16716              * @param {DatePicker} this
16717              * @param {Date} date The selected date
16718              */
16719         'select': true,
16720         /**
16721              * @event monthchange
16722              * Fires when the displayed month changes 
16723              * @param {DatePicker} this
16724              * @param {Date} date The selected month
16725              */
16726         'monthchange': true,
16727         /**
16728              * @event evententer
16729              * Fires when mouse over an event
16730              * @param {Calendar} this
16731              * @param {event} Event
16732              */
16733         'evententer': true,
16734         /**
16735              * @event eventleave
16736              * Fires when the mouse leaves an
16737              * @param {Calendar} this
16738              * @param {event}
16739              */
16740         'eventleave': true,
16741         /**
16742              * @event eventclick
16743              * Fires when the mouse click an
16744              * @param {Calendar} this
16745              * @param {event}
16746              */
16747         'eventclick': true
16748         
16749     });
16750
16751 };
16752
16753 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16754     
16755      /**
16756      * @cfg {Number} startDay
16757      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16758      */
16759     startDay : 0,
16760     
16761     loadMask : false,
16762     
16763     header : false,
16764       
16765     getAutoCreate : function(){
16766         
16767         
16768         var fc_button = function(name, corner, style, content ) {
16769             return Roo.apply({},{
16770                 tag : 'span',
16771                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16772                          (corner.length ?
16773                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16774                             ''
16775                         ),
16776                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16777                 unselectable: 'on'
16778             });
16779         };
16780         
16781         var header = {};
16782         
16783         if(!this.header){
16784             header = {
16785                 tag : 'table',
16786                 cls : 'fc-header',
16787                 style : 'width:100%',
16788                 cn : [
16789                     {
16790                         tag: 'tr',
16791                         cn : [
16792                             {
16793                                 tag : 'td',
16794                                 cls : 'fc-header-left',
16795                                 cn : [
16796                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16797                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16798                                     { tag: 'span', cls: 'fc-header-space' },
16799                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16800
16801
16802                                 ]
16803                             },
16804
16805                             {
16806                                 tag : 'td',
16807                                 cls : 'fc-header-center',
16808                                 cn : [
16809                                     {
16810                                         tag: 'span',
16811                                         cls: 'fc-header-title',
16812                                         cn : {
16813                                             tag: 'H2',
16814                                             html : 'month / year'
16815                                         }
16816                                     }
16817
16818                                 ]
16819                             },
16820                             {
16821                                 tag : 'td',
16822                                 cls : 'fc-header-right',
16823                                 cn : [
16824                               /*      fc_button('month', 'left', '', 'month' ),
16825                                     fc_button('week', '', '', 'week' ),
16826                                     fc_button('day', 'right', '', 'day' )
16827                                 */    
16828
16829                                 ]
16830                             }
16831
16832                         ]
16833                     }
16834                 ]
16835             };
16836         }
16837         
16838         header = this.header;
16839         
16840        
16841         var cal_heads = function() {
16842             var ret = [];
16843             // fixme - handle this.
16844             
16845             for (var i =0; i < Date.dayNames.length; i++) {
16846                 var d = Date.dayNames[i];
16847                 ret.push({
16848                     tag: 'th',
16849                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16850                     html : d.substring(0,3)
16851                 });
16852                 
16853             }
16854             ret[0].cls += ' fc-first';
16855             ret[6].cls += ' fc-last';
16856             return ret;
16857         };
16858         var cal_cell = function(n) {
16859             return  {
16860                 tag: 'td',
16861                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16862                 cn : [
16863                     {
16864                         cn : [
16865                             {
16866                                 cls: 'fc-day-number',
16867                                 html: 'D'
16868                             },
16869                             {
16870                                 cls: 'fc-day-content',
16871                              
16872                                 cn : [
16873                                      {
16874                                         style: 'position: relative;' // height: 17px;
16875                                     }
16876                                 ]
16877                             }
16878                             
16879                             
16880                         ]
16881                     }
16882                 ]
16883                 
16884             }
16885         };
16886         var cal_rows = function() {
16887             
16888             var ret = [];
16889             for (var r = 0; r < 6; r++) {
16890                 var row= {
16891                     tag : 'tr',
16892                     cls : 'fc-week',
16893                     cn : []
16894                 };
16895                 
16896                 for (var i =0; i < Date.dayNames.length; i++) {
16897                     var d = Date.dayNames[i];
16898                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16899
16900                 }
16901                 row.cn[0].cls+=' fc-first';
16902                 row.cn[0].cn[0].style = 'min-height:90px';
16903                 row.cn[6].cls+=' fc-last';
16904                 ret.push(row);
16905                 
16906             }
16907             ret[0].cls += ' fc-first';
16908             ret[4].cls += ' fc-prev-last';
16909             ret[5].cls += ' fc-last';
16910             return ret;
16911             
16912         };
16913         
16914         var cal_table = {
16915             tag: 'table',
16916             cls: 'fc-border-separate',
16917             style : 'width:100%',
16918             cellspacing  : 0,
16919             cn : [
16920                 { 
16921                     tag: 'thead',
16922                     cn : [
16923                         { 
16924                             tag: 'tr',
16925                             cls : 'fc-first fc-last',
16926                             cn : cal_heads()
16927                         }
16928                     ]
16929                 },
16930                 { 
16931                     tag: 'tbody',
16932                     cn : cal_rows()
16933                 }
16934                   
16935             ]
16936         };
16937          
16938          var cfg = {
16939             cls : 'fc fc-ltr',
16940             cn : [
16941                 header,
16942                 {
16943                     cls : 'fc-content',
16944                     style : "position: relative;",
16945                     cn : [
16946                         {
16947                             cls : 'fc-view fc-view-month fc-grid',
16948                             style : 'position: relative',
16949                             unselectable : 'on',
16950                             cn : [
16951                                 {
16952                                     cls : 'fc-event-container',
16953                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16954                                 },
16955                                 cal_table
16956                             ]
16957                         }
16958                     ]
16959     
16960                 }
16961            ] 
16962             
16963         };
16964         
16965          
16966         
16967         return cfg;
16968     },
16969     
16970     
16971     initEvents : function()
16972     {
16973         if(!this.store){
16974             throw "can not find store for calendar";
16975         }
16976         
16977         var mark = {
16978             tag: "div",
16979             cls:"x-dlg-mask",
16980             style: "text-align:center",
16981             cn: [
16982                 {
16983                     tag: "div",
16984                     style: "background-color:white;width:50%;margin:250 auto",
16985                     cn: [
16986                         {
16987                             tag: "img",
16988                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16989                         },
16990                         {
16991                             tag: "span",
16992                             html: "Loading"
16993                         }
16994                         
16995                     ]
16996                 }
16997             ]
16998         };
16999         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17000         
17001         var size = this.el.select('.fc-content', true).first().getSize();
17002         this.maskEl.setSize(size.width, size.height);
17003         this.maskEl.enableDisplayMode("block");
17004         if(!this.loadMask){
17005             this.maskEl.hide();
17006         }
17007         
17008         this.store = Roo.factory(this.store, Roo.data);
17009         this.store.on('load', this.onLoad, this);
17010         this.store.on('beforeload', this.onBeforeLoad, this);
17011         
17012         this.resize();
17013         
17014         this.cells = this.el.select('.fc-day',true);
17015         //Roo.log(this.cells);
17016         this.textNodes = this.el.query('.fc-day-number');
17017         this.cells.addClassOnOver('fc-state-hover');
17018         
17019         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17020         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17021         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17022         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17023         
17024         this.on('monthchange', this.onMonthChange, this);
17025         
17026         this.update(new Date().clearTime());
17027     },
17028     
17029     resize : function() {
17030         var sz  = this.el.getSize();
17031         
17032         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17033         this.el.select('.fc-day-content div',true).setHeight(34);
17034     },
17035     
17036     
17037     // private
17038     showPrevMonth : function(e){
17039         this.update(this.activeDate.add("mo", -1));
17040     },
17041     showToday : function(e){
17042         this.update(new Date().clearTime());
17043     },
17044     // private
17045     showNextMonth : function(e){
17046         this.update(this.activeDate.add("mo", 1));
17047     },
17048
17049     // private
17050     showPrevYear : function(){
17051         this.update(this.activeDate.add("y", -1));
17052     },
17053
17054     // private
17055     showNextYear : function(){
17056         this.update(this.activeDate.add("y", 1));
17057     },
17058
17059     
17060    // private
17061     update : function(date)
17062     {
17063         var vd = this.activeDate;
17064         this.activeDate = date;
17065 //        if(vd && this.el){
17066 //            var t = date.getTime();
17067 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17068 //                Roo.log('using add remove');
17069 //                
17070 //                this.fireEvent('monthchange', this, date);
17071 //                
17072 //                this.cells.removeClass("fc-state-highlight");
17073 //                this.cells.each(function(c){
17074 //                   if(c.dateValue == t){
17075 //                       c.addClass("fc-state-highlight");
17076 //                       setTimeout(function(){
17077 //                            try{c.dom.firstChild.focus();}catch(e){}
17078 //                       }, 50);
17079 //                       return false;
17080 //                   }
17081 //                   return true;
17082 //                });
17083 //                return;
17084 //            }
17085 //        }
17086         
17087         var days = date.getDaysInMonth();
17088         
17089         var firstOfMonth = date.getFirstDateOfMonth();
17090         var startingPos = firstOfMonth.getDay()-this.startDay;
17091         
17092         if(startingPos < this.startDay){
17093             startingPos += 7;
17094         }
17095         
17096         var pm = date.add(Date.MONTH, -1);
17097         var prevStart = pm.getDaysInMonth()-startingPos;
17098 //        
17099         this.cells = this.el.select('.fc-day',true);
17100         this.textNodes = this.el.query('.fc-day-number');
17101         this.cells.addClassOnOver('fc-state-hover');
17102         
17103         var cells = this.cells.elements;
17104         var textEls = this.textNodes;
17105         
17106         Roo.each(cells, function(cell){
17107             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17108         });
17109         
17110         days += startingPos;
17111
17112         // convert everything to numbers so it's fast
17113         var day = 86400000;
17114         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17115         //Roo.log(d);
17116         //Roo.log(pm);
17117         //Roo.log(prevStart);
17118         
17119         var today = new Date().clearTime().getTime();
17120         var sel = date.clearTime().getTime();
17121         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17122         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17123         var ddMatch = this.disabledDatesRE;
17124         var ddText = this.disabledDatesText;
17125         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17126         var ddaysText = this.disabledDaysText;
17127         var format = this.format;
17128         
17129         var setCellClass = function(cal, cell){
17130             cell.row = 0;
17131             cell.events = [];
17132             cell.more = [];
17133             //Roo.log('set Cell Class');
17134             cell.title = "";
17135             var t = d.getTime();
17136             
17137             //Roo.log(d);
17138             
17139             cell.dateValue = t;
17140             if(t == today){
17141                 cell.className += " fc-today";
17142                 cell.className += " fc-state-highlight";
17143                 cell.title = cal.todayText;
17144             }
17145             if(t == sel){
17146                 // disable highlight in other month..
17147                 //cell.className += " fc-state-highlight";
17148                 
17149             }
17150             // disabling
17151             if(t < min) {
17152                 cell.className = " fc-state-disabled";
17153                 cell.title = cal.minText;
17154                 return;
17155             }
17156             if(t > max) {
17157                 cell.className = " fc-state-disabled";
17158                 cell.title = cal.maxText;
17159                 return;
17160             }
17161             if(ddays){
17162                 if(ddays.indexOf(d.getDay()) != -1){
17163                     cell.title = ddaysText;
17164                     cell.className = " fc-state-disabled";
17165                 }
17166             }
17167             if(ddMatch && format){
17168                 var fvalue = d.dateFormat(format);
17169                 if(ddMatch.test(fvalue)){
17170                     cell.title = ddText.replace("%0", fvalue);
17171                     cell.className = " fc-state-disabled";
17172                 }
17173             }
17174             
17175             if (!cell.initialClassName) {
17176                 cell.initialClassName = cell.dom.className;
17177             }
17178             
17179             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17180         };
17181
17182         var i = 0;
17183         
17184         for(; i < startingPos; i++) {
17185             textEls[i].innerHTML = (++prevStart);
17186             d.setDate(d.getDate()+1);
17187             
17188             cells[i].className = "fc-past fc-other-month";
17189             setCellClass(this, cells[i]);
17190         }
17191         
17192         var intDay = 0;
17193         
17194         for(; i < days; i++){
17195             intDay = i - startingPos + 1;
17196             textEls[i].innerHTML = (intDay);
17197             d.setDate(d.getDate()+1);
17198             
17199             cells[i].className = ''; // "x-date-active";
17200             setCellClass(this, cells[i]);
17201         }
17202         var extraDays = 0;
17203         
17204         for(; i < 42; i++) {
17205             textEls[i].innerHTML = (++extraDays);
17206             d.setDate(d.getDate()+1);
17207             
17208             cells[i].className = "fc-future fc-other-month";
17209             setCellClass(this, cells[i]);
17210         }
17211         
17212         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17213         
17214         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17215         
17216         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17217         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17218         
17219         if(totalRows != 6){
17220             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17221             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17222         }
17223         
17224         this.fireEvent('monthchange', this, date);
17225         
17226         
17227         /*
17228         if(!this.internalRender){
17229             var main = this.el.dom.firstChild;
17230             var w = main.offsetWidth;
17231             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17232             Roo.fly(main).setWidth(w);
17233             this.internalRender = true;
17234             // opera does not respect the auto grow header center column
17235             // then, after it gets a width opera refuses to recalculate
17236             // without a second pass
17237             if(Roo.isOpera && !this.secondPass){
17238                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17239                 this.secondPass = true;
17240                 this.update.defer(10, this, [date]);
17241             }
17242         }
17243         */
17244         
17245     },
17246     
17247     findCell : function(dt) {
17248         dt = dt.clearTime().getTime();
17249         var ret = false;
17250         this.cells.each(function(c){
17251             //Roo.log("check " +c.dateValue + '?=' + dt);
17252             if(c.dateValue == dt){
17253                 ret = c;
17254                 return false;
17255             }
17256             return true;
17257         });
17258         
17259         return ret;
17260     },
17261     
17262     findCells : function(ev) {
17263         var s = ev.start.clone().clearTime().getTime();
17264        // Roo.log(s);
17265         var e= ev.end.clone().clearTime().getTime();
17266        // Roo.log(e);
17267         var ret = [];
17268         this.cells.each(function(c){
17269              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17270             
17271             if(c.dateValue > e){
17272                 return ;
17273             }
17274             if(c.dateValue < s){
17275                 return ;
17276             }
17277             ret.push(c);
17278         });
17279         
17280         return ret;    
17281     },
17282     
17283 //    findBestRow: function(cells)
17284 //    {
17285 //        var ret = 0;
17286 //        
17287 //        for (var i =0 ; i < cells.length;i++) {
17288 //            ret  = Math.max(cells[i].rows || 0,ret);
17289 //        }
17290 //        return ret;
17291 //        
17292 //    },
17293     
17294     
17295     addItem : function(ev)
17296     {
17297         // look for vertical location slot in
17298         var cells = this.findCells(ev);
17299         
17300 //        ev.row = this.findBestRow(cells);
17301         
17302         // work out the location.
17303         
17304         var crow = false;
17305         var rows = [];
17306         for(var i =0; i < cells.length; i++) {
17307             
17308             cells[i].row = cells[0].row;
17309             
17310             if(i == 0){
17311                 cells[i].row = cells[i].row + 1;
17312             }
17313             
17314             if (!crow) {
17315                 crow = {
17316                     start : cells[i],
17317                     end :  cells[i]
17318                 };
17319                 continue;
17320             }
17321             if (crow.start.getY() == cells[i].getY()) {
17322                 // on same row.
17323                 crow.end = cells[i];
17324                 continue;
17325             }
17326             // different row.
17327             rows.push(crow);
17328             crow = {
17329                 start: cells[i],
17330                 end : cells[i]
17331             };
17332             
17333         }
17334         
17335         rows.push(crow);
17336         ev.els = [];
17337         ev.rows = rows;
17338         ev.cells = cells;
17339         
17340         cells[0].events.push(ev);
17341         
17342         this.calevents.push(ev);
17343     },
17344     
17345     clearEvents: function() {
17346         
17347         if(!this.calevents){
17348             return;
17349         }
17350         
17351         Roo.each(this.cells.elements, function(c){
17352             c.row = 0;
17353             c.events = [];
17354             c.more = [];
17355         });
17356         
17357         Roo.each(this.calevents, function(e) {
17358             Roo.each(e.els, function(el) {
17359                 el.un('mouseenter' ,this.onEventEnter, this);
17360                 el.un('mouseleave' ,this.onEventLeave, this);
17361                 el.remove();
17362             },this);
17363         },this);
17364         
17365         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17366             e.remove();
17367         });
17368         
17369     },
17370     
17371     renderEvents: function()
17372     {   
17373         var _this = this;
17374         
17375         this.cells.each(function(c) {
17376             
17377             if(c.row < 5){
17378                 return;
17379             }
17380             
17381             var ev = c.events;
17382             
17383             var r = 4;
17384             if(c.row != c.events.length){
17385                 r = 4 - (4 - (c.row - c.events.length));
17386             }
17387             
17388             c.events = ev.slice(0, r);
17389             c.more = ev.slice(r);
17390             
17391             if(c.more.length && c.more.length == 1){
17392                 c.events.push(c.more.pop());
17393             }
17394             
17395             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17396             
17397         });
17398             
17399         this.cells.each(function(c) {
17400             
17401             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17402             
17403             
17404             for (var e = 0; e < c.events.length; e++){
17405                 var ev = c.events[e];
17406                 var rows = ev.rows;
17407                 
17408                 for(var i = 0; i < rows.length; i++) {
17409                 
17410                     // how many rows should it span..
17411
17412                     var  cfg = {
17413                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17414                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17415
17416                         unselectable : "on",
17417                         cn : [
17418                             {
17419                                 cls: 'fc-event-inner',
17420                                 cn : [
17421     //                                {
17422     //                                  tag:'span',
17423     //                                  cls: 'fc-event-time',
17424     //                                  html : cells.length > 1 ? '' : ev.time
17425     //                                },
17426                                     {
17427                                       tag:'span',
17428                                       cls: 'fc-event-title',
17429                                       html : String.format('{0}', ev.title)
17430                                     }
17431
17432
17433                                 ]
17434                             },
17435                             {
17436                                 cls: 'ui-resizable-handle ui-resizable-e',
17437                                 html : '&nbsp;&nbsp;&nbsp'
17438                             }
17439
17440                         ]
17441                     };
17442
17443                     if (i == 0) {
17444                         cfg.cls += ' fc-event-start';
17445                     }
17446                     if ((i+1) == rows.length) {
17447                         cfg.cls += ' fc-event-end';
17448                     }
17449
17450                     var ctr = _this.el.select('.fc-event-container',true).first();
17451                     var cg = ctr.createChild(cfg);
17452
17453                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17454                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17455
17456                     var r = (c.more.length) ? 1 : 0;
17457                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17458                     cg.setWidth(ebox.right - sbox.x -2);
17459
17460                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17461                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17462                     cg.on('click', _this.onEventClick, _this, ev);
17463
17464                     ev.els.push(cg);
17465                     
17466                 }
17467                 
17468             }
17469             
17470             
17471             if(c.more.length){
17472                 var  cfg = {
17473                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17474                     style : 'position: absolute',
17475                     unselectable : "on",
17476                     cn : [
17477                         {
17478                             cls: 'fc-event-inner',
17479                             cn : [
17480                                 {
17481                                   tag:'span',
17482                                   cls: 'fc-event-title',
17483                                   html : 'More'
17484                                 }
17485
17486
17487                             ]
17488                         },
17489                         {
17490                             cls: 'ui-resizable-handle ui-resizable-e',
17491                             html : '&nbsp;&nbsp;&nbsp'
17492                         }
17493
17494                     ]
17495                 };
17496
17497                 var ctr = _this.el.select('.fc-event-container',true).first();
17498                 var cg = ctr.createChild(cfg);
17499
17500                 var sbox = c.select('.fc-day-content',true).first().getBox();
17501                 var ebox = c.select('.fc-day-content',true).first().getBox();
17502                 //Roo.log(cg);
17503                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17504                 cg.setWidth(ebox.right - sbox.x -2);
17505
17506                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17507                 
17508             }
17509             
17510         });
17511         
17512         
17513         
17514     },
17515     
17516     onEventEnter: function (e, el,event,d) {
17517         this.fireEvent('evententer', this, el, event);
17518     },
17519     
17520     onEventLeave: function (e, el,event,d) {
17521         this.fireEvent('eventleave', this, el, event);
17522     },
17523     
17524     onEventClick: function (e, el,event,d) {
17525         this.fireEvent('eventclick', this, el, event);
17526     },
17527     
17528     onMonthChange: function () {
17529         this.store.load();
17530     },
17531     
17532     onMoreEventClick: function(e, el, more)
17533     {
17534         var _this = this;
17535         
17536         this.calpopover.placement = 'right';
17537         this.calpopover.setTitle('More');
17538         
17539         this.calpopover.setContent('');
17540         
17541         var ctr = this.calpopover.el.select('.popover-content', true).first();
17542         
17543         Roo.each(more, function(m){
17544             var cfg = {
17545                 cls : 'fc-event-hori fc-event-draggable',
17546                 html : m.title
17547             };
17548             var cg = ctr.createChild(cfg);
17549             
17550             cg.on('click', _this.onEventClick, _this, m);
17551         });
17552         
17553         this.calpopover.show(el);
17554         
17555         
17556     },
17557     
17558     onLoad: function () 
17559     {   
17560         this.calevents = [];
17561         var cal = this;
17562         
17563         if(this.store.getCount() > 0){
17564             this.store.data.each(function(d){
17565                cal.addItem({
17566                     id : d.data.id,
17567                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17568                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17569                     time : d.data.start_time,
17570                     title : d.data.title,
17571                     description : d.data.description,
17572                     venue : d.data.venue
17573                 });
17574             });
17575         }
17576         
17577         this.renderEvents();
17578         
17579         if(this.calevents.length && this.loadMask){
17580             this.maskEl.hide();
17581         }
17582     },
17583     
17584     onBeforeLoad: function()
17585     {
17586         this.clearEvents();
17587         if(this.loadMask){
17588             this.maskEl.show();
17589         }
17590     }
17591 });
17592
17593  
17594  /*
17595  * - LGPL
17596  *
17597  * element
17598  * 
17599  */
17600
17601 /**
17602  * @class Roo.bootstrap.Popover
17603  * @extends Roo.bootstrap.Component
17604  * Bootstrap Popover class
17605  * @cfg {String} html contents of the popover   (or false to use children..)
17606  * @cfg {String} title of popover (or false to hide)
17607  * @cfg {String} placement how it is placed
17608  * @cfg {String} trigger click || hover (or false to trigger manually)
17609  * @cfg {String} over what (parent or false to trigger manually.)
17610  * @cfg {Number} delay - delay before showing
17611  
17612  * @constructor
17613  * Create a new Popover
17614  * @param {Object} config The config object
17615  */
17616
17617 Roo.bootstrap.Popover = function(config){
17618     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17619     
17620     this.addEvents({
17621         // raw events
17622          /**
17623          * @event show
17624          * After the popover show
17625          * 
17626          * @param {Roo.bootstrap.Popover} this
17627          */
17628         "show" : true,
17629         /**
17630          * @event hide
17631          * After the popover hide
17632          * 
17633          * @param {Roo.bootstrap.Popover} this
17634          */
17635         "hide" : true
17636     });
17637 };
17638
17639 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17640     
17641     title: 'Fill in a title',
17642     html: false,
17643     
17644     placement : 'right',
17645     trigger : 'hover', // hover
17646     
17647     delay : 0,
17648     
17649     over: 'parent',
17650     
17651     can_build_overlaid : false,
17652     
17653     getChildContainer : function()
17654     {
17655         return this.el.select('.popover-content',true).first();
17656     },
17657     
17658     getAutoCreate : function(){
17659          
17660         var cfg = {
17661            cls : 'popover roo-dynamic',
17662            style: 'display:block',
17663            cn : [
17664                 {
17665                     cls : 'arrow'
17666                 },
17667                 {
17668                     cls : 'popover-inner',
17669                     cn : [
17670                         {
17671                             tag: 'h3',
17672                             cls: 'popover-title popover-header',
17673                             html : this.title
17674                         },
17675                         {
17676                             cls : 'popover-content popover-body',
17677                             html : this.html
17678                         }
17679                     ]
17680                     
17681                 }
17682            ]
17683         };
17684         
17685         return cfg;
17686     },
17687     setTitle: function(str)
17688     {
17689         this.title = str;
17690         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17691     },
17692     setContent: function(str)
17693     {
17694         this.html = str;
17695         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17696     },
17697     // as it get's added to the bottom of the page.
17698     onRender : function(ct, position)
17699     {
17700         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17701         if(!this.el){
17702             var cfg = Roo.apply({},  this.getAutoCreate());
17703             cfg.id = Roo.id();
17704             
17705             if (this.cls) {
17706                 cfg.cls += ' ' + this.cls;
17707             }
17708             if (this.style) {
17709                 cfg.style = this.style;
17710             }
17711             //Roo.log("adding to ");
17712             this.el = Roo.get(document.body).createChild(cfg, position);
17713 //            Roo.log(this.el);
17714         }
17715         this.initEvents();
17716     },
17717     
17718     initEvents : function()
17719     {
17720         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17721         this.el.enableDisplayMode('block');
17722         this.el.hide();
17723         if (this.over === false) {
17724             return; 
17725         }
17726         if (this.triggers === false) {
17727             return;
17728         }
17729         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17730         var triggers = this.trigger ? this.trigger.split(' ') : [];
17731         Roo.each(triggers, function(trigger) {
17732         
17733             if (trigger == 'click') {
17734                 on_el.on('click', this.toggle, this);
17735             } else if (trigger != 'manual') {
17736                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17737                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17738       
17739                 on_el.on(eventIn  ,this.enter, this);
17740                 on_el.on(eventOut, this.leave, this);
17741             }
17742         }, this);
17743         
17744     },
17745     
17746     
17747     // private
17748     timeout : null,
17749     hoverState : null,
17750     
17751     toggle : function () {
17752         this.hoverState == 'in' ? this.leave() : this.enter();
17753     },
17754     
17755     enter : function () {
17756         
17757         clearTimeout(this.timeout);
17758     
17759         this.hoverState = 'in';
17760     
17761         if (!this.delay || !this.delay.show) {
17762             this.show();
17763             return;
17764         }
17765         var _t = this;
17766         this.timeout = setTimeout(function () {
17767             if (_t.hoverState == 'in') {
17768                 _t.show();
17769             }
17770         }, this.delay.show)
17771     },
17772     
17773     leave : function() {
17774         clearTimeout(this.timeout);
17775     
17776         this.hoverState = 'out';
17777     
17778         if (!this.delay || !this.delay.hide) {
17779             this.hide();
17780             return;
17781         }
17782         var _t = this;
17783         this.timeout = setTimeout(function () {
17784             if (_t.hoverState == 'out') {
17785                 _t.hide();
17786             }
17787         }, this.delay.hide)
17788     },
17789     
17790     show : function (on_el)
17791     {
17792         if (!on_el) {
17793             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17794         }
17795         
17796         // set content.
17797         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17798         if (this.html !== false) {
17799             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17800         }
17801         this.el.removeClass([
17802             'fade','top','bottom', 'left', 'right','in',
17803             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17804         ]);
17805         if (!this.title.length) {
17806             this.el.select('.popover-title',true).hide();
17807         }
17808         
17809         var placement = typeof this.placement == 'function' ?
17810             this.placement.call(this, this.el, on_el) :
17811             this.placement;
17812             
17813         var autoToken = /\s?auto?\s?/i;
17814         var autoPlace = autoToken.test(placement);
17815         if (autoPlace) {
17816             placement = placement.replace(autoToken, '') || 'top';
17817         }
17818         
17819         //this.el.detach()
17820         //this.el.setXY([0,0]);
17821         this.el.show();
17822         this.el.dom.style.display='block';
17823         this.el.addClass(placement);
17824         
17825         //this.el.appendTo(on_el);
17826         
17827         var p = this.getPosition();
17828         var box = this.el.getBox();
17829         
17830         if (autoPlace) {
17831             // fixme..
17832         }
17833         var align = Roo.bootstrap.Popover.alignment[placement];
17834         
17835 //        Roo.log(align);
17836         this.el.alignTo(on_el, align[0],align[1]);
17837         //var arrow = this.el.select('.arrow',true).first();
17838         //arrow.set(align[2], 
17839         
17840         this.el.addClass('in');
17841         
17842         
17843         if (this.el.hasClass('fade')) {
17844             // fade it?
17845         }
17846         
17847         this.hoverState = 'in';
17848         
17849         this.fireEvent('show', this);
17850         
17851     },
17852     hide : function()
17853     {
17854         this.el.setXY([0,0]);
17855         this.el.removeClass('in');
17856         this.el.hide();
17857         this.hoverState = null;
17858         
17859         this.fireEvent('hide', this);
17860     }
17861     
17862 });
17863
17864 Roo.bootstrap.Popover.alignment = {
17865     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17866     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17867     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17868     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17869 };
17870
17871  /*
17872  * - LGPL
17873  *
17874  * Progress
17875  * 
17876  */
17877
17878 /**
17879  * @class Roo.bootstrap.Progress
17880  * @extends Roo.bootstrap.Component
17881  * Bootstrap Progress class
17882  * @cfg {Boolean} striped striped of the progress bar
17883  * @cfg {Boolean} active animated of the progress bar
17884  * 
17885  * 
17886  * @constructor
17887  * Create a new Progress
17888  * @param {Object} config The config object
17889  */
17890
17891 Roo.bootstrap.Progress = function(config){
17892     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17893 };
17894
17895 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17896     
17897     striped : false,
17898     active: false,
17899     
17900     getAutoCreate : function(){
17901         var cfg = {
17902             tag: 'div',
17903             cls: 'progress'
17904         };
17905         
17906         
17907         if(this.striped){
17908             cfg.cls += ' progress-striped';
17909         }
17910       
17911         if(this.active){
17912             cfg.cls += ' active';
17913         }
17914         
17915         
17916         return cfg;
17917     }
17918    
17919 });
17920
17921  
17922
17923  /*
17924  * - LGPL
17925  *
17926  * ProgressBar
17927  * 
17928  */
17929
17930 /**
17931  * @class Roo.bootstrap.ProgressBar
17932  * @extends Roo.bootstrap.Component
17933  * Bootstrap ProgressBar class
17934  * @cfg {Number} aria_valuenow aria-value now
17935  * @cfg {Number} aria_valuemin aria-value min
17936  * @cfg {Number} aria_valuemax aria-value max
17937  * @cfg {String} label label for the progress bar
17938  * @cfg {String} panel (success | info | warning | danger )
17939  * @cfg {String} role role of the progress bar
17940  * @cfg {String} sr_only text
17941  * 
17942  * 
17943  * @constructor
17944  * Create a new ProgressBar
17945  * @param {Object} config The config object
17946  */
17947
17948 Roo.bootstrap.ProgressBar = function(config){
17949     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17950 };
17951
17952 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17953     
17954     aria_valuenow : 0,
17955     aria_valuemin : 0,
17956     aria_valuemax : 100,
17957     label : false,
17958     panel : false,
17959     role : false,
17960     sr_only: false,
17961     
17962     getAutoCreate : function()
17963     {
17964         
17965         var cfg = {
17966             tag: 'div',
17967             cls: 'progress-bar',
17968             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17969         };
17970         
17971         if(this.sr_only){
17972             cfg.cn = {
17973                 tag: 'span',
17974                 cls: 'sr-only',
17975                 html: this.sr_only
17976             }
17977         }
17978         
17979         if(this.role){
17980             cfg.role = this.role;
17981         }
17982         
17983         if(this.aria_valuenow){
17984             cfg['aria-valuenow'] = this.aria_valuenow;
17985         }
17986         
17987         if(this.aria_valuemin){
17988             cfg['aria-valuemin'] = this.aria_valuemin;
17989         }
17990         
17991         if(this.aria_valuemax){
17992             cfg['aria-valuemax'] = this.aria_valuemax;
17993         }
17994         
17995         if(this.label && !this.sr_only){
17996             cfg.html = this.label;
17997         }
17998         
17999         if(this.panel){
18000             cfg.cls += ' progress-bar-' + this.panel;
18001         }
18002         
18003         return cfg;
18004     },
18005     
18006     update : function(aria_valuenow)
18007     {
18008         this.aria_valuenow = aria_valuenow;
18009         
18010         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18011     }
18012    
18013 });
18014
18015  
18016
18017  /*
18018  * - LGPL
18019  *
18020  * column
18021  * 
18022  */
18023
18024 /**
18025  * @class Roo.bootstrap.TabGroup
18026  * @extends Roo.bootstrap.Column
18027  * Bootstrap Column class
18028  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18029  * @cfg {Boolean} carousel true to make the group behave like a carousel
18030  * @cfg {Boolean} bullets show bullets for the panels
18031  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18032  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18033  * @cfg {Boolean} showarrow (true|false) show arrow default true
18034  * 
18035  * @constructor
18036  * Create a new TabGroup
18037  * @param {Object} config The config object
18038  */
18039
18040 Roo.bootstrap.TabGroup = function(config){
18041     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18042     if (!this.navId) {
18043         this.navId = Roo.id();
18044     }
18045     this.tabs = [];
18046     Roo.bootstrap.TabGroup.register(this);
18047     
18048 };
18049
18050 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18051     
18052     carousel : false,
18053     transition : false,
18054     bullets : 0,
18055     timer : 0,
18056     autoslide : false,
18057     slideFn : false,
18058     slideOnTouch : false,
18059     showarrow : true,
18060     
18061     getAutoCreate : function()
18062     {
18063         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18064         
18065         cfg.cls += ' tab-content';
18066         
18067         if (this.carousel) {
18068             cfg.cls += ' carousel slide';
18069             
18070             cfg.cn = [{
18071                cls : 'carousel-inner',
18072                cn : []
18073             }];
18074         
18075             if(this.bullets  && !Roo.isTouch){
18076                 
18077                 var bullets = {
18078                     cls : 'carousel-bullets',
18079                     cn : []
18080                 };
18081                
18082                 if(this.bullets_cls){
18083                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18084                 }
18085                 
18086                 bullets.cn.push({
18087                     cls : 'clear'
18088                 });
18089                 
18090                 cfg.cn[0].cn.push(bullets);
18091             }
18092             
18093             if(this.showarrow){
18094                 cfg.cn[0].cn.push({
18095                     tag : 'div',
18096                     class : 'carousel-arrow',
18097                     cn : [
18098                         {
18099                             tag : 'div',
18100                             class : 'carousel-prev',
18101                             cn : [
18102                                 {
18103                                     tag : 'i',
18104                                     class : 'fa fa-chevron-left'
18105                                 }
18106                             ]
18107                         },
18108                         {
18109                             tag : 'div',
18110                             class : 'carousel-next',
18111                             cn : [
18112                                 {
18113                                     tag : 'i',
18114                                     class : 'fa fa-chevron-right'
18115                                 }
18116                             ]
18117                         }
18118                     ]
18119                 });
18120             }
18121             
18122         }
18123         
18124         return cfg;
18125     },
18126     
18127     initEvents:  function()
18128     {
18129 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18130 //            this.el.on("touchstart", this.onTouchStart, this);
18131 //        }
18132         
18133         if(this.autoslide){
18134             var _this = this;
18135             
18136             this.slideFn = window.setInterval(function() {
18137                 _this.showPanelNext();
18138             }, this.timer);
18139         }
18140         
18141         if(this.showarrow){
18142             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18143             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18144         }
18145         
18146         
18147     },
18148     
18149 //    onTouchStart : function(e, el, o)
18150 //    {
18151 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18152 //            return;
18153 //        }
18154 //        
18155 //        this.showPanelNext();
18156 //    },
18157     
18158     
18159     getChildContainer : function()
18160     {
18161         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18162     },
18163     
18164     /**
18165     * register a Navigation item
18166     * @param {Roo.bootstrap.NavItem} the navitem to add
18167     */
18168     register : function(item)
18169     {
18170         this.tabs.push( item);
18171         item.navId = this.navId; // not really needed..
18172         this.addBullet();
18173     
18174     },
18175     
18176     getActivePanel : function()
18177     {
18178         var r = false;
18179         Roo.each(this.tabs, function(t) {
18180             if (t.active) {
18181                 r = t;
18182                 return false;
18183             }
18184             return null;
18185         });
18186         return r;
18187         
18188     },
18189     getPanelByName : function(n)
18190     {
18191         var r = false;
18192         Roo.each(this.tabs, function(t) {
18193             if (t.tabId == n) {
18194                 r = t;
18195                 return false;
18196             }
18197             return null;
18198         });
18199         return r;
18200     },
18201     indexOfPanel : function(p)
18202     {
18203         var r = false;
18204         Roo.each(this.tabs, function(t,i) {
18205             if (t.tabId == p.tabId) {
18206                 r = i;
18207                 return false;
18208             }
18209             return null;
18210         });
18211         return r;
18212     },
18213     /**
18214      * show a specific panel
18215      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18216      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18217      */
18218     showPanel : function (pan)
18219     {
18220         if(this.transition || typeof(pan) == 'undefined'){
18221             Roo.log("waiting for the transitionend");
18222             return;
18223         }
18224         
18225         if (typeof(pan) == 'number') {
18226             pan = this.tabs[pan];
18227         }
18228         
18229         if (typeof(pan) == 'string') {
18230             pan = this.getPanelByName(pan);
18231         }
18232         
18233         var cur = this.getActivePanel();
18234         
18235         if(!pan || !cur){
18236             Roo.log('pan or acitve pan is undefined');
18237             return false;
18238         }
18239         
18240         if (pan.tabId == this.getActivePanel().tabId) {
18241             return true;
18242         }
18243         
18244         if (false === cur.fireEvent('beforedeactivate')) {
18245             return false;
18246         }
18247         
18248         if(this.bullets > 0 && !Roo.isTouch){
18249             this.setActiveBullet(this.indexOfPanel(pan));
18250         }
18251         
18252         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18253             
18254             this.transition = true;
18255             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18256             var lr = dir == 'next' ? 'left' : 'right';
18257             pan.el.addClass(dir); // or prev
18258             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18259             cur.el.addClass(lr); // or right
18260             pan.el.addClass(lr);
18261             
18262             var _this = this;
18263             cur.el.on('transitionend', function() {
18264                 Roo.log("trans end?");
18265                 
18266                 pan.el.removeClass([lr,dir]);
18267                 pan.setActive(true);
18268                 
18269                 cur.el.removeClass([lr]);
18270                 cur.setActive(false);
18271                 
18272                 _this.transition = false;
18273                 
18274             }, this, { single:  true } );
18275             
18276             return true;
18277         }
18278         
18279         cur.setActive(false);
18280         pan.setActive(true);
18281         
18282         return true;
18283         
18284     },
18285     showPanelNext : function()
18286     {
18287         var i = this.indexOfPanel(this.getActivePanel());
18288         
18289         if (i >= this.tabs.length - 1 && !this.autoslide) {
18290             return;
18291         }
18292         
18293         if (i >= this.tabs.length - 1 && this.autoslide) {
18294             i = -1;
18295         }
18296         
18297         this.showPanel(this.tabs[i+1]);
18298     },
18299     
18300     showPanelPrev : function()
18301     {
18302         var i = this.indexOfPanel(this.getActivePanel());
18303         
18304         if (i  < 1 && !this.autoslide) {
18305             return;
18306         }
18307         
18308         if (i < 1 && this.autoslide) {
18309             i = this.tabs.length;
18310         }
18311         
18312         this.showPanel(this.tabs[i-1]);
18313     },
18314     
18315     
18316     addBullet: function()
18317     {
18318         if(!this.bullets || Roo.isTouch){
18319             return;
18320         }
18321         var ctr = this.el.select('.carousel-bullets',true).first();
18322         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18323         var bullet = ctr.createChild({
18324             cls : 'bullet bullet-' + i
18325         },ctr.dom.lastChild);
18326         
18327         
18328         var _this = this;
18329         
18330         bullet.on('click', (function(e, el, o, ii, t){
18331
18332             e.preventDefault();
18333
18334             this.showPanel(ii);
18335
18336             if(this.autoslide && this.slideFn){
18337                 clearInterval(this.slideFn);
18338                 this.slideFn = window.setInterval(function() {
18339                     _this.showPanelNext();
18340                 }, this.timer);
18341             }
18342
18343         }).createDelegate(this, [i, bullet], true));
18344                 
18345         
18346     },
18347      
18348     setActiveBullet : function(i)
18349     {
18350         if(Roo.isTouch){
18351             return;
18352         }
18353         
18354         Roo.each(this.el.select('.bullet', true).elements, function(el){
18355             el.removeClass('selected');
18356         });
18357
18358         var bullet = this.el.select('.bullet-' + i, true).first();
18359         
18360         if(!bullet){
18361             return;
18362         }
18363         
18364         bullet.addClass('selected');
18365     }
18366     
18367     
18368   
18369 });
18370
18371  
18372
18373  
18374  
18375 Roo.apply(Roo.bootstrap.TabGroup, {
18376     
18377     groups: {},
18378      /**
18379     * register a Navigation Group
18380     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18381     */
18382     register : function(navgrp)
18383     {
18384         this.groups[navgrp.navId] = navgrp;
18385         
18386     },
18387     /**
18388     * fetch a Navigation Group based on the navigation ID
18389     * if one does not exist , it will get created.
18390     * @param {string} the navgroup to add
18391     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18392     */
18393     get: function(navId) {
18394         if (typeof(this.groups[navId]) == 'undefined') {
18395             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18396         }
18397         return this.groups[navId] ;
18398     }
18399     
18400     
18401     
18402 });
18403
18404  /*
18405  * - LGPL
18406  *
18407  * TabPanel
18408  * 
18409  */
18410
18411 /**
18412  * @class Roo.bootstrap.TabPanel
18413  * @extends Roo.bootstrap.Component
18414  * Bootstrap TabPanel class
18415  * @cfg {Boolean} active panel active
18416  * @cfg {String} html panel content
18417  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18418  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18419  * @cfg {String} href click to link..
18420  * 
18421  * 
18422  * @constructor
18423  * Create a new TabPanel
18424  * @param {Object} config The config object
18425  */
18426
18427 Roo.bootstrap.TabPanel = function(config){
18428     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18429     this.addEvents({
18430         /**
18431              * @event changed
18432              * Fires when the active status changes
18433              * @param {Roo.bootstrap.TabPanel} this
18434              * @param {Boolean} state the new state
18435             
18436          */
18437         'changed': true,
18438         /**
18439              * @event beforedeactivate
18440              * Fires before a tab is de-activated - can be used to do validation on a form.
18441              * @param {Roo.bootstrap.TabPanel} this
18442              * @return {Boolean} false if there is an error
18443             
18444          */
18445         'beforedeactivate': true
18446      });
18447     
18448     this.tabId = this.tabId || Roo.id();
18449   
18450 };
18451
18452 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18453     
18454     active: false,
18455     html: false,
18456     tabId: false,
18457     navId : false,
18458     href : '',
18459     
18460     getAutoCreate : function(){
18461         var cfg = {
18462             tag: 'div',
18463             // item is needed for carousel - not sure if it has any effect otherwise
18464             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18465             html: this.html || ''
18466         };
18467         
18468         if(this.active){
18469             cfg.cls += ' active';
18470         }
18471         
18472         if(this.tabId){
18473             cfg.tabId = this.tabId;
18474         }
18475         
18476         
18477         return cfg;
18478     },
18479     
18480     initEvents:  function()
18481     {
18482         var p = this.parent();
18483         
18484         this.navId = this.navId || p.navId;
18485         
18486         if (typeof(this.navId) != 'undefined') {
18487             // not really needed.. but just in case.. parent should be a NavGroup.
18488             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18489             
18490             tg.register(this);
18491             
18492             var i = tg.tabs.length - 1;
18493             
18494             if(this.active && tg.bullets > 0 && i < tg.bullets){
18495                 tg.setActiveBullet(i);
18496             }
18497         }
18498         
18499         this.el.on('click', this.onClick, this);
18500         
18501         if(Roo.isTouch){
18502             this.el.on("touchstart", this.onTouchStart, this);
18503             this.el.on("touchmove", this.onTouchMove, this);
18504             this.el.on("touchend", this.onTouchEnd, this);
18505         }
18506         
18507     },
18508     
18509     onRender : function(ct, position)
18510     {
18511         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18512     },
18513     
18514     setActive : function(state)
18515     {
18516         Roo.log("panel - set active " + this.tabId + "=" + state);
18517         
18518         this.active = state;
18519         if (!state) {
18520             this.el.removeClass('active');
18521             
18522         } else  if (!this.el.hasClass('active')) {
18523             this.el.addClass('active');
18524         }
18525         
18526         this.fireEvent('changed', this, state);
18527     },
18528     
18529     onClick : function(e)
18530     {
18531         e.preventDefault();
18532         
18533         if(!this.href.length){
18534             return;
18535         }
18536         
18537         window.location.href = this.href;
18538     },
18539     
18540     startX : 0,
18541     startY : 0,
18542     endX : 0,
18543     endY : 0,
18544     swiping : false,
18545     
18546     onTouchStart : function(e)
18547     {
18548         this.swiping = false;
18549         
18550         this.startX = e.browserEvent.touches[0].clientX;
18551         this.startY = e.browserEvent.touches[0].clientY;
18552     },
18553     
18554     onTouchMove : function(e)
18555     {
18556         this.swiping = true;
18557         
18558         this.endX = e.browserEvent.touches[0].clientX;
18559         this.endY = e.browserEvent.touches[0].clientY;
18560     },
18561     
18562     onTouchEnd : function(e)
18563     {
18564         if(!this.swiping){
18565             this.onClick(e);
18566             return;
18567         }
18568         
18569         var tabGroup = this.parent();
18570         
18571         if(this.endX > this.startX){ // swiping right
18572             tabGroup.showPanelPrev();
18573             return;
18574         }
18575         
18576         if(this.startX > this.endX){ // swiping left
18577             tabGroup.showPanelNext();
18578             return;
18579         }
18580     }
18581     
18582     
18583 });
18584  
18585
18586  
18587
18588  /*
18589  * - LGPL
18590  *
18591  * DateField
18592  * 
18593  */
18594
18595 /**
18596  * @class Roo.bootstrap.DateField
18597  * @extends Roo.bootstrap.Input
18598  * Bootstrap DateField class
18599  * @cfg {Number} weekStart default 0
18600  * @cfg {String} viewMode default empty, (months|years)
18601  * @cfg {String} minViewMode default empty, (months|years)
18602  * @cfg {Number} startDate default -Infinity
18603  * @cfg {Number} endDate default Infinity
18604  * @cfg {Boolean} todayHighlight default false
18605  * @cfg {Boolean} todayBtn default false
18606  * @cfg {Boolean} calendarWeeks default false
18607  * @cfg {Object} daysOfWeekDisabled default empty
18608  * @cfg {Boolean} singleMode default false (true | false)
18609  * 
18610  * @cfg {Boolean} keyboardNavigation default true
18611  * @cfg {String} language default en
18612  * 
18613  * @constructor
18614  * Create a new DateField
18615  * @param {Object} config The config object
18616  */
18617
18618 Roo.bootstrap.DateField = function(config){
18619     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18620      this.addEvents({
18621             /**
18622              * @event show
18623              * Fires when this field show.
18624              * @param {Roo.bootstrap.DateField} this
18625              * @param {Mixed} date The date value
18626              */
18627             show : true,
18628             /**
18629              * @event show
18630              * Fires when this field hide.
18631              * @param {Roo.bootstrap.DateField} this
18632              * @param {Mixed} date The date value
18633              */
18634             hide : true,
18635             /**
18636              * @event select
18637              * Fires when select a date.
18638              * @param {Roo.bootstrap.DateField} this
18639              * @param {Mixed} date The date value
18640              */
18641             select : true,
18642             /**
18643              * @event beforeselect
18644              * Fires when before select a date.
18645              * @param {Roo.bootstrap.DateField} this
18646              * @param {Mixed} date The date value
18647              */
18648             beforeselect : true
18649         });
18650 };
18651
18652 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18653     
18654     /**
18655      * @cfg {String} format
18656      * The default date format string which can be overriden for localization support.  The format must be
18657      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18658      */
18659     format : "m/d/y",
18660     /**
18661      * @cfg {String} altFormats
18662      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18663      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18664      */
18665     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18666     
18667     weekStart : 0,
18668     
18669     viewMode : '',
18670     
18671     minViewMode : '',
18672     
18673     todayHighlight : false,
18674     
18675     todayBtn: false,
18676     
18677     language: 'en',
18678     
18679     keyboardNavigation: true,
18680     
18681     calendarWeeks: false,
18682     
18683     startDate: -Infinity,
18684     
18685     endDate: Infinity,
18686     
18687     daysOfWeekDisabled: [],
18688     
18689     _events: [],
18690     
18691     singleMode : false,
18692     
18693     UTCDate: function()
18694     {
18695         return new Date(Date.UTC.apply(Date, arguments));
18696     },
18697     
18698     UTCToday: function()
18699     {
18700         var today = new Date();
18701         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18702     },
18703     
18704     getDate: function() {
18705             var d = this.getUTCDate();
18706             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18707     },
18708     
18709     getUTCDate: function() {
18710             return this.date;
18711     },
18712     
18713     setDate: function(d) {
18714             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18715     },
18716     
18717     setUTCDate: function(d) {
18718             this.date = d;
18719             this.setValue(this.formatDate(this.date));
18720     },
18721         
18722     onRender: function(ct, position)
18723     {
18724         
18725         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18726         
18727         this.language = this.language || 'en';
18728         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18729         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18730         
18731         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18732         this.format = this.format || 'm/d/y';
18733         this.isInline = false;
18734         this.isInput = true;
18735         this.component = this.el.select('.add-on', true).first() || false;
18736         this.component = (this.component && this.component.length === 0) ? false : this.component;
18737         this.hasInput = this.component && this.inputEl().length;
18738         
18739         if (typeof(this.minViewMode === 'string')) {
18740             switch (this.minViewMode) {
18741                 case 'months':
18742                     this.minViewMode = 1;
18743                     break;
18744                 case 'years':
18745                     this.minViewMode = 2;
18746                     break;
18747                 default:
18748                     this.minViewMode = 0;
18749                     break;
18750             }
18751         }
18752         
18753         if (typeof(this.viewMode === 'string')) {
18754             switch (this.viewMode) {
18755                 case 'months':
18756                     this.viewMode = 1;
18757                     break;
18758                 case 'years':
18759                     this.viewMode = 2;
18760                     break;
18761                 default:
18762                     this.viewMode = 0;
18763                     break;
18764             }
18765         }
18766                 
18767         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18768         
18769 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18770         
18771         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18772         
18773         this.picker().on('mousedown', this.onMousedown, this);
18774         this.picker().on('click', this.onClick, this);
18775         
18776         this.picker().addClass('datepicker-dropdown');
18777         
18778         this.startViewMode = this.viewMode;
18779         
18780         if(this.singleMode){
18781             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18782                 v.setVisibilityMode(Roo.Element.DISPLAY);
18783                 v.hide();
18784             });
18785             
18786             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18787                 v.setStyle('width', '189px');
18788             });
18789         }
18790         
18791         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18792             if(!this.calendarWeeks){
18793                 v.remove();
18794                 return;
18795             }
18796             
18797             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18798             v.attr('colspan', function(i, val){
18799                 return parseInt(val) + 1;
18800             });
18801         });
18802                         
18803         
18804         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18805         
18806         this.setStartDate(this.startDate);
18807         this.setEndDate(this.endDate);
18808         
18809         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18810         
18811         this.fillDow();
18812         this.fillMonths();
18813         this.update();
18814         this.showMode();
18815         
18816         if(this.isInline) {
18817             this.showPopup();
18818         }
18819     },
18820     
18821     picker : function()
18822     {
18823         return this.pickerEl;
18824 //        return this.el.select('.datepicker', true).first();
18825     },
18826     
18827     fillDow: function()
18828     {
18829         var dowCnt = this.weekStart;
18830         
18831         var dow = {
18832             tag: 'tr',
18833             cn: [
18834                 
18835             ]
18836         };
18837         
18838         if(this.calendarWeeks){
18839             dow.cn.push({
18840                 tag: 'th',
18841                 cls: 'cw',
18842                 html: '&nbsp;'
18843             })
18844         }
18845         
18846         while (dowCnt < this.weekStart + 7) {
18847             dow.cn.push({
18848                 tag: 'th',
18849                 cls: 'dow',
18850                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18851             });
18852         }
18853         
18854         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18855     },
18856     
18857     fillMonths: function()
18858     {    
18859         var i = 0;
18860         var months = this.picker().select('>.datepicker-months td', true).first();
18861         
18862         months.dom.innerHTML = '';
18863         
18864         while (i < 12) {
18865             var month = {
18866                 tag: 'span',
18867                 cls: 'month',
18868                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18869             };
18870             
18871             months.createChild(month);
18872         }
18873         
18874     },
18875     
18876     update: function()
18877     {
18878         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;
18879         
18880         if (this.date < this.startDate) {
18881             this.viewDate = new Date(this.startDate);
18882         } else if (this.date > this.endDate) {
18883             this.viewDate = new Date(this.endDate);
18884         } else {
18885             this.viewDate = new Date(this.date);
18886         }
18887         
18888         this.fill();
18889     },
18890     
18891     fill: function() 
18892     {
18893         var d = new Date(this.viewDate),
18894                 year = d.getUTCFullYear(),
18895                 month = d.getUTCMonth(),
18896                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18897                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18898                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18899                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18900                 currentDate = this.date && this.date.valueOf(),
18901                 today = this.UTCToday();
18902         
18903         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18904         
18905 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18906         
18907 //        this.picker.select('>tfoot th.today').
18908 //                                              .text(dates[this.language].today)
18909 //                                              .toggle(this.todayBtn !== false);
18910     
18911         this.updateNavArrows();
18912         this.fillMonths();
18913                                                 
18914         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18915         
18916         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18917          
18918         prevMonth.setUTCDate(day);
18919         
18920         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18921         
18922         var nextMonth = new Date(prevMonth);
18923         
18924         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18925         
18926         nextMonth = nextMonth.valueOf();
18927         
18928         var fillMonths = false;
18929         
18930         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18931         
18932         while(prevMonth.valueOf() <= nextMonth) {
18933             var clsName = '';
18934             
18935             if (prevMonth.getUTCDay() === this.weekStart) {
18936                 if(fillMonths){
18937                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18938                 }
18939                     
18940                 fillMonths = {
18941                     tag: 'tr',
18942                     cn: []
18943                 };
18944                 
18945                 if(this.calendarWeeks){
18946                     // ISO 8601: First week contains first thursday.
18947                     // ISO also states week starts on Monday, but we can be more abstract here.
18948                     var
18949                     // Start of current week: based on weekstart/current date
18950                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18951                     // Thursday of this week
18952                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18953                     // First Thursday of year, year from thursday
18954                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18955                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18956                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18957                     
18958                     fillMonths.cn.push({
18959                         tag: 'td',
18960                         cls: 'cw',
18961                         html: calWeek
18962                     });
18963                 }
18964             }
18965             
18966             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18967                 clsName += ' old';
18968             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18969                 clsName += ' new';
18970             }
18971             if (this.todayHighlight &&
18972                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18973                 prevMonth.getUTCMonth() == today.getMonth() &&
18974                 prevMonth.getUTCDate() == today.getDate()) {
18975                 clsName += ' today';
18976             }
18977             
18978             if (currentDate && prevMonth.valueOf() === currentDate) {
18979                 clsName += ' active';
18980             }
18981             
18982             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18983                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18984                     clsName += ' disabled';
18985             }
18986             
18987             fillMonths.cn.push({
18988                 tag: 'td',
18989                 cls: 'day ' + clsName,
18990                 html: prevMonth.getDate()
18991             });
18992             
18993             prevMonth.setDate(prevMonth.getDate()+1);
18994         }
18995           
18996         var currentYear = this.date && this.date.getUTCFullYear();
18997         var currentMonth = this.date && this.date.getUTCMonth();
18998         
18999         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19000         
19001         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19002             v.removeClass('active');
19003             
19004             if(currentYear === year && k === currentMonth){
19005                 v.addClass('active');
19006             }
19007             
19008             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19009                 v.addClass('disabled');
19010             }
19011             
19012         });
19013         
19014         
19015         year = parseInt(year/10, 10) * 10;
19016         
19017         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19018         
19019         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19020         
19021         year -= 1;
19022         for (var i = -1; i < 11; i++) {
19023             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19024                 tag: 'span',
19025                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19026                 html: year
19027             });
19028             
19029             year += 1;
19030         }
19031     },
19032     
19033     showMode: function(dir) 
19034     {
19035         if (dir) {
19036             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19037         }
19038         
19039         Roo.each(this.picker().select('>div',true).elements, function(v){
19040             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19041             v.hide();
19042         });
19043         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19044     },
19045     
19046     place: function()
19047     {
19048         if(this.isInline) {
19049             return;
19050         }
19051         
19052         this.picker().removeClass(['bottom', 'top']);
19053         
19054         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19055             /*
19056              * place to the top of element!
19057              *
19058              */
19059             
19060             this.picker().addClass('top');
19061             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19062             
19063             return;
19064         }
19065         
19066         this.picker().addClass('bottom');
19067         
19068         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19069     },
19070     
19071     parseDate : function(value)
19072     {
19073         if(!value || value instanceof Date){
19074             return value;
19075         }
19076         var v = Date.parseDate(value, this.format);
19077         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19078             v = Date.parseDate(value, 'Y-m-d');
19079         }
19080         if(!v && this.altFormats){
19081             if(!this.altFormatsArray){
19082                 this.altFormatsArray = this.altFormats.split("|");
19083             }
19084             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19085                 v = Date.parseDate(value, this.altFormatsArray[i]);
19086             }
19087         }
19088         return v;
19089     },
19090     
19091     formatDate : function(date, fmt)
19092     {   
19093         return (!date || !(date instanceof Date)) ?
19094         date : date.dateFormat(fmt || this.format);
19095     },
19096     
19097     onFocus : function()
19098     {
19099         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19100         this.showPopup();
19101     },
19102     
19103     onBlur : function()
19104     {
19105         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19106         
19107         var d = this.inputEl().getValue();
19108         
19109         this.setValue(d);
19110                 
19111         this.hidePopup();
19112     },
19113     
19114     showPopup : function()
19115     {
19116         this.picker().show();
19117         this.update();
19118         this.place();
19119         
19120         this.fireEvent('showpopup', this, this.date);
19121     },
19122     
19123     hidePopup : function()
19124     {
19125         if(this.isInline) {
19126             return;
19127         }
19128         this.picker().hide();
19129         this.viewMode = this.startViewMode;
19130         this.showMode();
19131         
19132         this.fireEvent('hidepopup', this, this.date);
19133         
19134     },
19135     
19136     onMousedown: function(e)
19137     {
19138         e.stopPropagation();
19139         e.preventDefault();
19140     },
19141     
19142     keyup: function(e)
19143     {
19144         Roo.bootstrap.DateField.superclass.keyup.call(this);
19145         this.update();
19146     },
19147
19148     setValue: function(v)
19149     {
19150         if(this.fireEvent('beforeselect', this, v) !== false){
19151             var d = new Date(this.parseDate(v) ).clearTime();
19152         
19153             if(isNaN(d.getTime())){
19154                 this.date = this.viewDate = '';
19155                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19156                 return;
19157             }
19158
19159             v = this.formatDate(d);
19160
19161             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19162
19163             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19164
19165             this.update();
19166
19167             this.fireEvent('select', this, this.date);
19168         }
19169     },
19170     
19171     getValue: function()
19172     {
19173         return this.formatDate(this.date);
19174     },
19175     
19176     fireKey: function(e)
19177     {
19178         if (!this.picker().isVisible()){
19179             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19180                 this.showPopup();
19181             }
19182             return;
19183         }
19184         
19185         var dateChanged = false,
19186         dir, day, month,
19187         newDate, newViewDate;
19188         
19189         switch(e.keyCode){
19190             case 27: // escape
19191                 this.hidePopup();
19192                 e.preventDefault();
19193                 break;
19194             case 37: // left
19195             case 39: // right
19196                 if (!this.keyboardNavigation) {
19197                     break;
19198                 }
19199                 dir = e.keyCode == 37 ? -1 : 1;
19200                 
19201                 if (e.ctrlKey){
19202                     newDate = this.moveYear(this.date, dir);
19203                     newViewDate = this.moveYear(this.viewDate, dir);
19204                 } else if (e.shiftKey){
19205                     newDate = this.moveMonth(this.date, dir);
19206                     newViewDate = this.moveMonth(this.viewDate, dir);
19207                 } else {
19208                     newDate = new Date(this.date);
19209                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19210                     newViewDate = new Date(this.viewDate);
19211                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19212                 }
19213                 if (this.dateWithinRange(newDate)){
19214                     this.date = newDate;
19215                     this.viewDate = newViewDate;
19216                     this.setValue(this.formatDate(this.date));
19217 //                    this.update();
19218                     e.preventDefault();
19219                     dateChanged = true;
19220                 }
19221                 break;
19222             case 38: // up
19223             case 40: // down
19224                 if (!this.keyboardNavigation) {
19225                     break;
19226                 }
19227                 dir = e.keyCode == 38 ? -1 : 1;
19228                 if (e.ctrlKey){
19229                     newDate = this.moveYear(this.date, dir);
19230                     newViewDate = this.moveYear(this.viewDate, dir);
19231                 } else if (e.shiftKey){
19232                     newDate = this.moveMonth(this.date, dir);
19233                     newViewDate = this.moveMonth(this.viewDate, dir);
19234                 } else {
19235                     newDate = new Date(this.date);
19236                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19237                     newViewDate = new Date(this.viewDate);
19238                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19239                 }
19240                 if (this.dateWithinRange(newDate)){
19241                     this.date = newDate;
19242                     this.viewDate = newViewDate;
19243                     this.setValue(this.formatDate(this.date));
19244 //                    this.update();
19245                     e.preventDefault();
19246                     dateChanged = true;
19247                 }
19248                 break;
19249             case 13: // enter
19250                 this.setValue(this.formatDate(this.date));
19251                 this.hidePopup();
19252                 e.preventDefault();
19253                 break;
19254             case 9: // tab
19255                 this.setValue(this.formatDate(this.date));
19256                 this.hidePopup();
19257                 break;
19258             case 16: // shift
19259             case 17: // ctrl
19260             case 18: // alt
19261                 break;
19262             default :
19263                 this.hidePopup();
19264                 
19265         }
19266     },
19267     
19268     
19269     onClick: function(e) 
19270     {
19271         e.stopPropagation();
19272         e.preventDefault();
19273         
19274         var target = e.getTarget();
19275         
19276         if(target.nodeName.toLowerCase() === 'i'){
19277             target = Roo.get(target).dom.parentNode;
19278         }
19279         
19280         var nodeName = target.nodeName;
19281         var className = target.className;
19282         var html = target.innerHTML;
19283         //Roo.log(nodeName);
19284         
19285         switch(nodeName.toLowerCase()) {
19286             case 'th':
19287                 switch(className) {
19288                     case 'switch':
19289                         this.showMode(1);
19290                         break;
19291                     case 'prev':
19292                     case 'next':
19293                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19294                         switch(this.viewMode){
19295                                 case 0:
19296                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19297                                         break;
19298                                 case 1:
19299                                 case 2:
19300                                         this.viewDate = this.moveYear(this.viewDate, dir);
19301                                         break;
19302                         }
19303                         this.fill();
19304                         break;
19305                     case 'today':
19306                         var date = new Date();
19307                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19308 //                        this.fill()
19309                         this.setValue(this.formatDate(this.date));
19310                         
19311                         this.hidePopup();
19312                         break;
19313                 }
19314                 break;
19315             case 'span':
19316                 if (className.indexOf('disabled') < 0) {
19317                     this.viewDate.setUTCDate(1);
19318                     if (className.indexOf('month') > -1) {
19319                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19320                     } else {
19321                         var year = parseInt(html, 10) || 0;
19322                         this.viewDate.setUTCFullYear(year);
19323                         
19324                     }
19325                     
19326                     if(this.singleMode){
19327                         this.setValue(this.formatDate(this.viewDate));
19328                         this.hidePopup();
19329                         return;
19330                     }
19331                     
19332                     this.showMode(-1);
19333                     this.fill();
19334                 }
19335                 break;
19336                 
19337             case 'td':
19338                 //Roo.log(className);
19339                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19340                     var day = parseInt(html, 10) || 1;
19341                     var year = this.viewDate.getUTCFullYear(),
19342                         month = this.viewDate.getUTCMonth();
19343
19344                     if (className.indexOf('old') > -1) {
19345                         if(month === 0 ){
19346                             month = 11;
19347                             year -= 1;
19348                         }else{
19349                             month -= 1;
19350                         }
19351                     } else if (className.indexOf('new') > -1) {
19352                         if (month == 11) {
19353                             month = 0;
19354                             year += 1;
19355                         } else {
19356                             month += 1;
19357                         }
19358                     }
19359                     //Roo.log([year,month,day]);
19360                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19361                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19362 //                    this.fill();
19363                     //Roo.log(this.formatDate(this.date));
19364                     this.setValue(this.formatDate(this.date));
19365                     this.hidePopup();
19366                 }
19367                 break;
19368         }
19369     },
19370     
19371     setStartDate: function(startDate)
19372     {
19373         this.startDate = startDate || -Infinity;
19374         if (this.startDate !== -Infinity) {
19375             this.startDate = this.parseDate(this.startDate);
19376         }
19377         this.update();
19378         this.updateNavArrows();
19379     },
19380
19381     setEndDate: function(endDate)
19382     {
19383         this.endDate = endDate || Infinity;
19384         if (this.endDate !== Infinity) {
19385             this.endDate = this.parseDate(this.endDate);
19386         }
19387         this.update();
19388         this.updateNavArrows();
19389     },
19390     
19391     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19392     {
19393         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19394         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19395             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19396         }
19397         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19398             return parseInt(d, 10);
19399         });
19400         this.update();
19401         this.updateNavArrows();
19402     },
19403     
19404     updateNavArrows: function() 
19405     {
19406         if(this.singleMode){
19407             return;
19408         }
19409         
19410         var d = new Date(this.viewDate),
19411         year = d.getUTCFullYear(),
19412         month = d.getUTCMonth();
19413         
19414         Roo.each(this.picker().select('.prev', true).elements, function(v){
19415             v.show();
19416             switch (this.viewMode) {
19417                 case 0:
19418
19419                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19420                         v.hide();
19421                     }
19422                     break;
19423                 case 1:
19424                 case 2:
19425                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19426                         v.hide();
19427                     }
19428                     break;
19429             }
19430         });
19431         
19432         Roo.each(this.picker().select('.next', true).elements, function(v){
19433             v.show();
19434             switch (this.viewMode) {
19435                 case 0:
19436
19437                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19438                         v.hide();
19439                     }
19440                     break;
19441                 case 1:
19442                 case 2:
19443                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19444                         v.hide();
19445                     }
19446                     break;
19447             }
19448         })
19449     },
19450     
19451     moveMonth: function(date, dir)
19452     {
19453         if (!dir) {
19454             return date;
19455         }
19456         var new_date = new Date(date.valueOf()),
19457         day = new_date.getUTCDate(),
19458         month = new_date.getUTCMonth(),
19459         mag = Math.abs(dir),
19460         new_month, test;
19461         dir = dir > 0 ? 1 : -1;
19462         if (mag == 1){
19463             test = dir == -1
19464             // If going back one month, make sure month is not current month
19465             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19466             ? function(){
19467                 return new_date.getUTCMonth() == month;
19468             }
19469             // If going forward one month, make sure month is as expected
19470             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19471             : function(){
19472                 return new_date.getUTCMonth() != new_month;
19473             };
19474             new_month = month + dir;
19475             new_date.setUTCMonth(new_month);
19476             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19477             if (new_month < 0 || new_month > 11) {
19478                 new_month = (new_month + 12) % 12;
19479             }
19480         } else {
19481             // For magnitudes >1, move one month at a time...
19482             for (var i=0; i<mag; i++) {
19483                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19484                 new_date = this.moveMonth(new_date, dir);
19485             }
19486             // ...then reset the day, keeping it in the new month
19487             new_month = new_date.getUTCMonth();
19488             new_date.setUTCDate(day);
19489             test = function(){
19490                 return new_month != new_date.getUTCMonth();
19491             };
19492         }
19493         // Common date-resetting loop -- if date is beyond end of month, make it
19494         // end of month
19495         while (test()){
19496             new_date.setUTCDate(--day);
19497             new_date.setUTCMonth(new_month);
19498         }
19499         return new_date;
19500     },
19501
19502     moveYear: function(date, dir)
19503     {
19504         return this.moveMonth(date, dir*12);
19505     },
19506
19507     dateWithinRange: function(date)
19508     {
19509         return date >= this.startDate && date <= this.endDate;
19510     },
19511
19512     
19513     remove: function() 
19514     {
19515         this.picker().remove();
19516     },
19517     
19518     validateValue : function(value)
19519     {
19520         if(this.getVisibilityEl().hasClass('hidden')){
19521             return true;
19522         }
19523         
19524         if(value.length < 1)  {
19525             if(this.allowBlank){
19526                 return true;
19527             }
19528             return false;
19529         }
19530         
19531         if(value.length < this.minLength){
19532             return false;
19533         }
19534         if(value.length > this.maxLength){
19535             return false;
19536         }
19537         if(this.vtype){
19538             var vt = Roo.form.VTypes;
19539             if(!vt[this.vtype](value, this)){
19540                 return false;
19541             }
19542         }
19543         if(typeof this.validator == "function"){
19544             var msg = this.validator(value);
19545             if(msg !== true){
19546                 return false;
19547             }
19548         }
19549         
19550         if(this.regex && !this.regex.test(value)){
19551             return false;
19552         }
19553         
19554         if(typeof(this.parseDate(value)) == 'undefined'){
19555             return false;
19556         }
19557         
19558         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19559             return false;
19560         }      
19561         
19562         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19563             return false;
19564         } 
19565         
19566         
19567         return true;
19568     },
19569     
19570     reset : function()
19571     {
19572         this.date = this.viewDate = '';
19573         
19574         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19575     }
19576    
19577 });
19578
19579 Roo.apply(Roo.bootstrap.DateField,  {
19580     
19581     head : {
19582         tag: 'thead',
19583         cn: [
19584         {
19585             tag: 'tr',
19586             cn: [
19587             {
19588                 tag: 'th',
19589                 cls: 'prev',
19590                 html: '<i class="fa fa-arrow-left"/>'
19591             },
19592             {
19593                 tag: 'th',
19594                 cls: 'switch',
19595                 colspan: '5'
19596             },
19597             {
19598                 tag: 'th',
19599                 cls: 'next',
19600                 html: '<i class="fa fa-arrow-right"/>'
19601             }
19602
19603             ]
19604         }
19605         ]
19606     },
19607     
19608     content : {
19609         tag: 'tbody',
19610         cn: [
19611         {
19612             tag: 'tr',
19613             cn: [
19614             {
19615                 tag: 'td',
19616                 colspan: '7'
19617             }
19618             ]
19619         }
19620         ]
19621     },
19622     
19623     footer : {
19624         tag: 'tfoot',
19625         cn: [
19626         {
19627             tag: 'tr',
19628             cn: [
19629             {
19630                 tag: 'th',
19631                 colspan: '7',
19632                 cls: 'today'
19633             }
19634                     
19635             ]
19636         }
19637         ]
19638     },
19639     
19640     dates:{
19641         en: {
19642             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19643             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19644             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19645             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19646             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19647             today: "Today"
19648         }
19649     },
19650     
19651     modes: [
19652     {
19653         clsName: 'days',
19654         navFnc: 'Month',
19655         navStep: 1
19656     },
19657     {
19658         clsName: 'months',
19659         navFnc: 'FullYear',
19660         navStep: 1
19661     },
19662     {
19663         clsName: 'years',
19664         navFnc: 'FullYear',
19665         navStep: 10
19666     }]
19667 });
19668
19669 Roo.apply(Roo.bootstrap.DateField,  {
19670   
19671     template : {
19672         tag: 'div',
19673         cls: 'datepicker dropdown-menu roo-dynamic',
19674         cn: [
19675         {
19676             tag: 'div',
19677             cls: 'datepicker-days',
19678             cn: [
19679             {
19680                 tag: 'table',
19681                 cls: 'table-condensed',
19682                 cn:[
19683                 Roo.bootstrap.DateField.head,
19684                 {
19685                     tag: 'tbody'
19686                 },
19687                 Roo.bootstrap.DateField.footer
19688                 ]
19689             }
19690             ]
19691         },
19692         {
19693             tag: 'div',
19694             cls: 'datepicker-months',
19695             cn: [
19696             {
19697                 tag: 'table',
19698                 cls: 'table-condensed',
19699                 cn:[
19700                 Roo.bootstrap.DateField.head,
19701                 Roo.bootstrap.DateField.content,
19702                 Roo.bootstrap.DateField.footer
19703                 ]
19704             }
19705             ]
19706         },
19707         {
19708             tag: 'div',
19709             cls: 'datepicker-years',
19710             cn: [
19711             {
19712                 tag: 'table',
19713                 cls: 'table-condensed',
19714                 cn:[
19715                 Roo.bootstrap.DateField.head,
19716                 Roo.bootstrap.DateField.content,
19717                 Roo.bootstrap.DateField.footer
19718                 ]
19719             }
19720             ]
19721         }
19722         ]
19723     }
19724 });
19725
19726  
19727
19728  /*
19729  * - LGPL
19730  *
19731  * TimeField
19732  * 
19733  */
19734
19735 /**
19736  * @class Roo.bootstrap.TimeField
19737  * @extends Roo.bootstrap.Input
19738  * Bootstrap DateField class
19739  * 
19740  * 
19741  * @constructor
19742  * Create a new TimeField
19743  * @param {Object} config The config object
19744  */
19745
19746 Roo.bootstrap.TimeField = function(config){
19747     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19748     this.addEvents({
19749             /**
19750              * @event show
19751              * Fires when this field show.
19752              * @param {Roo.bootstrap.DateField} thisthis
19753              * @param {Mixed} date The date value
19754              */
19755             show : true,
19756             /**
19757              * @event show
19758              * Fires when this field hide.
19759              * @param {Roo.bootstrap.DateField} this
19760              * @param {Mixed} date The date value
19761              */
19762             hide : true,
19763             /**
19764              * @event select
19765              * Fires when select a date.
19766              * @param {Roo.bootstrap.DateField} this
19767              * @param {Mixed} date The date value
19768              */
19769             select : true
19770         });
19771 };
19772
19773 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19774     
19775     /**
19776      * @cfg {String} format
19777      * The default time format string which can be overriden for localization support.  The format must be
19778      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19779      */
19780     format : "H:i",
19781        
19782     onRender: function(ct, position)
19783     {
19784         
19785         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19786                 
19787         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19788         
19789         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19790         
19791         this.pop = this.picker().select('>.datepicker-time',true).first();
19792         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19793         
19794         this.picker().on('mousedown', this.onMousedown, this);
19795         this.picker().on('click', this.onClick, this);
19796         
19797         this.picker().addClass('datepicker-dropdown');
19798     
19799         this.fillTime();
19800         this.update();
19801             
19802         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19803         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19804         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19805         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19806         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19807         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19808
19809     },
19810     
19811     fireKey: function(e){
19812         if (!this.picker().isVisible()){
19813             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19814                 this.show();
19815             }
19816             return;
19817         }
19818
19819         e.preventDefault();
19820         
19821         switch(e.keyCode){
19822             case 27: // escape
19823                 this.hide();
19824                 break;
19825             case 37: // left
19826             case 39: // right
19827                 this.onTogglePeriod();
19828                 break;
19829             case 38: // up
19830                 this.onIncrementMinutes();
19831                 break;
19832             case 40: // down
19833                 this.onDecrementMinutes();
19834                 break;
19835             case 13: // enter
19836             case 9: // tab
19837                 this.setTime();
19838                 break;
19839         }
19840     },
19841     
19842     onClick: function(e) {
19843         e.stopPropagation();
19844         e.preventDefault();
19845     },
19846     
19847     picker : function()
19848     {
19849         return this.el.select('.datepicker', true).first();
19850     },
19851     
19852     fillTime: function()
19853     {    
19854         var time = this.pop.select('tbody', true).first();
19855         
19856         time.dom.innerHTML = '';
19857         
19858         time.createChild({
19859             tag: 'tr',
19860             cn: [
19861                 {
19862                     tag: 'td',
19863                     cn: [
19864                         {
19865                             tag: 'a',
19866                             href: '#',
19867                             cls: 'btn',
19868                             cn: [
19869                                 {
19870                                     tag: 'span',
19871                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19872                                 }
19873                             ]
19874                         } 
19875                     ]
19876                 },
19877                 {
19878                     tag: 'td',
19879                     cls: 'separator'
19880                 },
19881                 {
19882                     tag: 'td',
19883                     cn: [
19884                         {
19885                             tag: 'a',
19886                             href: '#',
19887                             cls: 'btn',
19888                             cn: [
19889                                 {
19890                                     tag: 'span',
19891                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19892                                 }
19893                             ]
19894                         }
19895                     ]
19896                 },
19897                 {
19898                     tag: 'td',
19899                     cls: 'separator'
19900                 }
19901             ]
19902         });
19903         
19904         time.createChild({
19905             tag: 'tr',
19906             cn: [
19907                 {
19908                     tag: 'td',
19909                     cn: [
19910                         {
19911                             tag: 'span',
19912                             cls: 'timepicker-hour',
19913                             html: '00'
19914                         }  
19915                     ]
19916                 },
19917                 {
19918                     tag: 'td',
19919                     cls: 'separator',
19920                     html: ':'
19921                 },
19922                 {
19923                     tag: 'td',
19924                     cn: [
19925                         {
19926                             tag: 'span',
19927                             cls: 'timepicker-minute',
19928                             html: '00'
19929                         }  
19930                     ]
19931                 },
19932                 {
19933                     tag: 'td',
19934                     cls: 'separator'
19935                 },
19936                 {
19937                     tag: 'td',
19938                     cn: [
19939                         {
19940                             tag: 'button',
19941                             type: 'button',
19942                             cls: 'btn btn-primary period',
19943                             html: 'AM'
19944                             
19945                         }
19946                     ]
19947                 }
19948             ]
19949         });
19950         
19951         time.createChild({
19952             tag: 'tr',
19953             cn: [
19954                 {
19955                     tag: 'td',
19956                     cn: [
19957                         {
19958                             tag: 'a',
19959                             href: '#',
19960                             cls: 'btn',
19961                             cn: [
19962                                 {
19963                                     tag: 'span',
19964                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19965                                 }
19966                             ]
19967                         }
19968                     ]
19969                 },
19970                 {
19971                     tag: 'td',
19972                     cls: 'separator'
19973                 },
19974                 {
19975                     tag: 'td',
19976                     cn: [
19977                         {
19978                             tag: 'a',
19979                             href: '#',
19980                             cls: 'btn',
19981                             cn: [
19982                                 {
19983                                     tag: 'span',
19984                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19985                                 }
19986                             ]
19987                         }
19988                     ]
19989                 },
19990                 {
19991                     tag: 'td',
19992                     cls: 'separator'
19993                 }
19994             ]
19995         });
19996         
19997     },
19998     
19999     update: function()
20000     {
20001         
20002         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20003         
20004         this.fill();
20005     },
20006     
20007     fill: function() 
20008     {
20009         var hours = this.time.getHours();
20010         var minutes = this.time.getMinutes();
20011         var period = 'AM';
20012         
20013         if(hours > 11){
20014             period = 'PM';
20015         }
20016         
20017         if(hours == 0){
20018             hours = 12;
20019         }
20020         
20021         
20022         if(hours > 12){
20023             hours = hours - 12;
20024         }
20025         
20026         if(hours < 10){
20027             hours = '0' + hours;
20028         }
20029         
20030         if(minutes < 10){
20031             minutes = '0' + minutes;
20032         }
20033         
20034         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20035         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20036         this.pop.select('button', true).first().dom.innerHTML = period;
20037         
20038     },
20039     
20040     place: function()
20041     {   
20042         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20043         
20044         var cls = ['bottom'];
20045         
20046         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20047             cls.pop();
20048             cls.push('top');
20049         }
20050         
20051         cls.push('right');
20052         
20053         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20054             cls.pop();
20055             cls.push('left');
20056         }
20057         
20058         this.picker().addClass(cls.join('-'));
20059         
20060         var _this = this;
20061         
20062         Roo.each(cls, function(c){
20063             if(c == 'bottom'){
20064                 _this.picker().setTop(_this.inputEl().getHeight());
20065                 return;
20066             }
20067             if(c == 'top'){
20068                 _this.picker().setTop(0 - _this.picker().getHeight());
20069                 return;
20070             }
20071             
20072             if(c == 'left'){
20073                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20074                 return;
20075             }
20076             if(c == 'right'){
20077                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20078                 return;
20079             }
20080         });
20081         
20082     },
20083   
20084     onFocus : function()
20085     {
20086         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20087         this.show();
20088     },
20089     
20090     onBlur : function()
20091     {
20092         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20093         this.hide();
20094     },
20095     
20096     show : function()
20097     {
20098         this.picker().show();
20099         this.pop.show();
20100         this.update();
20101         this.place();
20102         
20103         this.fireEvent('show', this, this.date);
20104     },
20105     
20106     hide : function()
20107     {
20108         this.picker().hide();
20109         this.pop.hide();
20110         
20111         this.fireEvent('hide', this, this.date);
20112     },
20113     
20114     setTime : function()
20115     {
20116         this.hide();
20117         this.setValue(this.time.format(this.format));
20118         
20119         this.fireEvent('select', this, this.date);
20120         
20121         
20122     },
20123     
20124     onMousedown: function(e){
20125         e.stopPropagation();
20126         e.preventDefault();
20127     },
20128     
20129     onIncrementHours: function()
20130     {
20131         Roo.log('onIncrementHours');
20132         this.time = this.time.add(Date.HOUR, 1);
20133         this.update();
20134         
20135     },
20136     
20137     onDecrementHours: function()
20138     {
20139         Roo.log('onDecrementHours');
20140         this.time = this.time.add(Date.HOUR, -1);
20141         this.update();
20142     },
20143     
20144     onIncrementMinutes: function()
20145     {
20146         Roo.log('onIncrementMinutes');
20147         this.time = this.time.add(Date.MINUTE, 1);
20148         this.update();
20149     },
20150     
20151     onDecrementMinutes: function()
20152     {
20153         Roo.log('onDecrementMinutes');
20154         this.time = this.time.add(Date.MINUTE, -1);
20155         this.update();
20156     },
20157     
20158     onTogglePeriod: function()
20159     {
20160         Roo.log('onTogglePeriod');
20161         this.time = this.time.add(Date.HOUR, 12);
20162         this.update();
20163     }
20164     
20165    
20166 });
20167
20168 Roo.apply(Roo.bootstrap.TimeField,  {
20169     
20170     content : {
20171         tag: 'tbody',
20172         cn: [
20173             {
20174                 tag: 'tr',
20175                 cn: [
20176                 {
20177                     tag: 'td',
20178                     colspan: '7'
20179                 }
20180                 ]
20181             }
20182         ]
20183     },
20184     
20185     footer : {
20186         tag: 'tfoot',
20187         cn: [
20188             {
20189                 tag: 'tr',
20190                 cn: [
20191                 {
20192                     tag: 'th',
20193                     colspan: '7',
20194                     cls: '',
20195                     cn: [
20196                         {
20197                             tag: 'button',
20198                             cls: 'btn btn-info ok',
20199                             html: 'OK'
20200                         }
20201                     ]
20202                 }
20203
20204                 ]
20205             }
20206         ]
20207     }
20208 });
20209
20210 Roo.apply(Roo.bootstrap.TimeField,  {
20211   
20212     template : {
20213         tag: 'div',
20214         cls: 'datepicker dropdown-menu',
20215         cn: [
20216             {
20217                 tag: 'div',
20218                 cls: 'datepicker-time',
20219                 cn: [
20220                 {
20221                     tag: 'table',
20222                     cls: 'table-condensed',
20223                     cn:[
20224                     Roo.bootstrap.TimeField.content,
20225                     Roo.bootstrap.TimeField.footer
20226                     ]
20227                 }
20228                 ]
20229             }
20230         ]
20231     }
20232 });
20233
20234  
20235
20236  /*
20237  * - LGPL
20238  *
20239  * MonthField
20240  * 
20241  */
20242
20243 /**
20244  * @class Roo.bootstrap.MonthField
20245  * @extends Roo.bootstrap.Input
20246  * Bootstrap MonthField class
20247  * 
20248  * @cfg {String} language default en
20249  * 
20250  * @constructor
20251  * Create a new MonthField
20252  * @param {Object} config The config object
20253  */
20254
20255 Roo.bootstrap.MonthField = function(config){
20256     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20257     
20258     this.addEvents({
20259         /**
20260          * @event show
20261          * Fires when this field show.
20262          * @param {Roo.bootstrap.MonthField} this
20263          * @param {Mixed} date The date value
20264          */
20265         show : true,
20266         /**
20267          * @event show
20268          * Fires when this field hide.
20269          * @param {Roo.bootstrap.MonthField} this
20270          * @param {Mixed} date The date value
20271          */
20272         hide : true,
20273         /**
20274          * @event select
20275          * Fires when select a date.
20276          * @param {Roo.bootstrap.MonthField} this
20277          * @param {String} oldvalue The old value
20278          * @param {String} newvalue The new value
20279          */
20280         select : true
20281     });
20282 };
20283
20284 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20285     
20286     onRender: function(ct, position)
20287     {
20288         
20289         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20290         
20291         this.language = this.language || 'en';
20292         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20293         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20294         
20295         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20296         this.isInline = false;
20297         this.isInput = true;
20298         this.component = this.el.select('.add-on', true).first() || false;
20299         this.component = (this.component && this.component.length === 0) ? false : this.component;
20300         this.hasInput = this.component && this.inputEL().length;
20301         
20302         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20303         
20304         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20305         
20306         this.picker().on('mousedown', this.onMousedown, this);
20307         this.picker().on('click', this.onClick, this);
20308         
20309         this.picker().addClass('datepicker-dropdown');
20310         
20311         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20312             v.setStyle('width', '189px');
20313         });
20314         
20315         this.fillMonths();
20316         
20317         this.update();
20318         
20319         if(this.isInline) {
20320             this.show();
20321         }
20322         
20323     },
20324     
20325     setValue: function(v, suppressEvent)
20326     {   
20327         var o = this.getValue();
20328         
20329         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20330         
20331         this.update();
20332
20333         if(suppressEvent !== true){
20334             this.fireEvent('select', this, o, v);
20335         }
20336         
20337     },
20338     
20339     getValue: function()
20340     {
20341         return this.value;
20342     },
20343     
20344     onClick: function(e) 
20345     {
20346         e.stopPropagation();
20347         e.preventDefault();
20348         
20349         var target = e.getTarget();
20350         
20351         if(target.nodeName.toLowerCase() === 'i'){
20352             target = Roo.get(target).dom.parentNode;
20353         }
20354         
20355         var nodeName = target.nodeName;
20356         var className = target.className;
20357         var html = target.innerHTML;
20358         
20359         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20360             return;
20361         }
20362         
20363         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20364         
20365         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20366         
20367         this.hide();
20368                         
20369     },
20370     
20371     picker : function()
20372     {
20373         return this.pickerEl;
20374     },
20375     
20376     fillMonths: function()
20377     {    
20378         var i = 0;
20379         var months = this.picker().select('>.datepicker-months td', true).first();
20380         
20381         months.dom.innerHTML = '';
20382         
20383         while (i < 12) {
20384             var month = {
20385                 tag: 'span',
20386                 cls: 'month',
20387                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20388             };
20389             
20390             months.createChild(month);
20391         }
20392         
20393     },
20394     
20395     update: function()
20396     {
20397         var _this = this;
20398         
20399         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20400             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20401         }
20402         
20403         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20404             e.removeClass('active');
20405             
20406             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20407                 e.addClass('active');
20408             }
20409         })
20410     },
20411     
20412     place: function()
20413     {
20414         if(this.isInline) {
20415             return;
20416         }
20417         
20418         this.picker().removeClass(['bottom', 'top']);
20419         
20420         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20421             /*
20422              * place to the top of element!
20423              *
20424              */
20425             
20426             this.picker().addClass('top');
20427             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20428             
20429             return;
20430         }
20431         
20432         this.picker().addClass('bottom');
20433         
20434         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20435     },
20436     
20437     onFocus : function()
20438     {
20439         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20440         this.show();
20441     },
20442     
20443     onBlur : function()
20444     {
20445         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20446         
20447         var d = this.inputEl().getValue();
20448         
20449         this.setValue(d);
20450                 
20451         this.hide();
20452     },
20453     
20454     show : function()
20455     {
20456         this.picker().show();
20457         this.picker().select('>.datepicker-months', true).first().show();
20458         this.update();
20459         this.place();
20460         
20461         this.fireEvent('show', this, this.date);
20462     },
20463     
20464     hide : function()
20465     {
20466         if(this.isInline) {
20467             return;
20468         }
20469         this.picker().hide();
20470         this.fireEvent('hide', this, this.date);
20471         
20472     },
20473     
20474     onMousedown: function(e)
20475     {
20476         e.stopPropagation();
20477         e.preventDefault();
20478     },
20479     
20480     keyup: function(e)
20481     {
20482         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20483         this.update();
20484     },
20485
20486     fireKey: function(e)
20487     {
20488         if (!this.picker().isVisible()){
20489             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20490                 this.show();
20491             }
20492             return;
20493         }
20494         
20495         var dir;
20496         
20497         switch(e.keyCode){
20498             case 27: // escape
20499                 this.hide();
20500                 e.preventDefault();
20501                 break;
20502             case 37: // left
20503             case 39: // right
20504                 dir = e.keyCode == 37 ? -1 : 1;
20505                 
20506                 this.vIndex = this.vIndex + dir;
20507                 
20508                 if(this.vIndex < 0){
20509                     this.vIndex = 0;
20510                 }
20511                 
20512                 if(this.vIndex > 11){
20513                     this.vIndex = 11;
20514                 }
20515                 
20516                 if(isNaN(this.vIndex)){
20517                     this.vIndex = 0;
20518                 }
20519                 
20520                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20521                 
20522                 break;
20523             case 38: // up
20524             case 40: // down
20525                 
20526                 dir = e.keyCode == 38 ? -1 : 1;
20527                 
20528                 this.vIndex = this.vIndex + dir * 4;
20529                 
20530                 if(this.vIndex < 0){
20531                     this.vIndex = 0;
20532                 }
20533                 
20534                 if(this.vIndex > 11){
20535                     this.vIndex = 11;
20536                 }
20537                 
20538                 if(isNaN(this.vIndex)){
20539                     this.vIndex = 0;
20540                 }
20541                 
20542                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20543                 break;
20544                 
20545             case 13: // enter
20546                 
20547                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20548                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20549                 }
20550                 
20551                 this.hide();
20552                 e.preventDefault();
20553                 break;
20554             case 9: // tab
20555                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20556                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20557                 }
20558                 this.hide();
20559                 break;
20560             case 16: // shift
20561             case 17: // ctrl
20562             case 18: // alt
20563                 break;
20564             default :
20565                 this.hide();
20566                 
20567         }
20568     },
20569     
20570     remove: function() 
20571     {
20572         this.picker().remove();
20573     }
20574    
20575 });
20576
20577 Roo.apply(Roo.bootstrap.MonthField,  {
20578     
20579     content : {
20580         tag: 'tbody',
20581         cn: [
20582         {
20583             tag: 'tr',
20584             cn: [
20585             {
20586                 tag: 'td',
20587                 colspan: '7'
20588             }
20589             ]
20590         }
20591         ]
20592     },
20593     
20594     dates:{
20595         en: {
20596             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20597             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20598         }
20599     }
20600 });
20601
20602 Roo.apply(Roo.bootstrap.MonthField,  {
20603   
20604     template : {
20605         tag: 'div',
20606         cls: 'datepicker dropdown-menu roo-dynamic',
20607         cn: [
20608             {
20609                 tag: 'div',
20610                 cls: 'datepicker-months',
20611                 cn: [
20612                 {
20613                     tag: 'table',
20614                     cls: 'table-condensed',
20615                     cn:[
20616                         Roo.bootstrap.DateField.content
20617                     ]
20618                 }
20619                 ]
20620             }
20621         ]
20622     }
20623 });
20624
20625  
20626
20627  
20628  /*
20629  * - LGPL
20630  *
20631  * CheckBox
20632  * 
20633  */
20634
20635 /**
20636  * @class Roo.bootstrap.CheckBox
20637  * @extends Roo.bootstrap.Input
20638  * Bootstrap CheckBox class
20639  * 
20640  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20641  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20642  * @cfg {String} boxLabel The text that appears beside the checkbox
20643  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20644  * @cfg {Boolean} checked initnal the element
20645  * @cfg {Boolean} inline inline the element (default false)
20646  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20647  * @cfg {String} tooltip label tooltip
20648  * 
20649  * @constructor
20650  * Create a new CheckBox
20651  * @param {Object} config The config object
20652  */
20653
20654 Roo.bootstrap.CheckBox = function(config){
20655     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20656    
20657     this.addEvents({
20658         /**
20659         * @event check
20660         * Fires when the element is checked or unchecked.
20661         * @param {Roo.bootstrap.CheckBox} this This input
20662         * @param {Boolean} checked The new checked value
20663         */
20664        check : true,
20665        /**
20666         * @event click
20667         * Fires when the element is click.
20668         * @param {Roo.bootstrap.CheckBox} this This input
20669         */
20670        click : true
20671     });
20672     
20673 };
20674
20675 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20676   
20677     inputType: 'checkbox',
20678     inputValue: 1,
20679     valueOff: 0,
20680     boxLabel: false,
20681     checked: false,
20682     weight : false,
20683     inline: false,
20684     tooltip : '',
20685     
20686     getAutoCreate : function()
20687     {
20688         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20689         
20690         var id = Roo.id();
20691         
20692         var cfg = {};
20693         
20694         cfg.cls = 'form-group ' + this.inputType; //input-group
20695         
20696         if(this.inline){
20697             cfg.cls += ' ' + this.inputType + '-inline';
20698         }
20699         
20700         var input =  {
20701             tag: 'input',
20702             id : id,
20703             type : this.inputType,
20704             value : this.inputValue,
20705             cls : 'roo-' + this.inputType, //'form-box',
20706             placeholder : this.placeholder || ''
20707             
20708         };
20709         
20710         if(this.inputType != 'radio'){
20711             var hidden =  {
20712                 tag: 'input',
20713                 type : 'hidden',
20714                 cls : 'roo-hidden-value',
20715                 value : this.checked ? this.inputValue : this.valueOff
20716             };
20717         }
20718         
20719             
20720         if (this.weight) { // Validity check?
20721             cfg.cls += " " + this.inputType + "-" + this.weight;
20722         }
20723         
20724         if (this.disabled) {
20725             input.disabled=true;
20726         }
20727         
20728         if(this.checked){
20729             input.checked = this.checked;
20730         }
20731         
20732         if (this.name) {
20733             
20734             input.name = this.name;
20735             
20736             if(this.inputType != 'radio'){
20737                 hidden.name = this.name;
20738                 input.name = '_hidden_' + this.name;
20739             }
20740         }
20741         
20742         if (this.size) {
20743             input.cls += ' input-' + this.size;
20744         }
20745         
20746         var settings=this;
20747         
20748         ['xs','sm','md','lg'].map(function(size){
20749             if (settings[size]) {
20750                 cfg.cls += ' col-' + size + '-' + settings[size];
20751             }
20752         });
20753         
20754         var inputblock = input;
20755          
20756         if (this.before || this.after) {
20757             
20758             inputblock = {
20759                 cls : 'input-group',
20760                 cn :  [] 
20761             };
20762             
20763             if (this.before) {
20764                 inputblock.cn.push({
20765                     tag :'span',
20766                     cls : 'input-group-addon',
20767                     html : this.before
20768                 });
20769             }
20770             
20771             inputblock.cn.push(input);
20772             
20773             if(this.inputType != 'radio'){
20774                 inputblock.cn.push(hidden);
20775             }
20776             
20777             if (this.after) {
20778                 inputblock.cn.push({
20779                     tag :'span',
20780                     cls : 'input-group-addon',
20781                     html : this.after
20782                 });
20783             }
20784             
20785         }
20786         
20787         if (align ==='left' && this.fieldLabel.length) {
20788 //                Roo.log("left and has label");
20789             cfg.cn = [
20790                 {
20791                     tag: 'label',
20792                     'for' :  id,
20793                     cls : 'control-label',
20794                     html : this.fieldLabel
20795                 },
20796                 {
20797                     cls : "", 
20798                     cn: [
20799                         inputblock
20800                     ]
20801                 }
20802             ];
20803             
20804             if(this.labelWidth > 12){
20805                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20806             }
20807             
20808             if(this.labelWidth < 13 && this.labelmd == 0){
20809                 this.labelmd = this.labelWidth;
20810             }
20811             
20812             if(this.labellg > 0){
20813                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20814                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20815             }
20816             
20817             if(this.labelmd > 0){
20818                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20819                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20820             }
20821             
20822             if(this.labelsm > 0){
20823                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20824                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20825             }
20826             
20827             if(this.labelxs > 0){
20828                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20829                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20830             }
20831             
20832         } else if ( this.fieldLabel.length) {
20833 //                Roo.log(" label");
20834                 cfg.cn = [
20835                    
20836                     {
20837                         tag: this.boxLabel ? 'span' : 'label',
20838                         'for': id,
20839                         cls: 'control-label box-input-label',
20840                         //cls : 'input-group-addon',
20841                         html : this.fieldLabel
20842                     },
20843                     
20844                     inputblock
20845                     
20846                 ];
20847
20848         } else {
20849             
20850 //                Roo.log(" no label && no align");
20851                 cfg.cn = [  inputblock ] ;
20852                 
20853                 
20854         }
20855         
20856         if(this.boxLabel){
20857              var boxLabelCfg = {
20858                 tag: 'label',
20859                 //'for': id, // box label is handled by onclick - so no for...
20860                 cls: 'box-label',
20861                 html: this.boxLabel
20862             };
20863             
20864             if(this.tooltip){
20865                 boxLabelCfg.tooltip = this.tooltip;
20866             }
20867              
20868             cfg.cn.push(boxLabelCfg);
20869         }
20870         
20871         if(this.inputType != 'radio'){
20872             cfg.cn.push(hidden);
20873         }
20874         
20875         return cfg;
20876         
20877     },
20878     
20879     /**
20880      * return the real input element.
20881      */
20882     inputEl: function ()
20883     {
20884         return this.el.select('input.roo-' + this.inputType,true).first();
20885     },
20886     hiddenEl: function ()
20887     {
20888         return this.el.select('input.roo-hidden-value',true).first();
20889     },
20890     
20891     labelEl: function()
20892     {
20893         return this.el.select('label.control-label',true).first();
20894     },
20895     /* depricated... */
20896     
20897     label: function()
20898     {
20899         return this.labelEl();
20900     },
20901     
20902     boxLabelEl: function()
20903     {
20904         return this.el.select('label.box-label',true).first();
20905     },
20906     
20907     initEvents : function()
20908     {
20909 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20910         
20911         this.inputEl().on('click', this.onClick,  this);
20912         
20913         if (this.boxLabel) { 
20914             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20915         }
20916         
20917         this.startValue = this.getValue();
20918         
20919         if(this.groupId){
20920             Roo.bootstrap.CheckBox.register(this);
20921         }
20922     },
20923     
20924     onClick : function(e)
20925     {   
20926         if(this.fireEvent('click', this, e) !== false){
20927             this.setChecked(!this.checked);
20928         }
20929         
20930     },
20931     
20932     setChecked : function(state,suppressEvent)
20933     {
20934         this.startValue = this.getValue();
20935
20936         if(this.inputType == 'radio'){
20937             
20938             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20939                 e.dom.checked = false;
20940             });
20941             
20942             this.inputEl().dom.checked = true;
20943             
20944             this.inputEl().dom.value = this.inputValue;
20945             
20946             if(suppressEvent !== true){
20947                 this.fireEvent('check', this, true);
20948             }
20949             
20950             this.validate();
20951             
20952             return;
20953         }
20954         
20955         this.checked = state;
20956         
20957         this.inputEl().dom.checked = state;
20958         
20959         
20960         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20961         
20962         if(suppressEvent !== true){
20963             this.fireEvent('check', this, state);
20964         }
20965         
20966         this.validate();
20967     },
20968     
20969     getValue : function()
20970     {
20971         if(this.inputType == 'radio'){
20972             return this.getGroupValue();
20973         }
20974         
20975         return this.hiddenEl().dom.value;
20976         
20977     },
20978     
20979     getGroupValue : function()
20980     {
20981         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20982             return '';
20983         }
20984         
20985         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20986     },
20987     
20988     setValue : function(v,suppressEvent)
20989     {
20990         if(this.inputType == 'radio'){
20991             this.setGroupValue(v, suppressEvent);
20992             return;
20993         }
20994         
20995         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20996         
20997         this.validate();
20998     },
20999     
21000     setGroupValue : function(v, suppressEvent)
21001     {
21002         this.startValue = this.getValue();
21003         
21004         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21005             e.dom.checked = false;
21006             
21007             if(e.dom.value == v){
21008                 e.dom.checked = true;
21009             }
21010         });
21011         
21012         if(suppressEvent !== true){
21013             this.fireEvent('check', this, true);
21014         }
21015
21016         this.validate();
21017         
21018         return;
21019     },
21020     
21021     validate : function()
21022     {
21023         if(this.getVisibilityEl().hasClass('hidden')){
21024             return true;
21025         }
21026         
21027         if(
21028                 this.disabled || 
21029                 (this.inputType == 'radio' && this.validateRadio()) ||
21030                 (this.inputType == 'checkbox' && this.validateCheckbox())
21031         ){
21032             this.markValid();
21033             return true;
21034         }
21035         
21036         this.markInvalid();
21037         return false;
21038     },
21039     
21040     validateRadio : function()
21041     {
21042         if(this.getVisibilityEl().hasClass('hidden')){
21043             return true;
21044         }
21045         
21046         if(this.allowBlank){
21047             return true;
21048         }
21049         
21050         var valid = false;
21051         
21052         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21053             if(!e.dom.checked){
21054                 return;
21055             }
21056             
21057             valid = true;
21058             
21059             return false;
21060         });
21061         
21062         return valid;
21063     },
21064     
21065     validateCheckbox : function()
21066     {
21067         if(!this.groupId){
21068             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21069             //return (this.getValue() == this.inputValue) ? true : false;
21070         }
21071         
21072         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21073         
21074         if(!group){
21075             return false;
21076         }
21077         
21078         var r = false;
21079         
21080         for(var i in group){
21081             if(group[i].el.isVisible(true)){
21082                 r = false;
21083                 break;
21084             }
21085             
21086             r = true;
21087         }
21088         
21089         for(var i in group){
21090             if(r){
21091                 break;
21092             }
21093             
21094             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21095         }
21096         
21097         return r;
21098     },
21099     
21100     /**
21101      * Mark this field as valid
21102      */
21103     markValid : function()
21104     {
21105         var _this = this;
21106         
21107         this.fireEvent('valid', this);
21108         
21109         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21110         
21111         if(this.groupId){
21112             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21113         }
21114         
21115         if(label){
21116             label.markValid();
21117         }
21118
21119         if(this.inputType == 'radio'){
21120             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21121                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21122                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21123             });
21124             
21125             return;
21126         }
21127
21128         if(!this.groupId){
21129             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21130             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21131             return;
21132         }
21133         
21134         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21135         
21136         if(!group){
21137             return;
21138         }
21139         
21140         for(var i in group){
21141             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21142             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21143         }
21144     },
21145     
21146      /**
21147      * Mark this field as invalid
21148      * @param {String} msg The validation message
21149      */
21150     markInvalid : function(msg)
21151     {
21152         if(this.allowBlank){
21153             return;
21154         }
21155         
21156         var _this = this;
21157         
21158         this.fireEvent('invalid', this, msg);
21159         
21160         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21161         
21162         if(this.groupId){
21163             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21164         }
21165         
21166         if(label){
21167             label.markInvalid();
21168         }
21169             
21170         if(this.inputType == 'radio'){
21171             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21172                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21173                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21174             });
21175             
21176             return;
21177         }
21178         
21179         if(!this.groupId){
21180             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21181             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21182             return;
21183         }
21184         
21185         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21186         
21187         if(!group){
21188             return;
21189         }
21190         
21191         for(var i in group){
21192             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21193             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21194         }
21195         
21196     },
21197     
21198     clearInvalid : function()
21199     {
21200         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21201         
21202         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21203         
21204         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21205         
21206         if (label && label.iconEl) {
21207             label.iconEl.removeClass(label.validClass);
21208             label.iconEl.removeClass(label.invalidClass);
21209         }
21210     },
21211     
21212     disable : function()
21213     {
21214         if(this.inputType != 'radio'){
21215             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21216             return;
21217         }
21218         
21219         var _this = this;
21220         
21221         if(this.rendered){
21222             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21223                 _this.getActionEl().addClass(this.disabledClass);
21224                 e.dom.disabled = true;
21225             });
21226         }
21227         
21228         this.disabled = true;
21229         this.fireEvent("disable", this);
21230         return this;
21231     },
21232
21233     enable : function()
21234     {
21235         if(this.inputType != 'radio'){
21236             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21237             return;
21238         }
21239         
21240         var _this = this;
21241         
21242         if(this.rendered){
21243             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21244                 _this.getActionEl().removeClass(this.disabledClass);
21245                 e.dom.disabled = false;
21246             });
21247         }
21248         
21249         this.disabled = false;
21250         this.fireEvent("enable", this);
21251         return this;
21252     },
21253     
21254     setBoxLabel : function(v)
21255     {
21256         this.boxLabel = v;
21257         
21258         if(this.rendered){
21259             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21260         }
21261     }
21262
21263 });
21264
21265 Roo.apply(Roo.bootstrap.CheckBox, {
21266     
21267     groups: {},
21268     
21269      /**
21270     * register a CheckBox Group
21271     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21272     */
21273     register : function(checkbox)
21274     {
21275         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21276             this.groups[checkbox.groupId] = {};
21277         }
21278         
21279         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21280             return;
21281         }
21282         
21283         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21284         
21285     },
21286     /**
21287     * fetch a CheckBox Group based on the group ID
21288     * @param {string} the group ID
21289     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21290     */
21291     get: function(groupId) {
21292         if (typeof(this.groups[groupId]) == 'undefined') {
21293             return false;
21294         }
21295         
21296         return this.groups[groupId] ;
21297     }
21298     
21299     
21300 });
21301 /*
21302  * - LGPL
21303  *
21304  * RadioItem
21305  * 
21306  */
21307
21308 /**
21309  * @class Roo.bootstrap.Radio
21310  * @extends Roo.bootstrap.Component
21311  * Bootstrap Radio class
21312  * @cfg {String} boxLabel - the label associated
21313  * @cfg {String} value - the value of radio
21314  * 
21315  * @constructor
21316  * Create a new Radio
21317  * @param {Object} config The config object
21318  */
21319 Roo.bootstrap.Radio = function(config){
21320     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21321     
21322 };
21323
21324 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21325     
21326     boxLabel : '',
21327     
21328     value : '',
21329     
21330     getAutoCreate : function()
21331     {
21332         var cfg = {
21333             tag : 'div',
21334             cls : 'form-group radio',
21335             cn : [
21336                 {
21337                     tag : 'label',
21338                     cls : 'box-label',
21339                     html : this.boxLabel
21340                 }
21341             ]
21342         };
21343         
21344         return cfg;
21345     },
21346     
21347     initEvents : function() 
21348     {
21349         this.parent().register(this);
21350         
21351         this.el.on('click', this.onClick, this);
21352         
21353     },
21354     
21355     onClick : function(e)
21356     {
21357         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21358             this.setChecked(true);
21359         }
21360     },
21361     
21362     setChecked : function(state, suppressEvent)
21363     {
21364         this.parent().setValue(this.value, suppressEvent);
21365         
21366     },
21367     
21368     setBoxLabel : function(v)
21369     {
21370         this.boxLabel = v;
21371         
21372         if(this.rendered){
21373             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21374         }
21375     }
21376     
21377 });
21378  
21379
21380  /*
21381  * - LGPL
21382  *
21383  * Input
21384  * 
21385  */
21386
21387 /**
21388  * @class Roo.bootstrap.SecurePass
21389  * @extends Roo.bootstrap.Input
21390  * Bootstrap SecurePass class
21391  *
21392  * 
21393  * @constructor
21394  * Create a new SecurePass
21395  * @param {Object} config The config object
21396  */
21397  
21398 Roo.bootstrap.SecurePass = function (config) {
21399     // these go here, so the translation tool can replace them..
21400     this.errors = {
21401         PwdEmpty: "Please type a password, and then retype it to confirm.",
21402         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21403         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21404         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21405         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21406         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21407         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21408         TooWeak: "Your password is Too Weak."
21409     },
21410     this.meterLabel = "Password strength:";
21411     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21412     this.meterClass = [
21413         "roo-password-meter-tooweak", 
21414         "roo-password-meter-weak", 
21415         "roo-password-meter-medium", 
21416         "roo-password-meter-strong", 
21417         "roo-password-meter-grey"
21418     ];
21419     
21420     this.errors = {};
21421     
21422     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21423 }
21424
21425 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21426     /**
21427      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21428      * {
21429      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21430      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21431      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21432      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21433      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21434      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21435      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21436      * })
21437      */
21438     // private
21439     
21440     meterWidth: 300,
21441     errorMsg :'',    
21442     errors: false,
21443     imageRoot: '/',
21444     /**
21445      * @cfg {String/Object} Label for the strength meter (defaults to
21446      * 'Password strength:')
21447      */
21448     // private
21449     meterLabel: '',
21450     /**
21451      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21452      * ['Weak', 'Medium', 'Strong'])
21453      */
21454     // private    
21455     pwdStrengths: false,    
21456     // private
21457     strength: 0,
21458     // private
21459     _lastPwd: null,
21460     // private
21461     kCapitalLetter: 0,
21462     kSmallLetter: 1,
21463     kDigit: 2,
21464     kPunctuation: 3,
21465     
21466     insecure: false,
21467     // private
21468     initEvents: function ()
21469     {
21470         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21471
21472         if (this.el.is('input[type=password]') && Roo.isSafari) {
21473             this.el.on('keydown', this.SafariOnKeyDown, this);
21474         }
21475
21476         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21477     },
21478     // private
21479     onRender: function (ct, position)
21480     {
21481         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21482         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21483         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21484
21485         this.trigger.createChild({
21486                    cn: [
21487                     {
21488                     //id: 'PwdMeter',
21489                     tag: 'div',
21490                     cls: 'roo-password-meter-grey col-xs-12',
21491                     style: {
21492                         //width: 0,
21493                         //width: this.meterWidth + 'px'                                                
21494                         }
21495                     },
21496                     {                            
21497                          cls: 'roo-password-meter-text'                          
21498                     }
21499                 ]            
21500         });
21501
21502          
21503         if (this.hideTrigger) {
21504             this.trigger.setDisplayed(false);
21505         }
21506         this.setSize(this.width || '', this.height || '');
21507     },
21508     // private
21509     onDestroy: function ()
21510     {
21511         if (this.trigger) {
21512             this.trigger.removeAllListeners();
21513             this.trigger.remove();
21514         }
21515         if (this.wrap) {
21516             this.wrap.remove();
21517         }
21518         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21519     },
21520     // private
21521     checkStrength: function ()
21522     {
21523         var pwd = this.inputEl().getValue();
21524         if (pwd == this._lastPwd) {
21525             return;
21526         }
21527
21528         var strength;
21529         if (this.ClientSideStrongPassword(pwd)) {
21530             strength = 3;
21531         } else if (this.ClientSideMediumPassword(pwd)) {
21532             strength = 2;
21533         } else if (this.ClientSideWeakPassword(pwd)) {
21534             strength = 1;
21535         } else {
21536             strength = 0;
21537         }
21538         
21539         Roo.log('strength1: ' + strength);
21540         
21541         //var pm = this.trigger.child('div/div/div').dom;
21542         var pm = this.trigger.child('div/div');
21543         pm.removeClass(this.meterClass);
21544         pm.addClass(this.meterClass[strength]);
21545                 
21546         
21547         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21548                 
21549         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21550         
21551         this._lastPwd = pwd;
21552     },
21553     reset: function ()
21554     {
21555         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21556         
21557         this._lastPwd = '';
21558         
21559         var pm = this.trigger.child('div/div');
21560         pm.removeClass(this.meterClass);
21561         pm.addClass('roo-password-meter-grey');        
21562         
21563         
21564         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21565         
21566         pt.innerHTML = '';
21567         this.inputEl().dom.type='password';
21568     },
21569     // private
21570     validateValue: function (value)
21571     {
21572         
21573         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21574             return false;
21575         }
21576         if (value.length == 0) {
21577             if (this.allowBlank) {
21578                 this.clearInvalid();
21579                 return true;
21580             }
21581
21582             this.markInvalid(this.errors.PwdEmpty);
21583             this.errorMsg = this.errors.PwdEmpty;
21584             return false;
21585         }
21586         
21587         if(this.insecure){
21588             return true;
21589         }
21590         
21591         if ('[\x21-\x7e]*'.match(value)) {
21592             this.markInvalid(this.errors.PwdBadChar);
21593             this.errorMsg = this.errors.PwdBadChar;
21594             return false;
21595         }
21596         if (value.length < 6) {
21597             this.markInvalid(this.errors.PwdShort);
21598             this.errorMsg = this.errors.PwdShort;
21599             return false;
21600         }
21601         if (value.length > 16) {
21602             this.markInvalid(this.errors.PwdLong);
21603             this.errorMsg = this.errors.PwdLong;
21604             return false;
21605         }
21606         var strength;
21607         if (this.ClientSideStrongPassword(value)) {
21608             strength = 3;
21609         } else if (this.ClientSideMediumPassword(value)) {
21610             strength = 2;
21611         } else if (this.ClientSideWeakPassword(value)) {
21612             strength = 1;
21613         } else {
21614             strength = 0;
21615         }
21616
21617         
21618         if (strength < 2) {
21619             //this.markInvalid(this.errors.TooWeak);
21620             this.errorMsg = this.errors.TooWeak;
21621             //return false;
21622         }
21623         
21624         
21625         console.log('strength2: ' + strength);
21626         
21627         //var pm = this.trigger.child('div/div/div').dom;
21628         
21629         var pm = this.trigger.child('div/div');
21630         pm.removeClass(this.meterClass);
21631         pm.addClass(this.meterClass[strength]);
21632                 
21633         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21634                 
21635         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21636         
21637         this.errorMsg = ''; 
21638         return true;
21639     },
21640     // private
21641     CharacterSetChecks: function (type)
21642     {
21643         this.type = type;
21644         this.fResult = false;
21645     },
21646     // private
21647     isctype: function (character, type)
21648     {
21649         switch (type) {  
21650             case this.kCapitalLetter:
21651                 if (character >= 'A' && character <= 'Z') {
21652                     return true;
21653                 }
21654                 break;
21655             
21656             case this.kSmallLetter:
21657                 if (character >= 'a' && character <= 'z') {
21658                     return true;
21659                 }
21660                 break;
21661             
21662             case this.kDigit:
21663                 if (character >= '0' && character <= '9') {
21664                     return true;
21665                 }
21666                 break;
21667             
21668             case this.kPunctuation:
21669                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21670                     return true;
21671                 }
21672                 break;
21673             
21674             default:
21675                 return false;
21676         }
21677
21678     },
21679     // private
21680     IsLongEnough: function (pwd, size)
21681     {
21682         return !(pwd == null || isNaN(size) || pwd.length < size);
21683     },
21684     // private
21685     SpansEnoughCharacterSets: function (word, nb)
21686     {
21687         if (!this.IsLongEnough(word, nb))
21688         {
21689             return false;
21690         }
21691
21692         var characterSetChecks = new Array(
21693             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21694             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21695         );
21696         
21697         for (var index = 0; index < word.length; ++index) {
21698             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21699                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21700                     characterSetChecks[nCharSet].fResult = true;
21701                     break;
21702                 }
21703             }
21704         }
21705
21706         var nCharSets = 0;
21707         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21708             if (characterSetChecks[nCharSet].fResult) {
21709                 ++nCharSets;
21710             }
21711         }
21712
21713         if (nCharSets < nb) {
21714             return false;
21715         }
21716         return true;
21717     },
21718     // private
21719     ClientSideStrongPassword: function (pwd)
21720     {
21721         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21722     },
21723     // private
21724     ClientSideMediumPassword: function (pwd)
21725     {
21726         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21727     },
21728     // private
21729     ClientSideWeakPassword: function (pwd)
21730     {
21731         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21732     }
21733           
21734 })//<script type="text/javascript">
21735
21736 /*
21737  * Based  Ext JS Library 1.1.1
21738  * Copyright(c) 2006-2007, Ext JS, LLC.
21739  * LGPL
21740  *
21741  */
21742  
21743 /**
21744  * @class Roo.HtmlEditorCore
21745  * @extends Roo.Component
21746  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21747  *
21748  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21749  */
21750
21751 Roo.HtmlEditorCore = function(config){
21752     
21753     
21754     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21755     
21756     
21757     this.addEvents({
21758         /**
21759          * @event initialize
21760          * Fires when the editor is fully initialized (including the iframe)
21761          * @param {Roo.HtmlEditorCore} this
21762          */
21763         initialize: true,
21764         /**
21765          * @event activate
21766          * Fires when the editor is first receives the focus. Any insertion must wait
21767          * until after this event.
21768          * @param {Roo.HtmlEditorCore} this
21769          */
21770         activate: true,
21771          /**
21772          * @event beforesync
21773          * Fires before the textarea is updated with content from the editor iframe. Return false
21774          * to cancel the sync.
21775          * @param {Roo.HtmlEditorCore} this
21776          * @param {String} html
21777          */
21778         beforesync: true,
21779          /**
21780          * @event beforepush
21781          * Fires before the iframe editor is updated with content from the textarea. Return false
21782          * to cancel the push.
21783          * @param {Roo.HtmlEditorCore} this
21784          * @param {String} html
21785          */
21786         beforepush: true,
21787          /**
21788          * @event sync
21789          * Fires when the textarea is updated with content from the editor iframe.
21790          * @param {Roo.HtmlEditorCore} this
21791          * @param {String} html
21792          */
21793         sync: true,
21794          /**
21795          * @event push
21796          * Fires when the iframe editor is updated with content from the textarea.
21797          * @param {Roo.HtmlEditorCore} this
21798          * @param {String} html
21799          */
21800         push: true,
21801         
21802         /**
21803          * @event editorevent
21804          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21805          * @param {Roo.HtmlEditorCore} this
21806          */
21807         editorevent: true
21808         
21809     });
21810     
21811     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21812     
21813     // defaults : white / black...
21814     this.applyBlacklists();
21815     
21816     
21817     
21818 };
21819
21820
21821 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21822
21823
21824      /**
21825      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21826      */
21827     
21828     owner : false,
21829     
21830      /**
21831      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21832      *                        Roo.resizable.
21833      */
21834     resizable : false,
21835      /**
21836      * @cfg {Number} height (in pixels)
21837      */   
21838     height: 300,
21839    /**
21840      * @cfg {Number} width (in pixels)
21841      */   
21842     width: 500,
21843     
21844     /**
21845      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21846      * 
21847      */
21848     stylesheets: false,
21849     
21850     // id of frame..
21851     frameId: false,
21852     
21853     // private properties
21854     validationEvent : false,
21855     deferHeight: true,
21856     initialized : false,
21857     activated : false,
21858     sourceEditMode : false,
21859     onFocus : Roo.emptyFn,
21860     iframePad:3,
21861     hideMode:'offsets',
21862     
21863     clearUp: true,
21864     
21865     // blacklist + whitelisted elements..
21866     black: false,
21867     white: false,
21868      
21869     bodyCls : '',
21870
21871     /**
21872      * Protected method that will not generally be called directly. It
21873      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21874      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21875      */
21876     getDocMarkup : function(){
21877         // body styles..
21878         var st = '';
21879         
21880         // inherit styels from page...?? 
21881         if (this.stylesheets === false) {
21882             
21883             Roo.get(document.head).select('style').each(function(node) {
21884                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21885             });
21886             
21887             Roo.get(document.head).select('link').each(function(node) { 
21888                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21889             });
21890             
21891         } else if (!this.stylesheets.length) {
21892                 // simple..
21893                 st = '<style type="text/css">' +
21894                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21895                    '</style>';
21896         } else { 
21897             st = '<style type="text/css">' +
21898                     this.stylesheets +
21899                 '</style>';
21900         }
21901         
21902         st +=  '<style type="text/css">' +
21903             'IMG { cursor: pointer } ' +
21904         '</style>';
21905
21906         var cls = 'roo-htmleditor-body';
21907         
21908         if(this.bodyCls.length){
21909             cls += ' ' + this.bodyCls;
21910         }
21911         
21912         return '<html><head>' + st  +
21913             //<style type="text/css">' +
21914             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21915             //'</style>' +
21916             ' </head><body class="' +  cls + '"></body></html>';
21917     },
21918
21919     // private
21920     onRender : function(ct, position)
21921     {
21922         var _t = this;
21923         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21924         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21925         
21926         
21927         this.el.dom.style.border = '0 none';
21928         this.el.dom.setAttribute('tabIndex', -1);
21929         this.el.addClass('x-hidden hide');
21930         
21931         
21932         
21933         if(Roo.isIE){ // fix IE 1px bogus margin
21934             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21935         }
21936        
21937         
21938         this.frameId = Roo.id();
21939         
21940          
21941         
21942         var iframe = this.owner.wrap.createChild({
21943             tag: 'iframe',
21944             cls: 'form-control', // bootstrap..
21945             id: this.frameId,
21946             name: this.frameId,
21947             frameBorder : 'no',
21948             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21949         }, this.el
21950         );
21951         
21952         
21953         this.iframe = iframe.dom;
21954
21955          this.assignDocWin();
21956         
21957         this.doc.designMode = 'on';
21958        
21959         this.doc.open();
21960         this.doc.write(this.getDocMarkup());
21961         this.doc.close();
21962
21963         
21964         var task = { // must defer to wait for browser to be ready
21965             run : function(){
21966                 //console.log("run task?" + this.doc.readyState);
21967                 this.assignDocWin();
21968                 if(this.doc.body || this.doc.readyState == 'complete'){
21969                     try {
21970                         this.doc.designMode="on";
21971                     } catch (e) {
21972                         return;
21973                     }
21974                     Roo.TaskMgr.stop(task);
21975                     this.initEditor.defer(10, this);
21976                 }
21977             },
21978             interval : 10,
21979             duration: 10000,
21980             scope: this
21981         };
21982         Roo.TaskMgr.start(task);
21983
21984     },
21985
21986     // private
21987     onResize : function(w, h)
21988     {
21989          Roo.log('resize: ' +w + ',' + h );
21990         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21991         if(!this.iframe){
21992             return;
21993         }
21994         if(typeof w == 'number'){
21995             
21996             this.iframe.style.width = w + 'px';
21997         }
21998         if(typeof h == 'number'){
21999             
22000             this.iframe.style.height = h + 'px';
22001             if(this.doc){
22002                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22003             }
22004         }
22005         
22006     },
22007
22008     /**
22009      * Toggles the editor between standard and source edit mode.
22010      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22011      */
22012     toggleSourceEdit : function(sourceEditMode){
22013         
22014         this.sourceEditMode = sourceEditMode === true;
22015         
22016         if(this.sourceEditMode){
22017  
22018             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22019             
22020         }else{
22021             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22022             //this.iframe.className = '';
22023             this.deferFocus();
22024         }
22025         //this.setSize(this.owner.wrap.getSize());
22026         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22027     },
22028
22029     
22030   
22031
22032     /**
22033      * Protected method that will not generally be called directly. If you need/want
22034      * custom HTML cleanup, this is the method you should override.
22035      * @param {String} html The HTML to be cleaned
22036      * return {String} The cleaned HTML
22037      */
22038     cleanHtml : function(html){
22039         html = String(html);
22040         if(html.length > 5){
22041             if(Roo.isSafari){ // strip safari nonsense
22042                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22043             }
22044         }
22045         if(html == '&nbsp;'){
22046             html = '';
22047         }
22048         return html;
22049     },
22050
22051     /**
22052      * HTML Editor -> Textarea
22053      * Protected method that will not generally be called directly. Syncs the contents
22054      * of the editor iframe with the textarea.
22055      */
22056     syncValue : function(){
22057         if(this.initialized){
22058             var bd = (this.doc.body || this.doc.documentElement);
22059             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22060             var html = bd.innerHTML;
22061             if(Roo.isSafari){
22062                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22063                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22064                 if(m && m[1]){
22065                     html = '<div style="'+m[0]+'">' + html + '</div>';
22066                 }
22067             }
22068             html = this.cleanHtml(html);
22069             // fix up the special chars.. normaly like back quotes in word...
22070             // however we do not want to do this with chinese..
22071             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22072                 var cc = b.charCodeAt();
22073                 if (
22074                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22075                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22076                     (cc >= 0xf900 && cc < 0xfb00 )
22077                 ) {
22078                         return b;
22079                 }
22080                 return "&#"+cc+";" 
22081             });
22082             if(this.owner.fireEvent('beforesync', this, html) !== false){
22083                 this.el.dom.value = html;
22084                 this.owner.fireEvent('sync', this, html);
22085             }
22086         }
22087     },
22088
22089     /**
22090      * Protected method that will not generally be called directly. Pushes the value of the textarea
22091      * into the iframe editor.
22092      */
22093     pushValue : function(){
22094         if(this.initialized){
22095             var v = this.el.dom.value.trim();
22096             
22097 //            if(v.length < 1){
22098 //                v = '&#160;';
22099 //            }
22100             
22101             if(this.owner.fireEvent('beforepush', this, v) !== false){
22102                 var d = (this.doc.body || this.doc.documentElement);
22103                 d.innerHTML = v;
22104                 this.cleanUpPaste();
22105                 this.el.dom.value = d.innerHTML;
22106                 this.owner.fireEvent('push', this, v);
22107             }
22108         }
22109     },
22110
22111     // private
22112     deferFocus : function(){
22113         this.focus.defer(10, this);
22114     },
22115
22116     // doc'ed in Field
22117     focus : function(){
22118         if(this.win && !this.sourceEditMode){
22119             this.win.focus();
22120         }else{
22121             this.el.focus();
22122         }
22123     },
22124     
22125     assignDocWin: function()
22126     {
22127         var iframe = this.iframe;
22128         
22129          if(Roo.isIE){
22130             this.doc = iframe.contentWindow.document;
22131             this.win = iframe.contentWindow;
22132         } else {
22133 //            if (!Roo.get(this.frameId)) {
22134 //                return;
22135 //            }
22136 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22137 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22138             
22139             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22140                 return;
22141             }
22142             
22143             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22144             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22145         }
22146     },
22147     
22148     // private
22149     initEditor : function(){
22150         //console.log("INIT EDITOR");
22151         this.assignDocWin();
22152         
22153         
22154         
22155         this.doc.designMode="on";
22156         this.doc.open();
22157         this.doc.write(this.getDocMarkup());
22158         this.doc.close();
22159         
22160         var dbody = (this.doc.body || this.doc.documentElement);
22161         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22162         // this copies styles from the containing element into thsi one..
22163         // not sure why we need all of this..
22164         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22165         
22166         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22167         //ss['background-attachment'] = 'fixed'; // w3c
22168         dbody.bgProperties = 'fixed'; // ie
22169         //Roo.DomHelper.applyStyles(dbody, ss);
22170         Roo.EventManager.on(this.doc, {
22171             //'mousedown': this.onEditorEvent,
22172             'mouseup': this.onEditorEvent,
22173             'dblclick': this.onEditorEvent,
22174             'click': this.onEditorEvent,
22175             'keyup': this.onEditorEvent,
22176             buffer:100,
22177             scope: this
22178         });
22179         if(Roo.isGecko){
22180             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22181         }
22182         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22183             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22184         }
22185         this.initialized = true;
22186
22187         this.owner.fireEvent('initialize', this);
22188         this.pushValue();
22189     },
22190
22191     // private
22192     onDestroy : function(){
22193         
22194         
22195         
22196         if(this.rendered){
22197             
22198             //for (var i =0; i < this.toolbars.length;i++) {
22199             //    // fixme - ask toolbars for heights?
22200             //    this.toolbars[i].onDestroy();
22201            // }
22202             
22203             //this.wrap.dom.innerHTML = '';
22204             //this.wrap.remove();
22205         }
22206     },
22207
22208     // private
22209     onFirstFocus : function(){
22210         
22211         this.assignDocWin();
22212         
22213         
22214         this.activated = true;
22215          
22216     
22217         if(Roo.isGecko){ // prevent silly gecko errors
22218             this.win.focus();
22219             var s = this.win.getSelection();
22220             if(!s.focusNode || s.focusNode.nodeType != 3){
22221                 var r = s.getRangeAt(0);
22222                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22223                 r.collapse(true);
22224                 this.deferFocus();
22225             }
22226             try{
22227                 this.execCmd('useCSS', true);
22228                 this.execCmd('styleWithCSS', false);
22229             }catch(e){}
22230         }
22231         this.owner.fireEvent('activate', this);
22232     },
22233
22234     // private
22235     adjustFont: function(btn){
22236         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22237         //if(Roo.isSafari){ // safari
22238         //    adjust *= 2;
22239        // }
22240         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22241         if(Roo.isSafari){ // safari
22242             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22243             v =  (v < 10) ? 10 : v;
22244             v =  (v > 48) ? 48 : v;
22245             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22246             
22247         }
22248         
22249         
22250         v = Math.max(1, v+adjust);
22251         
22252         this.execCmd('FontSize', v  );
22253     },
22254
22255     onEditorEvent : function(e)
22256     {
22257         this.owner.fireEvent('editorevent', this, e);
22258       //  this.updateToolbar();
22259         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22260     },
22261
22262     insertTag : function(tg)
22263     {
22264         // could be a bit smarter... -> wrap the current selected tRoo..
22265         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22266             
22267             range = this.createRange(this.getSelection());
22268             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22269             wrappingNode.appendChild(range.extractContents());
22270             range.insertNode(wrappingNode);
22271
22272             return;
22273             
22274             
22275             
22276         }
22277         this.execCmd("formatblock",   tg);
22278         
22279     },
22280     
22281     insertText : function(txt)
22282     {
22283         
22284         
22285         var range = this.createRange();
22286         range.deleteContents();
22287                //alert(Sender.getAttribute('label'));
22288                
22289         range.insertNode(this.doc.createTextNode(txt));
22290     } ,
22291     
22292      
22293
22294     /**
22295      * Executes a Midas editor command on the editor document and performs necessary focus and
22296      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22297      * @param {String} cmd The Midas command
22298      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22299      */
22300     relayCmd : function(cmd, value){
22301         this.win.focus();
22302         this.execCmd(cmd, value);
22303         this.owner.fireEvent('editorevent', this);
22304         //this.updateToolbar();
22305         this.owner.deferFocus();
22306     },
22307
22308     /**
22309      * Executes a Midas editor command directly on the editor document.
22310      * For visual commands, you should use {@link #relayCmd} instead.
22311      * <b>This should only be called after the editor is initialized.</b>
22312      * @param {String} cmd The Midas command
22313      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22314      */
22315     execCmd : function(cmd, value){
22316         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22317         this.syncValue();
22318     },
22319  
22320  
22321    
22322     /**
22323      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22324      * to insert tRoo.
22325      * @param {String} text | dom node.. 
22326      */
22327     insertAtCursor : function(text)
22328     {
22329         
22330         if(!this.activated){
22331             return;
22332         }
22333         /*
22334         if(Roo.isIE){
22335             this.win.focus();
22336             var r = this.doc.selection.createRange();
22337             if(r){
22338                 r.collapse(true);
22339                 r.pasteHTML(text);
22340                 this.syncValue();
22341                 this.deferFocus();
22342             
22343             }
22344             return;
22345         }
22346         */
22347         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22348             this.win.focus();
22349             
22350             
22351             // from jquery ui (MIT licenced)
22352             var range, node;
22353             var win = this.win;
22354             
22355             if (win.getSelection && win.getSelection().getRangeAt) {
22356                 range = win.getSelection().getRangeAt(0);
22357                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22358                 range.insertNode(node);
22359             } else if (win.document.selection && win.document.selection.createRange) {
22360                 // no firefox support
22361                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22362                 win.document.selection.createRange().pasteHTML(txt);
22363             } else {
22364                 // no firefox support
22365                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22366                 this.execCmd('InsertHTML', txt);
22367             } 
22368             
22369             this.syncValue();
22370             
22371             this.deferFocus();
22372         }
22373     },
22374  // private
22375     mozKeyPress : function(e){
22376         if(e.ctrlKey){
22377             var c = e.getCharCode(), cmd;
22378           
22379             if(c > 0){
22380                 c = String.fromCharCode(c).toLowerCase();
22381                 switch(c){
22382                     case 'b':
22383                         cmd = 'bold';
22384                         break;
22385                     case 'i':
22386                         cmd = 'italic';
22387                         break;
22388                     
22389                     case 'u':
22390                         cmd = 'underline';
22391                         break;
22392                     
22393                     case 'v':
22394                         this.cleanUpPaste.defer(100, this);
22395                         return;
22396                         
22397                 }
22398                 if(cmd){
22399                     this.win.focus();
22400                     this.execCmd(cmd);
22401                     this.deferFocus();
22402                     e.preventDefault();
22403                 }
22404                 
22405             }
22406         }
22407     },
22408
22409     // private
22410     fixKeys : function(){ // load time branching for fastest keydown performance
22411         if(Roo.isIE){
22412             return function(e){
22413                 var k = e.getKey(), r;
22414                 if(k == e.TAB){
22415                     e.stopEvent();
22416                     r = this.doc.selection.createRange();
22417                     if(r){
22418                         r.collapse(true);
22419                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22420                         this.deferFocus();
22421                     }
22422                     return;
22423                 }
22424                 
22425                 if(k == e.ENTER){
22426                     r = this.doc.selection.createRange();
22427                     if(r){
22428                         var target = r.parentElement();
22429                         if(!target || target.tagName.toLowerCase() != 'li'){
22430                             e.stopEvent();
22431                             r.pasteHTML('<br />');
22432                             r.collapse(false);
22433                             r.select();
22434                         }
22435                     }
22436                 }
22437                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22438                     this.cleanUpPaste.defer(100, this);
22439                     return;
22440                 }
22441                 
22442                 
22443             };
22444         }else if(Roo.isOpera){
22445             return function(e){
22446                 var k = e.getKey();
22447                 if(k == e.TAB){
22448                     e.stopEvent();
22449                     this.win.focus();
22450                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22451                     this.deferFocus();
22452                 }
22453                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22454                     this.cleanUpPaste.defer(100, this);
22455                     return;
22456                 }
22457                 
22458             };
22459         }else if(Roo.isSafari){
22460             return function(e){
22461                 var k = e.getKey();
22462                 
22463                 if(k == e.TAB){
22464                     e.stopEvent();
22465                     this.execCmd('InsertText','\t');
22466                     this.deferFocus();
22467                     return;
22468                 }
22469                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22470                     this.cleanUpPaste.defer(100, this);
22471                     return;
22472                 }
22473                 
22474              };
22475         }
22476     }(),
22477     
22478     getAllAncestors: function()
22479     {
22480         var p = this.getSelectedNode();
22481         var a = [];
22482         if (!p) {
22483             a.push(p); // push blank onto stack..
22484             p = this.getParentElement();
22485         }
22486         
22487         
22488         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22489             a.push(p);
22490             p = p.parentNode;
22491         }
22492         a.push(this.doc.body);
22493         return a;
22494     },
22495     lastSel : false,
22496     lastSelNode : false,
22497     
22498     
22499     getSelection : function() 
22500     {
22501         this.assignDocWin();
22502         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22503     },
22504     
22505     getSelectedNode: function() 
22506     {
22507         // this may only work on Gecko!!!
22508         
22509         // should we cache this!!!!
22510         
22511         
22512         
22513          
22514         var range = this.createRange(this.getSelection()).cloneRange();
22515         
22516         if (Roo.isIE) {
22517             var parent = range.parentElement();
22518             while (true) {
22519                 var testRange = range.duplicate();
22520                 testRange.moveToElementText(parent);
22521                 if (testRange.inRange(range)) {
22522                     break;
22523                 }
22524                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22525                     break;
22526                 }
22527                 parent = parent.parentElement;
22528             }
22529             return parent;
22530         }
22531         
22532         // is ancestor a text element.
22533         var ac =  range.commonAncestorContainer;
22534         if (ac.nodeType == 3) {
22535             ac = ac.parentNode;
22536         }
22537         
22538         var ar = ac.childNodes;
22539          
22540         var nodes = [];
22541         var other_nodes = [];
22542         var has_other_nodes = false;
22543         for (var i=0;i<ar.length;i++) {
22544             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22545                 continue;
22546             }
22547             // fullly contained node.
22548             
22549             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22550                 nodes.push(ar[i]);
22551                 continue;
22552             }
22553             
22554             // probably selected..
22555             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22556                 other_nodes.push(ar[i]);
22557                 continue;
22558             }
22559             // outer..
22560             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22561                 continue;
22562             }
22563             
22564             
22565             has_other_nodes = true;
22566         }
22567         if (!nodes.length && other_nodes.length) {
22568             nodes= other_nodes;
22569         }
22570         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22571             return false;
22572         }
22573         
22574         return nodes[0];
22575     },
22576     createRange: function(sel)
22577     {
22578         // this has strange effects when using with 
22579         // top toolbar - not sure if it's a great idea.
22580         //this.editor.contentWindow.focus();
22581         if (typeof sel != "undefined") {
22582             try {
22583                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22584             } catch(e) {
22585                 return this.doc.createRange();
22586             }
22587         } else {
22588             return this.doc.createRange();
22589         }
22590     },
22591     getParentElement: function()
22592     {
22593         
22594         this.assignDocWin();
22595         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22596         
22597         var range = this.createRange(sel);
22598          
22599         try {
22600             var p = range.commonAncestorContainer;
22601             while (p.nodeType == 3) { // text node
22602                 p = p.parentNode;
22603             }
22604             return p;
22605         } catch (e) {
22606             return null;
22607         }
22608     
22609     },
22610     /***
22611      *
22612      * Range intersection.. the hard stuff...
22613      *  '-1' = before
22614      *  '0' = hits..
22615      *  '1' = after.
22616      *         [ -- selected range --- ]
22617      *   [fail]                        [fail]
22618      *
22619      *    basically..
22620      *      if end is before start or  hits it. fail.
22621      *      if start is after end or hits it fail.
22622      *
22623      *   if either hits (but other is outside. - then it's not 
22624      *   
22625      *    
22626      **/
22627     
22628     
22629     // @see http://www.thismuchiknow.co.uk/?p=64.
22630     rangeIntersectsNode : function(range, node)
22631     {
22632         var nodeRange = node.ownerDocument.createRange();
22633         try {
22634             nodeRange.selectNode(node);
22635         } catch (e) {
22636             nodeRange.selectNodeContents(node);
22637         }
22638     
22639         var rangeStartRange = range.cloneRange();
22640         rangeStartRange.collapse(true);
22641     
22642         var rangeEndRange = range.cloneRange();
22643         rangeEndRange.collapse(false);
22644     
22645         var nodeStartRange = nodeRange.cloneRange();
22646         nodeStartRange.collapse(true);
22647     
22648         var nodeEndRange = nodeRange.cloneRange();
22649         nodeEndRange.collapse(false);
22650     
22651         return rangeStartRange.compareBoundaryPoints(
22652                  Range.START_TO_START, nodeEndRange) == -1 &&
22653                rangeEndRange.compareBoundaryPoints(
22654                  Range.START_TO_START, nodeStartRange) == 1;
22655         
22656          
22657     },
22658     rangeCompareNode : function(range, node)
22659     {
22660         var nodeRange = node.ownerDocument.createRange();
22661         try {
22662             nodeRange.selectNode(node);
22663         } catch (e) {
22664             nodeRange.selectNodeContents(node);
22665         }
22666         
22667         
22668         range.collapse(true);
22669     
22670         nodeRange.collapse(true);
22671      
22672         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22673         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22674          
22675         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22676         
22677         var nodeIsBefore   =  ss == 1;
22678         var nodeIsAfter    = ee == -1;
22679         
22680         if (nodeIsBefore && nodeIsAfter) {
22681             return 0; // outer
22682         }
22683         if (!nodeIsBefore && nodeIsAfter) {
22684             return 1; //right trailed.
22685         }
22686         
22687         if (nodeIsBefore && !nodeIsAfter) {
22688             return 2;  // left trailed.
22689         }
22690         // fully contined.
22691         return 3;
22692     },
22693
22694     // private? - in a new class?
22695     cleanUpPaste :  function()
22696     {
22697         // cleans up the whole document..
22698         Roo.log('cleanuppaste');
22699         
22700         this.cleanUpChildren(this.doc.body);
22701         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22702         if (clean != this.doc.body.innerHTML) {
22703             this.doc.body.innerHTML = clean;
22704         }
22705         
22706     },
22707     
22708     cleanWordChars : function(input) {// change the chars to hex code
22709         var he = Roo.HtmlEditorCore;
22710         
22711         var output = input;
22712         Roo.each(he.swapCodes, function(sw) { 
22713             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22714             
22715             output = output.replace(swapper, sw[1]);
22716         });
22717         
22718         return output;
22719     },
22720     
22721     
22722     cleanUpChildren : function (n)
22723     {
22724         if (!n.childNodes.length) {
22725             return;
22726         }
22727         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22728            this.cleanUpChild(n.childNodes[i]);
22729         }
22730     },
22731     
22732     
22733         
22734     
22735     cleanUpChild : function (node)
22736     {
22737         var ed = this;
22738         //console.log(node);
22739         if (node.nodeName == "#text") {
22740             // clean up silly Windows -- stuff?
22741             return; 
22742         }
22743         if (node.nodeName == "#comment") {
22744             node.parentNode.removeChild(node);
22745             // clean up silly Windows -- stuff?
22746             return; 
22747         }
22748         var lcname = node.tagName.toLowerCase();
22749         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22750         // whitelist of tags..
22751         
22752         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22753             // remove node.
22754             node.parentNode.removeChild(node);
22755             return;
22756             
22757         }
22758         
22759         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22760         
22761         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22762         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22763         
22764         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22765         //    remove_keep_children = true;
22766         //}
22767         
22768         if (remove_keep_children) {
22769             this.cleanUpChildren(node);
22770             // inserts everything just before this node...
22771             while (node.childNodes.length) {
22772                 var cn = node.childNodes[0];
22773                 node.removeChild(cn);
22774                 node.parentNode.insertBefore(cn, node);
22775             }
22776             node.parentNode.removeChild(node);
22777             return;
22778         }
22779         
22780         if (!node.attributes || !node.attributes.length) {
22781             this.cleanUpChildren(node);
22782             return;
22783         }
22784         
22785         function cleanAttr(n,v)
22786         {
22787             
22788             if (v.match(/^\./) || v.match(/^\//)) {
22789                 return;
22790             }
22791             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22792                 return;
22793             }
22794             if (v.match(/^#/)) {
22795                 return;
22796             }
22797 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22798             node.removeAttribute(n);
22799             
22800         }
22801         
22802         var cwhite = this.cwhite;
22803         var cblack = this.cblack;
22804             
22805         function cleanStyle(n,v)
22806         {
22807             if (v.match(/expression/)) { //XSS?? should we even bother..
22808                 node.removeAttribute(n);
22809                 return;
22810             }
22811             
22812             var parts = v.split(/;/);
22813             var clean = [];
22814             
22815             Roo.each(parts, function(p) {
22816                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22817                 if (!p.length) {
22818                     return true;
22819                 }
22820                 var l = p.split(':').shift().replace(/\s+/g,'');
22821                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22822                 
22823                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22824 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22825                     //node.removeAttribute(n);
22826                     return true;
22827                 }
22828                 //Roo.log()
22829                 // only allow 'c whitelisted system attributes'
22830                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22831 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22832                     //node.removeAttribute(n);
22833                     return true;
22834                 }
22835                 
22836                 
22837                  
22838                 
22839                 clean.push(p);
22840                 return true;
22841             });
22842             if (clean.length) { 
22843                 node.setAttribute(n, clean.join(';'));
22844             } else {
22845                 node.removeAttribute(n);
22846             }
22847             
22848         }
22849         
22850         
22851         for (var i = node.attributes.length-1; i > -1 ; i--) {
22852             var a = node.attributes[i];
22853             //console.log(a);
22854             
22855             if (a.name.toLowerCase().substr(0,2)=='on')  {
22856                 node.removeAttribute(a.name);
22857                 continue;
22858             }
22859             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22860                 node.removeAttribute(a.name);
22861                 continue;
22862             }
22863             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22864                 cleanAttr(a.name,a.value); // fixme..
22865                 continue;
22866             }
22867             if (a.name == 'style') {
22868                 cleanStyle(a.name,a.value);
22869                 continue;
22870             }
22871             /// clean up MS crap..
22872             // tecnically this should be a list of valid class'es..
22873             
22874             
22875             if (a.name == 'class') {
22876                 if (a.value.match(/^Mso/)) {
22877                     node.className = '';
22878                 }
22879                 
22880                 if (a.value.match(/^body$/)) {
22881                     node.className = '';
22882                 }
22883                 continue;
22884             }
22885             
22886             // style cleanup!?
22887             // class cleanup?
22888             
22889         }
22890         
22891         
22892         this.cleanUpChildren(node);
22893         
22894         
22895     },
22896     
22897     /**
22898      * Clean up MS wordisms...
22899      */
22900     cleanWord : function(node)
22901     {
22902         
22903         
22904         if (!node) {
22905             this.cleanWord(this.doc.body);
22906             return;
22907         }
22908         if (node.nodeName == "#text") {
22909             // clean up silly Windows -- stuff?
22910             return; 
22911         }
22912         if (node.nodeName == "#comment") {
22913             node.parentNode.removeChild(node);
22914             // clean up silly Windows -- stuff?
22915             return; 
22916         }
22917         
22918         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22919             node.parentNode.removeChild(node);
22920             return;
22921         }
22922         
22923         // remove - but keep children..
22924         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22925             while (node.childNodes.length) {
22926                 var cn = node.childNodes[0];
22927                 node.removeChild(cn);
22928                 node.parentNode.insertBefore(cn, node);
22929             }
22930             node.parentNode.removeChild(node);
22931             this.iterateChildren(node, this.cleanWord);
22932             return;
22933         }
22934         // clean styles
22935         if (node.className.length) {
22936             
22937             var cn = node.className.split(/\W+/);
22938             var cna = [];
22939             Roo.each(cn, function(cls) {
22940                 if (cls.match(/Mso[a-zA-Z]+/)) {
22941                     return;
22942                 }
22943                 cna.push(cls);
22944             });
22945             node.className = cna.length ? cna.join(' ') : '';
22946             if (!cna.length) {
22947                 node.removeAttribute("class");
22948             }
22949         }
22950         
22951         if (node.hasAttribute("lang")) {
22952             node.removeAttribute("lang");
22953         }
22954         
22955         if (node.hasAttribute("style")) {
22956             
22957             var styles = node.getAttribute("style").split(";");
22958             var nstyle = [];
22959             Roo.each(styles, function(s) {
22960                 if (!s.match(/:/)) {
22961                     return;
22962                 }
22963                 var kv = s.split(":");
22964                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22965                     return;
22966                 }
22967                 // what ever is left... we allow.
22968                 nstyle.push(s);
22969             });
22970             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22971             if (!nstyle.length) {
22972                 node.removeAttribute('style');
22973             }
22974         }
22975         this.iterateChildren(node, this.cleanWord);
22976         
22977         
22978         
22979     },
22980     /**
22981      * iterateChildren of a Node, calling fn each time, using this as the scole..
22982      * @param {DomNode} node node to iterate children of.
22983      * @param {Function} fn method of this class to call on each item.
22984      */
22985     iterateChildren : function(node, fn)
22986     {
22987         if (!node.childNodes.length) {
22988                 return;
22989         }
22990         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22991            fn.call(this, node.childNodes[i])
22992         }
22993     },
22994     
22995     
22996     /**
22997      * cleanTableWidths.
22998      *
22999      * Quite often pasting from word etc.. results in tables with column and widths.
23000      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23001      *
23002      */
23003     cleanTableWidths : function(node)
23004     {
23005          
23006          
23007         if (!node) {
23008             this.cleanTableWidths(this.doc.body);
23009             return;
23010         }
23011         
23012         // ignore list...
23013         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23014             return; 
23015         }
23016         Roo.log(node.tagName);
23017         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23018             this.iterateChildren(node, this.cleanTableWidths);
23019             return;
23020         }
23021         if (node.hasAttribute('width')) {
23022             node.removeAttribute('width');
23023         }
23024         
23025          
23026         if (node.hasAttribute("style")) {
23027             // pretty basic...
23028             
23029             var styles = node.getAttribute("style").split(";");
23030             var nstyle = [];
23031             Roo.each(styles, function(s) {
23032                 if (!s.match(/:/)) {
23033                     return;
23034                 }
23035                 var kv = s.split(":");
23036                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23037                     return;
23038                 }
23039                 // what ever is left... we allow.
23040                 nstyle.push(s);
23041             });
23042             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23043             if (!nstyle.length) {
23044                 node.removeAttribute('style');
23045             }
23046         }
23047         
23048         this.iterateChildren(node, this.cleanTableWidths);
23049         
23050         
23051     },
23052     
23053     
23054     
23055     
23056     domToHTML : function(currentElement, depth, nopadtext) {
23057         
23058         depth = depth || 0;
23059         nopadtext = nopadtext || false;
23060     
23061         if (!currentElement) {
23062             return this.domToHTML(this.doc.body);
23063         }
23064         
23065         //Roo.log(currentElement);
23066         var j;
23067         var allText = false;
23068         var nodeName = currentElement.nodeName;
23069         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23070         
23071         if  (nodeName == '#text') {
23072             
23073             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23074         }
23075         
23076         
23077         var ret = '';
23078         if (nodeName != 'BODY') {
23079              
23080             var i = 0;
23081             // Prints the node tagName, such as <A>, <IMG>, etc
23082             if (tagName) {
23083                 var attr = [];
23084                 for(i = 0; i < currentElement.attributes.length;i++) {
23085                     // quoting?
23086                     var aname = currentElement.attributes.item(i).name;
23087                     if (!currentElement.attributes.item(i).value.length) {
23088                         continue;
23089                     }
23090                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23091                 }
23092                 
23093                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23094             } 
23095             else {
23096                 
23097                 // eack
23098             }
23099         } else {
23100             tagName = false;
23101         }
23102         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23103             return ret;
23104         }
23105         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23106             nopadtext = true;
23107         }
23108         
23109         
23110         // Traverse the tree
23111         i = 0;
23112         var currentElementChild = currentElement.childNodes.item(i);
23113         var allText = true;
23114         var innerHTML  = '';
23115         lastnode = '';
23116         while (currentElementChild) {
23117             // Formatting code (indent the tree so it looks nice on the screen)
23118             var nopad = nopadtext;
23119             if (lastnode == 'SPAN') {
23120                 nopad  = true;
23121             }
23122             // text
23123             if  (currentElementChild.nodeName == '#text') {
23124                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23125                 toadd = nopadtext ? toadd : toadd.trim();
23126                 if (!nopad && toadd.length > 80) {
23127                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23128                 }
23129                 innerHTML  += toadd;
23130                 
23131                 i++;
23132                 currentElementChild = currentElement.childNodes.item(i);
23133                 lastNode = '';
23134                 continue;
23135             }
23136             allText = false;
23137             
23138             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23139                 
23140             // Recursively traverse the tree structure of the child node
23141             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23142             lastnode = currentElementChild.nodeName;
23143             i++;
23144             currentElementChild=currentElement.childNodes.item(i);
23145         }
23146         
23147         ret += innerHTML;
23148         
23149         if (!allText) {
23150                 // The remaining code is mostly for formatting the tree
23151             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23152         }
23153         
23154         
23155         if (tagName) {
23156             ret+= "</"+tagName+">";
23157         }
23158         return ret;
23159         
23160     },
23161         
23162     applyBlacklists : function()
23163     {
23164         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23165         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23166         
23167         this.white = [];
23168         this.black = [];
23169         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23170             if (b.indexOf(tag) > -1) {
23171                 return;
23172             }
23173             this.white.push(tag);
23174             
23175         }, this);
23176         
23177         Roo.each(w, function(tag) {
23178             if (b.indexOf(tag) > -1) {
23179                 return;
23180             }
23181             if (this.white.indexOf(tag) > -1) {
23182                 return;
23183             }
23184             this.white.push(tag);
23185             
23186         }, this);
23187         
23188         
23189         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23190             if (w.indexOf(tag) > -1) {
23191                 return;
23192             }
23193             this.black.push(tag);
23194             
23195         }, this);
23196         
23197         Roo.each(b, function(tag) {
23198             if (w.indexOf(tag) > -1) {
23199                 return;
23200             }
23201             if (this.black.indexOf(tag) > -1) {
23202                 return;
23203             }
23204             this.black.push(tag);
23205             
23206         }, this);
23207         
23208         
23209         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23210         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23211         
23212         this.cwhite = [];
23213         this.cblack = [];
23214         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23215             if (b.indexOf(tag) > -1) {
23216                 return;
23217             }
23218             this.cwhite.push(tag);
23219             
23220         }, this);
23221         
23222         Roo.each(w, function(tag) {
23223             if (b.indexOf(tag) > -1) {
23224                 return;
23225             }
23226             if (this.cwhite.indexOf(tag) > -1) {
23227                 return;
23228             }
23229             this.cwhite.push(tag);
23230             
23231         }, this);
23232         
23233         
23234         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23235             if (w.indexOf(tag) > -1) {
23236                 return;
23237             }
23238             this.cblack.push(tag);
23239             
23240         }, this);
23241         
23242         Roo.each(b, function(tag) {
23243             if (w.indexOf(tag) > -1) {
23244                 return;
23245             }
23246             if (this.cblack.indexOf(tag) > -1) {
23247                 return;
23248             }
23249             this.cblack.push(tag);
23250             
23251         }, this);
23252     },
23253     
23254     setStylesheets : function(stylesheets)
23255     {
23256         if(typeof(stylesheets) == 'string'){
23257             Roo.get(this.iframe.contentDocument.head).createChild({
23258                 tag : 'link',
23259                 rel : 'stylesheet',
23260                 type : 'text/css',
23261                 href : stylesheets
23262             });
23263             
23264             return;
23265         }
23266         var _this = this;
23267      
23268         Roo.each(stylesheets, function(s) {
23269             if(!s.length){
23270                 return;
23271             }
23272             
23273             Roo.get(_this.iframe.contentDocument.head).createChild({
23274                 tag : 'link',
23275                 rel : 'stylesheet',
23276                 type : 'text/css',
23277                 href : s
23278             });
23279         });
23280
23281         
23282     },
23283     
23284     removeStylesheets : function()
23285     {
23286         var _this = this;
23287         
23288         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23289             s.remove();
23290         });
23291     },
23292     
23293     setStyle : function(style)
23294     {
23295         Roo.get(this.iframe.contentDocument.head).createChild({
23296             tag : 'style',
23297             type : 'text/css',
23298             html : style
23299         });
23300
23301         return;
23302     }
23303     
23304     // hide stuff that is not compatible
23305     /**
23306      * @event blur
23307      * @hide
23308      */
23309     /**
23310      * @event change
23311      * @hide
23312      */
23313     /**
23314      * @event focus
23315      * @hide
23316      */
23317     /**
23318      * @event specialkey
23319      * @hide
23320      */
23321     /**
23322      * @cfg {String} fieldClass @hide
23323      */
23324     /**
23325      * @cfg {String} focusClass @hide
23326      */
23327     /**
23328      * @cfg {String} autoCreate @hide
23329      */
23330     /**
23331      * @cfg {String} inputType @hide
23332      */
23333     /**
23334      * @cfg {String} invalidClass @hide
23335      */
23336     /**
23337      * @cfg {String} invalidText @hide
23338      */
23339     /**
23340      * @cfg {String} msgFx @hide
23341      */
23342     /**
23343      * @cfg {String} validateOnBlur @hide
23344      */
23345 });
23346
23347 Roo.HtmlEditorCore.white = [
23348         'area', 'br', 'img', 'input', 'hr', 'wbr',
23349         
23350        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23351        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23352        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23353        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23354        'table',   'ul',         'xmp', 
23355        
23356        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23357       'thead',   'tr', 
23358      
23359       'dir', 'menu', 'ol', 'ul', 'dl',
23360        
23361       'embed',  'object'
23362 ];
23363
23364
23365 Roo.HtmlEditorCore.black = [
23366     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23367         'applet', // 
23368         'base',   'basefont', 'bgsound', 'blink',  'body', 
23369         'frame',  'frameset', 'head',    'html',   'ilayer', 
23370         'iframe', 'layer',  'link',     'meta',    'object',   
23371         'script', 'style' ,'title',  'xml' // clean later..
23372 ];
23373 Roo.HtmlEditorCore.clean = [
23374     'script', 'style', 'title', 'xml'
23375 ];
23376 Roo.HtmlEditorCore.remove = [
23377     'font'
23378 ];
23379 // attributes..
23380
23381 Roo.HtmlEditorCore.ablack = [
23382     'on'
23383 ];
23384     
23385 Roo.HtmlEditorCore.aclean = [ 
23386     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23387 ];
23388
23389 // protocols..
23390 Roo.HtmlEditorCore.pwhite= [
23391         'http',  'https',  'mailto'
23392 ];
23393
23394 // white listed style attributes.
23395 Roo.HtmlEditorCore.cwhite= [
23396       //  'text-align', /// default is to allow most things..
23397       
23398          
23399 //        'font-size'//??
23400 ];
23401
23402 // black listed style attributes.
23403 Roo.HtmlEditorCore.cblack= [
23404       //  'font-size' -- this can be set by the project 
23405 ];
23406
23407
23408 Roo.HtmlEditorCore.swapCodes   =[ 
23409     [    8211, "--" ], 
23410     [    8212, "--" ], 
23411     [    8216,  "'" ],  
23412     [    8217, "'" ],  
23413     [    8220, '"' ],  
23414     [    8221, '"' ],  
23415     [    8226, "*" ],  
23416     [    8230, "..." ]
23417 ]; 
23418
23419     /*
23420  * - LGPL
23421  *
23422  * HtmlEditor
23423  * 
23424  */
23425
23426 /**
23427  * @class Roo.bootstrap.HtmlEditor
23428  * @extends Roo.bootstrap.TextArea
23429  * Bootstrap HtmlEditor class
23430
23431  * @constructor
23432  * Create a new HtmlEditor
23433  * @param {Object} config The config object
23434  */
23435
23436 Roo.bootstrap.HtmlEditor = function(config){
23437     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23438     if (!this.toolbars) {
23439         this.toolbars = [];
23440     }
23441     
23442     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23443     this.addEvents({
23444             /**
23445              * @event initialize
23446              * Fires when the editor is fully initialized (including the iframe)
23447              * @param {HtmlEditor} this
23448              */
23449             initialize: true,
23450             /**
23451              * @event activate
23452              * Fires when the editor is first receives the focus. Any insertion must wait
23453              * until after this event.
23454              * @param {HtmlEditor} this
23455              */
23456             activate: true,
23457              /**
23458              * @event beforesync
23459              * Fires before the textarea is updated with content from the editor iframe. Return false
23460              * to cancel the sync.
23461              * @param {HtmlEditor} this
23462              * @param {String} html
23463              */
23464             beforesync: true,
23465              /**
23466              * @event beforepush
23467              * Fires before the iframe editor is updated with content from the textarea. Return false
23468              * to cancel the push.
23469              * @param {HtmlEditor} this
23470              * @param {String} html
23471              */
23472             beforepush: true,
23473              /**
23474              * @event sync
23475              * Fires when the textarea is updated with content from the editor iframe.
23476              * @param {HtmlEditor} this
23477              * @param {String} html
23478              */
23479             sync: true,
23480              /**
23481              * @event push
23482              * Fires when the iframe editor is updated with content from the textarea.
23483              * @param {HtmlEditor} this
23484              * @param {String} html
23485              */
23486             push: true,
23487              /**
23488              * @event editmodechange
23489              * Fires when the editor switches edit modes
23490              * @param {HtmlEditor} this
23491              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23492              */
23493             editmodechange: true,
23494             /**
23495              * @event editorevent
23496              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23497              * @param {HtmlEditor} this
23498              */
23499             editorevent: true,
23500             /**
23501              * @event firstfocus
23502              * Fires when on first focus - needed by toolbars..
23503              * @param {HtmlEditor} this
23504              */
23505             firstfocus: true,
23506             /**
23507              * @event autosave
23508              * Auto save the htmlEditor value as a file into Events
23509              * @param {HtmlEditor} this
23510              */
23511             autosave: true,
23512             /**
23513              * @event savedpreview
23514              * preview the saved version of htmlEditor
23515              * @param {HtmlEditor} this
23516              */
23517             savedpreview: true
23518         });
23519 };
23520
23521
23522 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23523     
23524     
23525       /**
23526      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23527      */
23528     toolbars : false,
23529     
23530      /**
23531     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23532     */
23533     btns : [],
23534    
23535      /**
23536      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23537      *                        Roo.resizable.
23538      */
23539     resizable : false,
23540      /**
23541      * @cfg {Number} height (in pixels)
23542      */   
23543     height: 300,
23544    /**
23545      * @cfg {Number} width (in pixels)
23546      */   
23547     width: false,
23548     
23549     /**
23550      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23551      * 
23552      */
23553     stylesheets: false,
23554     
23555     // id of frame..
23556     frameId: false,
23557     
23558     // private properties
23559     validationEvent : false,
23560     deferHeight: true,
23561     initialized : false,
23562     activated : false,
23563     
23564     onFocus : Roo.emptyFn,
23565     iframePad:3,
23566     hideMode:'offsets',
23567     
23568     tbContainer : false,
23569     
23570     bodyCls : '',
23571     
23572     toolbarContainer :function() {
23573         return this.wrap.select('.x-html-editor-tb',true).first();
23574     },
23575
23576     /**
23577      * Protected method that will not generally be called directly. It
23578      * is called when the editor creates its toolbar. Override this method if you need to
23579      * add custom toolbar buttons.
23580      * @param {HtmlEditor} editor
23581      */
23582     createToolbar : function(){
23583         Roo.log('renewing');
23584         Roo.log("create toolbars");
23585         
23586         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23587         this.toolbars[0].render(this.toolbarContainer());
23588         
23589         return;
23590         
23591 //        if (!editor.toolbars || !editor.toolbars.length) {
23592 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23593 //        }
23594 //        
23595 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23596 //            editor.toolbars[i] = Roo.factory(
23597 //                    typeof(editor.toolbars[i]) == 'string' ?
23598 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23599 //                Roo.bootstrap.HtmlEditor);
23600 //            editor.toolbars[i].init(editor);
23601 //        }
23602     },
23603
23604      
23605     // private
23606     onRender : function(ct, position)
23607     {
23608        // Roo.log("Call onRender: " + this.xtype);
23609         var _t = this;
23610         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23611       
23612         this.wrap = this.inputEl().wrap({
23613             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23614         });
23615         
23616         this.editorcore.onRender(ct, position);
23617          
23618         if (this.resizable) {
23619             this.resizeEl = new Roo.Resizable(this.wrap, {
23620                 pinned : true,
23621                 wrap: true,
23622                 dynamic : true,
23623                 minHeight : this.height,
23624                 height: this.height,
23625                 handles : this.resizable,
23626                 width: this.width,
23627                 listeners : {
23628                     resize : function(r, w, h) {
23629                         _t.onResize(w,h); // -something
23630                     }
23631                 }
23632             });
23633             
23634         }
23635         this.createToolbar(this);
23636        
23637         
23638         if(!this.width && this.resizable){
23639             this.setSize(this.wrap.getSize());
23640         }
23641         if (this.resizeEl) {
23642             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23643             // should trigger onReize..
23644         }
23645         
23646     },
23647
23648     // private
23649     onResize : function(w, h)
23650     {
23651         Roo.log('resize: ' +w + ',' + h );
23652         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23653         var ew = false;
23654         var eh = false;
23655         
23656         if(this.inputEl() ){
23657             if(typeof w == 'number'){
23658                 var aw = w - this.wrap.getFrameWidth('lr');
23659                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23660                 ew = aw;
23661             }
23662             if(typeof h == 'number'){
23663                  var tbh = -11;  // fixme it needs to tool bar size!
23664                 for (var i =0; i < this.toolbars.length;i++) {
23665                     // fixme - ask toolbars for heights?
23666                     tbh += this.toolbars[i].el.getHeight();
23667                     //if (this.toolbars[i].footer) {
23668                     //    tbh += this.toolbars[i].footer.el.getHeight();
23669                     //}
23670                 }
23671               
23672                 
23673                 
23674                 
23675                 
23676                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23677                 ah -= 5; // knock a few pixes off for look..
23678                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23679                 var eh = ah;
23680             }
23681         }
23682         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23683         this.editorcore.onResize(ew,eh);
23684         
23685     },
23686
23687     /**
23688      * Toggles the editor between standard and source edit mode.
23689      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23690      */
23691     toggleSourceEdit : function(sourceEditMode)
23692     {
23693         this.editorcore.toggleSourceEdit(sourceEditMode);
23694         
23695         if(this.editorcore.sourceEditMode){
23696             Roo.log('editor - showing textarea');
23697             
23698 //            Roo.log('in');
23699 //            Roo.log(this.syncValue());
23700             this.syncValue();
23701             this.inputEl().removeClass(['hide', 'x-hidden']);
23702             this.inputEl().dom.removeAttribute('tabIndex');
23703             this.inputEl().focus();
23704         }else{
23705             Roo.log('editor - hiding textarea');
23706 //            Roo.log('out')
23707 //            Roo.log(this.pushValue()); 
23708             this.pushValue();
23709             
23710             this.inputEl().addClass(['hide', 'x-hidden']);
23711             this.inputEl().dom.setAttribute('tabIndex', -1);
23712             //this.deferFocus();
23713         }
23714          
23715         if(this.resizable){
23716             this.setSize(this.wrap.getSize());
23717         }
23718         
23719         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23720     },
23721  
23722     // private (for BoxComponent)
23723     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23724
23725     // private (for BoxComponent)
23726     getResizeEl : function(){
23727         return this.wrap;
23728     },
23729
23730     // private (for BoxComponent)
23731     getPositionEl : function(){
23732         return this.wrap;
23733     },
23734
23735     // private
23736     initEvents : function(){
23737         this.originalValue = this.getValue();
23738     },
23739
23740 //    /**
23741 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23742 //     * @method
23743 //     */
23744 //    markInvalid : Roo.emptyFn,
23745 //    /**
23746 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23747 //     * @method
23748 //     */
23749 //    clearInvalid : Roo.emptyFn,
23750
23751     setValue : function(v){
23752         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23753         this.editorcore.pushValue();
23754     },
23755
23756      
23757     // private
23758     deferFocus : function(){
23759         this.focus.defer(10, this);
23760     },
23761
23762     // doc'ed in Field
23763     focus : function(){
23764         this.editorcore.focus();
23765         
23766     },
23767       
23768
23769     // private
23770     onDestroy : function(){
23771         
23772         
23773         
23774         if(this.rendered){
23775             
23776             for (var i =0; i < this.toolbars.length;i++) {
23777                 // fixme - ask toolbars for heights?
23778                 this.toolbars[i].onDestroy();
23779             }
23780             
23781             this.wrap.dom.innerHTML = '';
23782             this.wrap.remove();
23783         }
23784     },
23785
23786     // private
23787     onFirstFocus : function(){
23788         //Roo.log("onFirstFocus");
23789         this.editorcore.onFirstFocus();
23790          for (var i =0; i < this.toolbars.length;i++) {
23791             this.toolbars[i].onFirstFocus();
23792         }
23793         
23794     },
23795     
23796     // private
23797     syncValue : function()
23798     {   
23799         this.editorcore.syncValue();
23800     },
23801     
23802     pushValue : function()
23803     {   
23804         this.editorcore.pushValue();
23805     }
23806      
23807     
23808     // hide stuff that is not compatible
23809     /**
23810      * @event blur
23811      * @hide
23812      */
23813     /**
23814      * @event change
23815      * @hide
23816      */
23817     /**
23818      * @event focus
23819      * @hide
23820      */
23821     /**
23822      * @event specialkey
23823      * @hide
23824      */
23825     /**
23826      * @cfg {String} fieldClass @hide
23827      */
23828     /**
23829      * @cfg {String} focusClass @hide
23830      */
23831     /**
23832      * @cfg {String} autoCreate @hide
23833      */
23834     /**
23835      * @cfg {String} inputType @hide
23836      */
23837     /**
23838      * @cfg {String} invalidClass @hide
23839      */
23840     /**
23841      * @cfg {String} invalidText @hide
23842      */
23843     /**
23844      * @cfg {String} msgFx @hide
23845      */
23846     /**
23847      * @cfg {String} validateOnBlur @hide
23848      */
23849 });
23850  
23851     
23852    
23853    
23854    
23855       
23856 Roo.namespace('Roo.bootstrap.htmleditor');
23857 /**
23858  * @class Roo.bootstrap.HtmlEditorToolbar1
23859  * Basic Toolbar
23860  * 
23861  * Usage:
23862  *
23863  new Roo.bootstrap.HtmlEditor({
23864     ....
23865     toolbars : [
23866         new Roo.bootstrap.HtmlEditorToolbar1({
23867             disable : { fonts: 1 , format: 1, ..., ... , ...],
23868             btns : [ .... ]
23869         })
23870     }
23871      
23872  * 
23873  * @cfg {Object} disable List of elements to disable..
23874  * @cfg {Array} btns List of additional buttons.
23875  * 
23876  * 
23877  * NEEDS Extra CSS? 
23878  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23879  */
23880  
23881 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23882 {
23883     
23884     Roo.apply(this, config);
23885     
23886     // default disabled, based on 'good practice'..
23887     this.disable = this.disable || {};
23888     Roo.applyIf(this.disable, {
23889         fontSize : true,
23890         colors : true,
23891         specialElements : true
23892     });
23893     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23894     
23895     this.editor = config.editor;
23896     this.editorcore = config.editor.editorcore;
23897     
23898     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23899     
23900     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23901     // dont call parent... till later.
23902 }
23903 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23904      
23905     bar : true,
23906     
23907     editor : false,
23908     editorcore : false,
23909     
23910     
23911     formats : [
23912         "p" ,  
23913         "h1","h2","h3","h4","h5","h6", 
23914         "pre", "code", 
23915         "abbr", "acronym", "address", "cite", "samp", "var",
23916         'div','span'
23917     ],
23918     
23919     onRender : function(ct, position)
23920     {
23921        // Roo.log("Call onRender: " + this.xtype);
23922         
23923        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23924        Roo.log(this.el);
23925        this.el.dom.style.marginBottom = '0';
23926        var _this = this;
23927        var editorcore = this.editorcore;
23928        var editor= this.editor;
23929        
23930        var children = [];
23931        var btn = function(id,cmd , toggle, handler, html){
23932        
23933             var  event = toggle ? 'toggle' : 'click';
23934        
23935             var a = {
23936                 size : 'sm',
23937                 xtype: 'Button',
23938                 xns: Roo.bootstrap,
23939                 glyphicon : id,
23940                 cmd : id || cmd,
23941                 enableToggle:toggle !== false,
23942                 html : html || '',
23943                 pressed : toggle ? false : null,
23944                 listeners : {}
23945             };
23946             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23947                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23948             };
23949             children.push(a);
23950             return a;
23951        }
23952        
23953     //    var cb_box = function...
23954         
23955         var style = {
23956                 xtype: 'Button',
23957                 size : 'sm',
23958                 xns: Roo.bootstrap,
23959                 glyphicon : 'font',
23960                 //html : 'submit'
23961                 menu : {
23962                     xtype: 'Menu',
23963                     xns: Roo.bootstrap,
23964                     items:  []
23965                 }
23966         };
23967         Roo.each(this.formats, function(f) {
23968             style.menu.items.push({
23969                 xtype :'MenuItem',
23970                 xns: Roo.bootstrap,
23971                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23972                 tagname : f,
23973                 listeners : {
23974                     click : function()
23975                     {
23976                         editorcore.insertTag(this.tagname);
23977                         editor.focus();
23978                     }
23979                 }
23980                 
23981             });
23982         });
23983         children.push(style);   
23984         
23985         btn('bold',false,true);
23986         btn('italic',false,true);
23987         btn('align-left', 'justifyleft',true);
23988         btn('align-center', 'justifycenter',true);
23989         btn('align-right' , 'justifyright',true);
23990         btn('link', false, false, function(btn) {
23991             //Roo.log("create link?");
23992             var url = prompt(this.createLinkText, this.defaultLinkValue);
23993             if(url && url != 'http:/'+'/'){
23994                 this.editorcore.relayCmd('createlink', url);
23995             }
23996         }),
23997         btn('list','insertunorderedlist',true);
23998         btn('pencil', false,true, function(btn){
23999                 Roo.log(this);
24000                 this.toggleSourceEdit(btn.pressed);
24001         });
24002         
24003         if (this.editor.btns.length > 0) {
24004             for (var i = 0; i<this.editor.btns.length; i++) {
24005                 children.push(this.editor.btns[i]);
24006             }
24007         }
24008         
24009         /*
24010         var cog = {
24011                 xtype: 'Button',
24012                 size : 'sm',
24013                 xns: Roo.bootstrap,
24014                 glyphicon : 'cog',
24015                 //html : 'submit'
24016                 menu : {
24017                     xtype: 'Menu',
24018                     xns: Roo.bootstrap,
24019                     items:  []
24020                 }
24021         };
24022         
24023         cog.menu.items.push({
24024             xtype :'MenuItem',
24025             xns: Roo.bootstrap,
24026             html : Clean styles,
24027             tagname : f,
24028             listeners : {
24029                 click : function()
24030                 {
24031                     editorcore.insertTag(this.tagname);
24032                     editor.focus();
24033                 }
24034             }
24035             
24036         });
24037        */
24038         
24039          
24040        this.xtype = 'NavSimplebar';
24041         
24042         for(var i=0;i< children.length;i++) {
24043             
24044             this.buttons.add(this.addxtypeChild(children[i]));
24045             
24046         }
24047         
24048         editor.on('editorevent', this.updateToolbar, this);
24049     },
24050     onBtnClick : function(id)
24051     {
24052        this.editorcore.relayCmd(id);
24053        this.editorcore.focus();
24054     },
24055     
24056     /**
24057      * Protected method that will not generally be called directly. It triggers
24058      * a toolbar update by reading the markup state of the current selection in the editor.
24059      */
24060     updateToolbar: function(){
24061
24062         if(!this.editorcore.activated){
24063             this.editor.onFirstFocus(); // is this neeed?
24064             return;
24065         }
24066
24067         var btns = this.buttons; 
24068         var doc = this.editorcore.doc;
24069         btns.get('bold').setActive(doc.queryCommandState('bold'));
24070         btns.get('italic').setActive(doc.queryCommandState('italic'));
24071         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24072         
24073         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24074         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24075         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24076         
24077         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24078         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24079          /*
24080         
24081         var ans = this.editorcore.getAllAncestors();
24082         if (this.formatCombo) {
24083             
24084             
24085             var store = this.formatCombo.store;
24086             this.formatCombo.setValue("");
24087             for (var i =0; i < ans.length;i++) {
24088                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24089                     // select it..
24090                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24091                     break;
24092                 }
24093             }
24094         }
24095         
24096         
24097         
24098         // hides menus... - so this cant be on a menu...
24099         Roo.bootstrap.MenuMgr.hideAll();
24100         */
24101         Roo.bootstrap.MenuMgr.hideAll();
24102         //this.editorsyncValue();
24103     },
24104     onFirstFocus: function() {
24105         this.buttons.each(function(item){
24106            item.enable();
24107         });
24108     },
24109     toggleSourceEdit : function(sourceEditMode){
24110         
24111           
24112         if(sourceEditMode){
24113             Roo.log("disabling buttons");
24114            this.buttons.each( function(item){
24115                 if(item.cmd != 'pencil'){
24116                     item.disable();
24117                 }
24118             });
24119           
24120         }else{
24121             Roo.log("enabling buttons");
24122             if(this.editorcore.initialized){
24123                 this.buttons.each( function(item){
24124                     item.enable();
24125                 });
24126             }
24127             
24128         }
24129         Roo.log("calling toggole on editor");
24130         // tell the editor that it's been pressed..
24131         this.editor.toggleSourceEdit(sourceEditMode);
24132        
24133     }
24134 });
24135
24136
24137
24138
24139
24140 /**
24141  * @class Roo.bootstrap.Table.AbstractSelectionModel
24142  * @extends Roo.util.Observable
24143  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24144  * implemented by descendant classes.  This class should not be directly instantiated.
24145  * @constructor
24146  */
24147 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24148     this.locked = false;
24149     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24150 };
24151
24152
24153 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24154     /** @ignore Called by the grid automatically. Do not call directly. */
24155     init : function(grid){
24156         this.grid = grid;
24157         this.initEvents();
24158     },
24159
24160     /**
24161      * Locks the selections.
24162      */
24163     lock : function(){
24164         this.locked = true;
24165     },
24166
24167     /**
24168      * Unlocks the selections.
24169      */
24170     unlock : function(){
24171         this.locked = false;
24172     },
24173
24174     /**
24175      * Returns true if the selections are locked.
24176      * @return {Boolean}
24177      */
24178     isLocked : function(){
24179         return this.locked;
24180     }
24181 });
24182 /**
24183  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24184  * @class Roo.bootstrap.Table.RowSelectionModel
24185  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24186  * It supports multiple selections and keyboard selection/navigation. 
24187  * @constructor
24188  * @param {Object} config
24189  */
24190
24191 Roo.bootstrap.Table.RowSelectionModel = function(config){
24192     Roo.apply(this, config);
24193     this.selections = new Roo.util.MixedCollection(false, function(o){
24194         return o.id;
24195     });
24196
24197     this.last = false;
24198     this.lastActive = false;
24199
24200     this.addEvents({
24201         /**
24202              * @event selectionchange
24203              * Fires when the selection changes
24204              * @param {SelectionModel} this
24205              */
24206             "selectionchange" : true,
24207         /**
24208              * @event afterselectionchange
24209              * Fires after the selection changes (eg. by key press or clicking)
24210              * @param {SelectionModel} this
24211              */
24212             "afterselectionchange" : true,
24213         /**
24214              * @event beforerowselect
24215              * Fires when a row is selected being selected, return false to cancel.
24216              * @param {SelectionModel} this
24217              * @param {Number} rowIndex The selected index
24218              * @param {Boolean} keepExisting False if other selections will be cleared
24219              */
24220             "beforerowselect" : true,
24221         /**
24222              * @event rowselect
24223              * Fires when a row is selected.
24224              * @param {SelectionModel} this
24225              * @param {Number} rowIndex The selected index
24226              * @param {Roo.data.Record} r The record
24227              */
24228             "rowselect" : true,
24229         /**
24230              * @event rowdeselect
24231              * Fires when a row is deselected.
24232              * @param {SelectionModel} this
24233              * @param {Number} rowIndex The selected index
24234              */
24235         "rowdeselect" : true
24236     });
24237     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24238     this.locked = false;
24239  };
24240
24241 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24242     /**
24243      * @cfg {Boolean} singleSelect
24244      * True to allow selection of only one row at a time (defaults to false)
24245      */
24246     singleSelect : false,
24247
24248     // private
24249     initEvents : function()
24250     {
24251
24252         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24253         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24254         //}else{ // allow click to work like normal
24255          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24256         //}
24257         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24258         this.grid.on("rowclick", this.handleMouseDown, this);
24259         
24260         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24261             "up" : function(e){
24262                 if(!e.shiftKey){
24263                     this.selectPrevious(e.shiftKey);
24264                 }else if(this.last !== false && this.lastActive !== false){
24265                     var last = this.last;
24266                     this.selectRange(this.last,  this.lastActive-1);
24267                     this.grid.getView().focusRow(this.lastActive);
24268                     if(last !== false){
24269                         this.last = last;
24270                     }
24271                 }else{
24272                     this.selectFirstRow();
24273                 }
24274                 this.fireEvent("afterselectionchange", this);
24275             },
24276             "down" : function(e){
24277                 if(!e.shiftKey){
24278                     this.selectNext(e.shiftKey);
24279                 }else if(this.last !== false && this.lastActive !== false){
24280                     var last = this.last;
24281                     this.selectRange(this.last,  this.lastActive+1);
24282                     this.grid.getView().focusRow(this.lastActive);
24283                     if(last !== false){
24284                         this.last = last;
24285                     }
24286                 }else{
24287                     this.selectFirstRow();
24288                 }
24289                 this.fireEvent("afterselectionchange", this);
24290             },
24291             scope: this
24292         });
24293         this.grid.store.on('load', function(){
24294             this.selections.clear();
24295         },this);
24296         /*
24297         var view = this.grid.view;
24298         view.on("refresh", this.onRefresh, this);
24299         view.on("rowupdated", this.onRowUpdated, this);
24300         view.on("rowremoved", this.onRemove, this);
24301         */
24302     },
24303
24304     // private
24305     onRefresh : function()
24306     {
24307         var ds = this.grid.store, i, v = this.grid.view;
24308         var s = this.selections;
24309         s.each(function(r){
24310             if((i = ds.indexOfId(r.id)) != -1){
24311                 v.onRowSelect(i);
24312             }else{
24313                 s.remove(r);
24314             }
24315         });
24316     },
24317
24318     // private
24319     onRemove : function(v, index, r){
24320         this.selections.remove(r);
24321     },
24322
24323     // private
24324     onRowUpdated : function(v, index, r){
24325         if(this.isSelected(r)){
24326             v.onRowSelect(index);
24327         }
24328     },
24329
24330     /**
24331      * Select records.
24332      * @param {Array} records The records to select
24333      * @param {Boolean} keepExisting (optional) True to keep existing selections
24334      */
24335     selectRecords : function(records, keepExisting)
24336     {
24337         if(!keepExisting){
24338             this.clearSelections();
24339         }
24340             var ds = this.grid.store;
24341         for(var i = 0, len = records.length; i < len; i++){
24342             this.selectRow(ds.indexOf(records[i]), true);
24343         }
24344     },
24345
24346     /**
24347      * Gets the number of selected rows.
24348      * @return {Number}
24349      */
24350     getCount : function(){
24351         return this.selections.length;
24352     },
24353
24354     /**
24355      * Selects the first row in the grid.
24356      */
24357     selectFirstRow : function(){
24358         this.selectRow(0);
24359     },
24360
24361     /**
24362      * Select the last row.
24363      * @param {Boolean} keepExisting (optional) True to keep existing selections
24364      */
24365     selectLastRow : function(keepExisting){
24366         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24367         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24368     },
24369
24370     /**
24371      * Selects the row immediately following the last selected row.
24372      * @param {Boolean} keepExisting (optional) True to keep existing selections
24373      */
24374     selectNext : function(keepExisting)
24375     {
24376             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24377             this.selectRow(this.last+1, keepExisting);
24378             this.grid.getView().focusRow(this.last);
24379         }
24380     },
24381
24382     /**
24383      * Selects the row that precedes the last selected row.
24384      * @param {Boolean} keepExisting (optional) True to keep existing selections
24385      */
24386     selectPrevious : function(keepExisting){
24387         if(this.last){
24388             this.selectRow(this.last-1, keepExisting);
24389             this.grid.getView().focusRow(this.last);
24390         }
24391     },
24392
24393     /**
24394      * Returns the selected records
24395      * @return {Array} Array of selected records
24396      */
24397     getSelections : function(){
24398         return [].concat(this.selections.items);
24399     },
24400
24401     /**
24402      * Returns the first selected record.
24403      * @return {Record}
24404      */
24405     getSelected : function(){
24406         return this.selections.itemAt(0);
24407     },
24408
24409
24410     /**
24411      * Clears all selections.
24412      */
24413     clearSelections : function(fast)
24414     {
24415         if(this.locked) {
24416             return;
24417         }
24418         if(fast !== true){
24419                 var ds = this.grid.store;
24420             var s = this.selections;
24421             s.each(function(r){
24422                 this.deselectRow(ds.indexOfId(r.id));
24423             }, this);
24424             s.clear();
24425         }else{
24426             this.selections.clear();
24427         }
24428         this.last = false;
24429     },
24430
24431
24432     /**
24433      * Selects all rows.
24434      */
24435     selectAll : function(){
24436         if(this.locked) {
24437             return;
24438         }
24439         this.selections.clear();
24440         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24441             this.selectRow(i, true);
24442         }
24443     },
24444
24445     /**
24446      * Returns True if there is a selection.
24447      * @return {Boolean}
24448      */
24449     hasSelection : function(){
24450         return this.selections.length > 0;
24451     },
24452
24453     /**
24454      * Returns True if the specified row is selected.
24455      * @param {Number/Record} record The record or index of the record to check
24456      * @return {Boolean}
24457      */
24458     isSelected : function(index){
24459             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24460         return (r && this.selections.key(r.id) ? true : false);
24461     },
24462
24463     /**
24464      * Returns True if the specified record id is selected.
24465      * @param {String} id The id of record to check
24466      * @return {Boolean}
24467      */
24468     isIdSelected : function(id){
24469         return (this.selections.key(id) ? true : false);
24470     },
24471
24472
24473     // private
24474     handleMouseDBClick : function(e, t){
24475         
24476     },
24477     // private
24478     handleMouseDown : function(e, t)
24479     {
24480             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24481         if(this.isLocked() || rowIndex < 0 ){
24482             return;
24483         };
24484         if(e.shiftKey && this.last !== false){
24485             var last = this.last;
24486             this.selectRange(last, rowIndex, e.ctrlKey);
24487             this.last = last; // reset the last
24488             t.focus();
24489     
24490         }else{
24491             var isSelected = this.isSelected(rowIndex);
24492             //Roo.log("select row:" + rowIndex);
24493             if(isSelected){
24494                 this.deselectRow(rowIndex);
24495             } else {
24496                         this.selectRow(rowIndex, true);
24497             }
24498     
24499             /*
24500                 if(e.button !== 0 && isSelected){
24501                 alert('rowIndex 2: ' + rowIndex);
24502                     view.focusRow(rowIndex);
24503                 }else if(e.ctrlKey && isSelected){
24504                     this.deselectRow(rowIndex);
24505                 }else if(!isSelected){
24506                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24507                     view.focusRow(rowIndex);
24508                 }
24509             */
24510         }
24511         this.fireEvent("afterselectionchange", this);
24512     },
24513     // private
24514     handleDragableRowClick :  function(grid, rowIndex, e) 
24515     {
24516         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24517             this.selectRow(rowIndex, false);
24518             grid.view.focusRow(rowIndex);
24519              this.fireEvent("afterselectionchange", this);
24520         }
24521     },
24522     
24523     /**
24524      * Selects multiple rows.
24525      * @param {Array} rows Array of the indexes of the row to select
24526      * @param {Boolean} keepExisting (optional) True to keep existing selections
24527      */
24528     selectRows : function(rows, keepExisting){
24529         if(!keepExisting){
24530             this.clearSelections();
24531         }
24532         for(var i = 0, len = rows.length; i < len; i++){
24533             this.selectRow(rows[i], true);
24534         }
24535     },
24536
24537     /**
24538      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24539      * @param {Number} startRow The index of the first row in the range
24540      * @param {Number} endRow The index of the last row in the range
24541      * @param {Boolean} keepExisting (optional) True to retain existing selections
24542      */
24543     selectRange : function(startRow, endRow, keepExisting){
24544         if(this.locked) {
24545             return;
24546         }
24547         if(!keepExisting){
24548             this.clearSelections();
24549         }
24550         if(startRow <= endRow){
24551             for(var i = startRow; i <= endRow; i++){
24552                 this.selectRow(i, true);
24553             }
24554         }else{
24555             for(var i = startRow; i >= endRow; i--){
24556                 this.selectRow(i, true);
24557             }
24558         }
24559     },
24560
24561     /**
24562      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24563      * @param {Number} startRow The index of the first row in the range
24564      * @param {Number} endRow The index of the last row in the range
24565      */
24566     deselectRange : function(startRow, endRow, preventViewNotify){
24567         if(this.locked) {
24568             return;
24569         }
24570         for(var i = startRow; i <= endRow; i++){
24571             this.deselectRow(i, preventViewNotify);
24572         }
24573     },
24574
24575     /**
24576      * Selects a row.
24577      * @param {Number} row The index of the row to select
24578      * @param {Boolean} keepExisting (optional) True to keep existing selections
24579      */
24580     selectRow : function(index, keepExisting, preventViewNotify)
24581     {
24582             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24583             return;
24584         }
24585         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24586             if(!keepExisting || this.singleSelect){
24587                 this.clearSelections();
24588             }
24589             
24590             var r = this.grid.store.getAt(index);
24591             //console.log('selectRow - record id :' + r.id);
24592             
24593             this.selections.add(r);
24594             this.last = this.lastActive = index;
24595             if(!preventViewNotify){
24596                 var proxy = new Roo.Element(
24597                                 this.grid.getRowDom(index)
24598                 );
24599                 proxy.addClass('bg-info info');
24600             }
24601             this.fireEvent("rowselect", this, index, r);
24602             this.fireEvent("selectionchange", this);
24603         }
24604     },
24605
24606     /**
24607      * Deselects a row.
24608      * @param {Number} row The index of the row to deselect
24609      */
24610     deselectRow : function(index, preventViewNotify)
24611     {
24612         if(this.locked) {
24613             return;
24614         }
24615         if(this.last == index){
24616             this.last = false;
24617         }
24618         if(this.lastActive == index){
24619             this.lastActive = false;
24620         }
24621         
24622         var r = this.grid.store.getAt(index);
24623         if (!r) {
24624             return;
24625         }
24626         
24627         this.selections.remove(r);
24628         //.console.log('deselectRow - record id :' + r.id);
24629         if(!preventViewNotify){
24630         
24631             var proxy = new Roo.Element(
24632                 this.grid.getRowDom(index)
24633             );
24634             proxy.removeClass('bg-info info');
24635         }
24636         this.fireEvent("rowdeselect", this, index);
24637         this.fireEvent("selectionchange", this);
24638     },
24639
24640     // private
24641     restoreLast : function(){
24642         if(this._last){
24643             this.last = this._last;
24644         }
24645     },
24646
24647     // private
24648     acceptsNav : function(row, col, cm){
24649         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24650     },
24651
24652     // private
24653     onEditorKey : function(field, e){
24654         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24655         if(k == e.TAB){
24656             e.stopEvent();
24657             ed.completeEdit();
24658             if(e.shiftKey){
24659                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24660             }else{
24661                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24662             }
24663         }else if(k == e.ENTER && !e.ctrlKey){
24664             e.stopEvent();
24665             ed.completeEdit();
24666             if(e.shiftKey){
24667                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24668             }else{
24669                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24670             }
24671         }else if(k == e.ESC){
24672             ed.cancelEdit();
24673         }
24674         if(newCell){
24675             g.startEditing(newCell[0], newCell[1]);
24676         }
24677     }
24678 });
24679 /*
24680  * Based on:
24681  * Ext JS Library 1.1.1
24682  * Copyright(c) 2006-2007, Ext JS, LLC.
24683  *
24684  * Originally Released Under LGPL - original licence link has changed is not relivant.
24685  *
24686  * Fork - LGPL
24687  * <script type="text/javascript">
24688  */
24689  
24690 /**
24691  * @class Roo.bootstrap.PagingToolbar
24692  * @extends Roo.bootstrap.NavSimplebar
24693  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24694  * @constructor
24695  * Create a new PagingToolbar
24696  * @param {Object} config The config object
24697  * @param {Roo.data.Store} store
24698  */
24699 Roo.bootstrap.PagingToolbar = function(config)
24700 {
24701     // old args format still supported... - xtype is prefered..
24702         // created from xtype...
24703     
24704     this.ds = config.dataSource;
24705     
24706     if (config.store && !this.ds) {
24707         this.store= Roo.factory(config.store, Roo.data);
24708         this.ds = this.store;
24709         this.ds.xmodule = this.xmodule || false;
24710     }
24711     
24712     this.toolbarItems = [];
24713     if (config.items) {
24714         this.toolbarItems = config.items;
24715     }
24716     
24717     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24718     
24719     this.cursor = 0;
24720     
24721     if (this.ds) { 
24722         this.bind(this.ds);
24723     }
24724     
24725     if (Roo.bootstrap.version == 4) {
24726         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24727     } else {
24728         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24729     }
24730     
24731 };
24732
24733 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24734     /**
24735      * @cfg {Roo.data.Store} dataSource
24736      * The underlying data store providing the paged data
24737      */
24738     /**
24739      * @cfg {String/HTMLElement/Element} container
24740      * container The id or element that will contain the toolbar
24741      */
24742     /**
24743      * @cfg {Boolean} displayInfo
24744      * True to display the displayMsg (defaults to false)
24745      */
24746     /**
24747      * @cfg {Number} pageSize
24748      * The number of records to display per page (defaults to 20)
24749      */
24750     pageSize: 20,
24751     /**
24752      * @cfg {String} displayMsg
24753      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24754      */
24755     displayMsg : 'Displaying {0} - {1} of {2}',
24756     /**
24757      * @cfg {String} emptyMsg
24758      * The message to display when no records are found (defaults to "No data to display")
24759      */
24760     emptyMsg : 'No data to display',
24761     /**
24762      * Customizable piece of the default paging text (defaults to "Page")
24763      * @type String
24764      */
24765     beforePageText : "Page",
24766     /**
24767      * Customizable piece of the default paging text (defaults to "of %0")
24768      * @type String
24769      */
24770     afterPageText : "of {0}",
24771     /**
24772      * Customizable piece of the default paging text (defaults to "First Page")
24773      * @type String
24774      */
24775     firstText : "First Page",
24776     /**
24777      * Customizable piece of the default paging text (defaults to "Previous Page")
24778      * @type String
24779      */
24780     prevText : "Previous Page",
24781     /**
24782      * Customizable piece of the default paging text (defaults to "Next Page")
24783      * @type String
24784      */
24785     nextText : "Next Page",
24786     /**
24787      * Customizable piece of the default paging text (defaults to "Last Page")
24788      * @type String
24789      */
24790     lastText : "Last Page",
24791     /**
24792      * Customizable piece of the default paging text (defaults to "Refresh")
24793      * @type String
24794      */
24795     refreshText : "Refresh",
24796
24797     buttons : false,
24798     // private
24799     onRender : function(ct, position) 
24800     {
24801         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24802         this.navgroup.parentId = this.id;
24803         this.navgroup.onRender(this.el, null);
24804         // add the buttons to the navgroup
24805         
24806         if(this.displayInfo){
24807             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24808             this.displayEl = this.el.select('.x-paging-info', true).first();
24809 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24810 //            this.displayEl = navel.el.select('span',true).first();
24811         }
24812         
24813         var _this = this;
24814         
24815         if(this.buttons){
24816             Roo.each(_this.buttons, function(e){ // this might need to use render????
24817                Roo.factory(e).render(_this.el);
24818             });
24819         }
24820             
24821         Roo.each(_this.toolbarItems, function(e) {
24822             _this.navgroup.addItem(e);
24823         });
24824         
24825         
24826         this.first = this.navgroup.addItem({
24827             tooltip: this.firstText,
24828             cls: "prev btn-outline-secondary",
24829             html : ' <i class="fa fa-step-backward"></i>',
24830             disabled: true,
24831             preventDefault: true,
24832             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24833         });
24834         
24835         this.prev =  this.navgroup.addItem({
24836             tooltip: this.prevText,
24837             cls: "prev btn-outline-secondary",
24838             html : ' <i class="fa fa-backward"></i>',
24839             disabled: true,
24840             preventDefault: true,
24841             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24842         });
24843     //this.addSeparator();
24844         
24845         
24846         var field = this.navgroup.addItem( {
24847             tagtype : 'span',
24848             cls : 'x-paging-position  btn-outline-secondary',
24849              disabled: true,
24850             html : this.beforePageText  +
24851                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24852                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24853          } ); //?? escaped?
24854         
24855         this.field = field.el.select('input', true).first();
24856         this.field.on("keydown", this.onPagingKeydown, this);
24857         this.field.on("focus", function(){this.dom.select();});
24858     
24859     
24860         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24861         //this.field.setHeight(18);
24862         //this.addSeparator();
24863         this.next = this.navgroup.addItem({
24864             tooltip: this.nextText,
24865             cls: "next btn-outline-secondary",
24866             html : ' <i class="fa fa-forward"></i>',
24867             disabled: true,
24868             preventDefault: true,
24869             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24870         });
24871         this.last = this.navgroup.addItem({
24872             tooltip: this.lastText,
24873             html : ' <i class="fa fa-step-forward"></i>',
24874             cls: "next btn-outline-secondary",
24875             disabled: true,
24876             preventDefault: true,
24877             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24878         });
24879     //this.addSeparator();
24880         this.loading = this.navgroup.addItem({
24881             tooltip: this.refreshText,
24882             cls: "btn-outline-secondary",
24883             html : ' <i class="fa fa-refresh"></i>',
24884             preventDefault: true,
24885             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24886         });
24887         
24888     },
24889
24890     // private
24891     updateInfo : function(){
24892         if(this.displayEl){
24893             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24894             var msg = count == 0 ?
24895                 this.emptyMsg :
24896                 String.format(
24897                     this.displayMsg,
24898                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24899                 );
24900             this.displayEl.update(msg);
24901         }
24902     },
24903
24904     // private
24905     onLoad : function(ds, r, o)
24906     {
24907         this.cursor = o.params.start ? o.params.start : 0;
24908         
24909         var d = this.getPageData(),
24910             ap = d.activePage,
24911             ps = d.pages;
24912         
24913         
24914         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24915         this.field.dom.value = ap;
24916         this.first.setDisabled(ap == 1);
24917         this.prev.setDisabled(ap == 1);
24918         this.next.setDisabled(ap == ps);
24919         this.last.setDisabled(ap == ps);
24920         this.loading.enable();
24921         this.updateInfo();
24922     },
24923
24924     // private
24925     getPageData : function(){
24926         var total = this.ds.getTotalCount();
24927         return {
24928             total : total,
24929             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24930             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24931         };
24932     },
24933
24934     // private
24935     onLoadError : function(){
24936         this.loading.enable();
24937     },
24938
24939     // private
24940     onPagingKeydown : function(e){
24941         var k = e.getKey();
24942         var d = this.getPageData();
24943         if(k == e.RETURN){
24944             var v = this.field.dom.value, pageNum;
24945             if(!v || isNaN(pageNum = parseInt(v, 10))){
24946                 this.field.dom.value = d.activePage;
24947                 return;
24948             }
24949             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24950             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24951             e.stopEvent();
24952         }
24953         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))
24954         {
24955           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24956           this.field.dom.value = pageNum;
24957           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24958           e.stopEvent();
24959         }
24960         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24961         {
24962           var v = this.field.dom.value, pageNum; 
24963           var increment = (e.shiftKey) ? 10 : 1;
24964           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24965                 increment *= -1;
24966           }
24967           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24968             this.field.dom.value = d.activePage;
24969             return;
24970           }
24971           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24972           {
24973             this.field.dom.value = parseInt(v, 10) + increment;
24974             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24975             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24976           }
24977           e.stopEvent();
24978         }
24979     },
24980
24981     // private
24982     beforeLoad : function(){
24983         if(this.loading){
24984             this.loading.disable();
24985         }
24986     },
24987
24988     // private
24989     onClick : function(which){
24990         
24991         var ds = this.ds;
24992         if (!ds) {
24993             return;
24994         }
24995         
24996         switch(which){
24997             case "first":
24998                 ds.load({params:{start: 0, limit: this.pageSize}});
24999             break;
25000             case "prev":
25001                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25002             break;
25003             case "next":
25004                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25005             break;
25006             case "last":
25007                 var total = ds.getTotalCount();
25008                 var extra = total % this.pageSize;
25009                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25010                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25011             break;
25012             case "refresh":
25013                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25014             break;
25015         }
25016     },
25017
25018     /**
25019      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25020      * @param {Roo.data.Store} store The data store to unbind
25021      */
25022     unbind : function(ds){
25023         ds.un("beforeload", this.beforeLoad, this);
25024         ds.un("load", this.onLoad, this);
25025         ds.un("loadexception", this.onLoadError, this);
25026         ds.un("remove", this.updateInfo, this);
25027         ds.un("add", this.updateInfo, this);
25028         this.ds = undefined;
25029     },
25030
25031     /**
25032      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25033      * @param {Roo.data.Store} store The data store to bind
25034      */
25035     bind : function(ds){
25036         ds.on("beforeload", this.beforeLoad, this);
25037         ds.on("load", this.onLoad, this);
25038         ds.on("loadexception", this.onLoadError, this);
25039         ds.on("remove", this.updateInfo, this);
25040         ds.on("add", this.updateInfo, this);
25041         this.ds = ds;
25042     }
25043 });/*
25044  * - LGPL
25045  *
25046  * element
25047  * 
25048  */
25049
25050 /**
25051  * @class Roo.bootstrap.MessageBar
25052  * @extends Roo.bootstrap.Component
25053  * Bootstrap MessageBar class
25054  * @cfg {String} html contents of the MessageBar
25055  * @cfg {String} weight (info | success | warning | danger) default info
25056  * @cfg {String} beforeClass insert the bar before the given class
25057  * @cfg {Boolean} closable (true | false) default false
25058  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25059  * 
25060  * @constructor
25061  * Create a new Element
25062  * @param {Object} config The config object
25063  */
25064
25065 Roo.bootstrap.MessageBar = function(config){
25066     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25067 };
25068
25069 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25070     
25071     html: '',
25072     weight: 'info',
25073     closable: false,
25074     fixed: false,
25075     beforeClass: 'bootstrap-sticky-wrap',
25076     
25077     getAutoCreate : function(){
25078         
25079         var cfg = {
25080             tag: 'div',
25081             cls: 'alert alert-dismissable alert-' + this.weight,
25082             cn: [
25083                 {
25084                     tag: 'span',
25085                     cls: 'message',
25086                     html: this.html || ''
25087                 }
25088             ]
25089         };
25090         
25091         if(this.fixed){
25092             cfg.cls += ' alert-messages-fixed';
25093         }
25094         
25095         if(this.closable){
25096             cfg.cn.push({
25097                 tag: 'button',
25098                 cls: 'close',
25099                 html: 'x'
25100             });
25101         }
25102         
25103         return cfg;
25104     },
25105     
25106     onRender : function(ct, position)
25107     {
25108         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25109         
25110         if(!this.el){
25111             var cfg = Roo.apply({},  this.getAutoCreate());
25112             cfg.id = Roo.id();
25113             
25114             if (this.cls) {
25115                 cfg.cls += ' ' + this.cls;
25116             }
25117             if (this.style) {
25118                 cfg.style = this.style;
25119             }
25120             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25121             
25122             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25123         }
25124         
25125         this.el.select('>button.close').on('click', this.hide, this);
25126         
25127     },
25128     
25129     show : function()
25130     {
25131         if (!this.rendered) {
25132             this.render();
25133         }
25134         
25135         this.el.show();
25136         
25137         this.fireEvent('show', this);
25138         
25139     },
25140     
25141     hide : function()
25142     {
25143         if (!this.rendered) {
25144             this.render();
25145         }
25146         
25147         this.el.hide();
25148         
25149         this.fireEvent('hide', this);
25150     },
25151     
25152     update : function()
25153     {
25154 //        var e = this.el.dom.firstChild;
25155 //        
25156 //        if(this.closable){
25157 //            e = e.nextSibling;
25158 //        }
25159 //        
25160 //        e.data = this.html || '';
25161
25162         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25163     }
25164    
25165 });
25166
25167  
25168
25169      /*
25170  * - LGPL
25171  *
25172  * Graph
25173  * 
25174  */
25175
25176
25177 /**
25178  * @class Roo.bootstrap.Graph
25179  * @extends Roo.bootstrap.Component
25180  * Bootstrap Graph class
25181 > Prameters
25182  -sm {number} sm 4
25183  -md {number} md 5
25184  @cfg {String} graphtype  bar | vbar | pie
25185  @cfg {number} g_x coodinator | centre x (pie)
25186  @cfg {number} g_y coodinator | centre y (pie)
25187  @cfg {number} g_r radius (pie)
25188  @cfg {number} g_height height of the chart (respected by all elements in the set)
25189  @cfg {number} g_width width of the chart (respected by all elements in the set)
25190  @cfg {Object} title The title of the chart
25191     
25192  -{Array}  values
25193  -opts (object) options for the chart 
25194      o {
25195      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25196      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25197      o vgutter (number)
25198      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.
25199      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25200      o to
25201      o stretch (boolean)
25202      o }
25203  -opts (object) options for the pie
25204      o{
25205      o cut
25206      o startAngle (number)
25207      o endAngle (number)
25208      } 
25209  *
25210  * @constructor
25211  * Create a new Input
25212  * @param {Object} config The config object
25213  */
25214
25215 Roo.bootstrap.Graph = function(config){
25216     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25217     
25218     this.addEvents({
25219         // img events
25220         /**
25221          * @event click
25222          * The img click event for the img.
25223          * @param {Roo.EventObject} e
25224          */
25225         "click" : true
25226     });
25227 };
25228
25229 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25230     
25231     sm: 4,
25232     md: 5,
25233     graphtype: 'bar',
25234     g_height: 250,
25235     g_width: 400,
25236     g_x: 50,
25237     g_y: 50,
25238     g_r: 30,
25239     opts:{
25240         //g_colors: this.colors,
25241         g_type: 'soft',
25242         g_gutter: '20%'
25243
25244     },
25245     title : false,
25246
25247     getAutoCreate : function(){
25248         
25249         var cfg = {
25250             tag: 'div',
25251             html : null
25252         };
25253         
25254         
25255         return  cfg;
25256     },
25257
25258     onRender : function(ct,position){
25259         
25260         
25261         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25262         
25263         if (typeof(Raphael) == 'undefined') {
25264             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25265             return;
25266         }
25267         
25268         this.raphael = Raphael(this.el.dom);
25269         
25270                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25271                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25272                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25273                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25274                 /*
25275                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25276                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25277                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25278                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25279                 
25280                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25281                 r.barchart(330, 10, 300, 220, data1);
25282                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25283                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25284                 */
25285                 
25286                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25287                 // r.barchart(30, 30, 560, 250,  xdata, {
25288                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25289                 //     axis : "0 0 1 1",
25290                 //     axisxlabels :  xdata
25291                 //     //yvalues : cols,
25292                    
25293                 // });
25294 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25295 //        
25296 //        this.load(null,xdata,{
25297 //                axis : "0 0 1 1",
25298 //                axisxlabels :  xdata
25299 //                });
25300
25301     },
25302
25303     load : function(graphtype,xdata,opts)
25304     {
25305         this.raphael.clear();
25306         if(!graphtype) {
25307             graphtype = this.graphtype;
25308         }
25309         if(!opts){
25310             opts = this.opts;
25311         }
25312         var r = this.raphael,
25313             fin = function () {
25314                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25315             },
25316             fout = function () {
25317                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25318             },
25319             pfin = function() {
25320                 this.sector.stop();
25321                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25322
25323                 if (this.label) {
25324                     this.label[0].stop();
25325                     this.label[0].attr({ r: 7.5 });
25326                     this.label[1].attr({ "font-weight": 800 });
25327                 }
25328             },
25329             pfout = function() {
25330                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25331
25332                 if (this.label) {
25333                     this.label[0].animate({ r: 5 }, 500, "bounce");
25334                     this.label[1].attr({ "font-weight": 400 });
25335                 }
25336             };
25337
25338         switch(graphtype){
25339             case 'bar':
25340                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25341                 break;
25342             case 'hbar':
25343                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25344                 break;
25345             case 'pie':
25346 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25347 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25348 //            
25349                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25350                 
25351                 break;
25352
25353         }
25354         
25355         if(this.title){
25356             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25357         }
25358         
25359     },
25360     
25361     setTitle: function(o)
25362     {
25363         this.title = o;
25364     },
25365     
25366     initEvents: function() {
25367         
25368         if(!this.href){
25369             this.el.on('click', this.onClick, this);
25370         }
25371     },
25372     
25373     onClick : function(e)
25374     {
25375         Roo.log('img onclick');
25376         this.fireEvent('click', this, e);
25377     }
25378    
25379 });
25380
25381  
25382 /*
25383  * - LGPL
25384  *
25385  * numberBox
25386  * 
25387  */
25388 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25389
25390 /**
25391  * @class Roo.bootstrap.dash.NumberBox
25392  * @extends Roo.bootstrap.Component
25393  * Bootstrap NumberBox class
25394  * @cfg {String} headline Box headline
25395  * @cfg {String} content Box content
25396  * @cfg {String} icon Box icon
25397  * @cfg {String} footer Footer text
25398  * @cfg {String} fhref Footer href
25399  * 
25400  * @constructor
25401  * Create a new NumberBox
25402  * @param {Object} config The config object
25403  */
25404
25405
25406 Roo.bootstrap.dash.NumberBox = function(config){
25407     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25408     
25409 };
25410
25411 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25412     
25413     headline : '',
25414     content : '',
25415     icon : '',
25416     footer : '',
25417     fhref : '',
25418     ficon : '',
25419     
25420     getAutoCreate : function(){
25421         
25422         var cfg = {
25423             tag : 'div',
25424             cls : 'small-box ',
25425             cn : [
25426                 {
25427                     tag : 'div',
25428                     cls : 'inner',
25429                     cn :[
25430                         {
25431                             tag : 'h3',
25432                             cls : 'roo-headline',
25433                             html : this.headline
25434                         },
25435                         {
25436                             tag : 'p',
25437                             cls : 'roo-content',
25438                             html : this.content
25439                         }
25440                     ]
25441                 }
25442             ]
25443         };
25444         
25445         if(this.icon){
25446             cfg.cn.push({
25447                 tag : 'div',
25448                 cls : 'icon',
25449                 cn :[
25450                     {
25451                         tag : 'i',
25452                         cls : 'ion ' + this.icon
25453                     }
25454                 ]
25455             });
25456         }
25457         
25458         if(this.footer){
25459             var footer = {
25460                 tag : 'a',
25461                 cls : 'small-box-footer',
25462                 href : this.fhref || '#',
25463                 html : this.footer
25464             };
25465             
25466             cfg.cn.push(footer);
25467             
25468         }
25469         
25470         return  cfg;
25471     },
25472
25473     onRender : function(ct,position){
25474         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25475
25476
25477        
25478                 
25479     },
25480
25481     setHeadline: function (value)
25482     {
25483         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25484     },
25485     
25486     setFooter: function (value, href)
25487     {
25488         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25489         
25490         if(href){
25491             this.el.select('a.small-box-footer',true).first().attr('href', href);
25492         }
25493         
25494     },
25495
25496     setContent: function (value)
25497     {
25498         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25499     },
25500
25501     initEvents: function() 
25502     {   
25503         
25504     }
25505     
25506 });
25507
25508  
25509 /*
25510  * - LGPL
25511  *
25512  * TabBox
25513  * 
25514  */
25515 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25516
25517 /**
25518  * @class Roo.bootstrap.dash.TabBox
25519  * @extends Roo.bootstrap.Component
25520  * Bootstrap TabBox class
25521  * @cfg {String} title Title of the TabBox
25522  * @cfg {String} icon Icon of the TabBox
25523  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25524  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25525  * 
25526  * @constructor
25527  * Create a new TabBox
25528  * @param {Object} config The config object
25529  */
25530
25531
25532 Roo.bootstrap.dash.TabBox = function(config){
25533     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25534     this.addEvents({
25535         // raw events
25536         /**
25537          * @event addpane
25538          * When a pane is added
25539          * @param {Roo.bootstrap.dash.TabPane} pane
25540          */
25541         "addpane" : true,
25542         /**
25543          * @event activatepane
25544          * When a pane is activated
25545          * @param {Roo.bootstrap.dash.TabPane} pane
25546          */
25547         "activatepane" : true
25548         
25549          
25550     });
25551     
25552     this.panes = [];
25553 };
25554
25555 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25556
25557     title : '',
25558     icon : false,
25559     showtabs : true,
25560     tabScrollable : false,
25561     
25562     getChildContainer : function()
25563     {
25564         return this.el.select('.tab-content', true).first();
25565     },
25566     
25567     getAutoCreate : function(){
25568         
25569         var header = {
25570             tag: 'li',
25571             cls: 'pull-left header',
25572             html: this.title,
25573             cn : []
25574         };
25575         
25576         if(this.icon){
25577             header.cn.push({
25578                 tag: 'i',
25579                 cls: 'fa ' + this.icon
25580             });
25581         }
25582         
25583         var h = {
25584             tag: 'ul',
25585             cls: 'nav nav-tabs pull-right',
25586             cn: [
25587                 header
25588             ]
25589         };
25590         
25591         if(this.tabScrollable){
25592             h = {
25593                 tag: 'div',
25594                 cls: 'tab-header',
25595                 cn: [
25596                     {
25597                         tag: 'ul',
25598                         cls: 'nav nav-tabs pull-right',
25599                         cn: [
25600                             header
25601                         ]
25602                     }
25603                 ]
25604             };
25605         }
25606         
25607         var cfg = {
25608             tag: 'div',
25609             cls: 'nav-tabs-custom',
25610             cn: [
25611                 h,
25612                 {
25613                     tag: 'div',
25614                     cls: 'tab-content no-padding',
25615                     cn: []
25616                 }
25617             ]
25618         };
25619
25620         return  cfg;
25621     },
25622     initEvents : function()
25623     {
25624         //Roo.log('add add pane handler');
25625         this.on('addpane', this.onAddPane, this);
25626     },
25627      /**
25628      * Updates the box title
25629      * @param {String} html to set the title to.
25630      */
25631     setTitle : function(value)
25632     {
25633         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25634     },
25635     onAddPane : function(pane)
25636     {
25637         this.panes.push(pane);
25638         //Roo.log('addpane');
25639         //Roo.log(pane);
25640         // tabs are rendere left to right..
25641         if(!this.showtabs){
25642             return;
25643         }
25644         
25645         var ctr = this.el.select('.nav-tabs', true).first();
25646          
25647          
25648         var existing = ctr.select('.nav-tab',true);
25649         var qty = existing.getCount();;
25650         
25651         
25652         var tab = ctr.createChild({
25653             tag : 'li',
25654             cls : 'nav-tab' + (qty ? '' : ' active'),
25655             cn : [
25656                 {
25657                     tag : 'a',
25658                     href:'#',
25659                     html : pane.title
25660                 }
25661             ]
25662         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25663         pane.tab = tab;
25664         
25665         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25666         if (!qty) {
25667             pane.el.addClass('active');
25668         }
25669         
25670                 
25671     },
25672     onTabClick : function(ev,un,ob,pane)
25673     {
25674         //Roo.log('tab - prev default');
25675         ev.preventDefault();
25676         
25677         
25678         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25679         pane.tab.addClass('active');
25680         //Roo.log(pane.title);
25681         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25682         // technically we should have a deactivate event.. but maybe add later.
25683         // and it should not de-activate the selected tab...
25684         this.fireEvent('activatepane', pane);
25685         pane.el.addClass('active');
25686         pane.fireEvent('activate');
25687         
25688         
25689     },
25690     
25691     getActivePane : function()
25692     {
25693         var r = false;
25694         Roo.each(this.panes, function(p) {
25695             if(p.el.hasClass('active')){
25696                 r = p;
25697                 return false;
25698             }
25699             
25700             return;
25701         });
25702         
25703         return r;
25704     }
25705     
25706     
25707 });
25708
25709  
25710 /*
25711  * - LGPL
25712  *
25713  * Tab pane
25714  * 
25715  */
25716 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25717 /**
25718  * @class Roo.bootstrap.TabPane
25719  * @extends Roo.bootstrap.Component
25720  * Bootstrap TabPane class
25721  * @cfg {Boolean} active (false | true) Default false
25722  * @cfg {String} title title of panel
25723
25724  * 
25725  * @constructor
25726  * Create a new TabPane
25727  * @param {Object} config The config object
25728  */
25729
25730 Roo.bootstrap.dash.TabPane = function(config){
25731     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25732     
25733     this.addEvents({
25734         // raw events
25735         /**
25736          * @event activate
25737          * When a pane is activated
25738          * @param {Roo.bootstrap.dash.TabPane} pane
25739          */
25740         "activate" : true
25741          
25742     });
25743 };
25744
25745 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25746     
25747     active : false,
25748     title : '',
25749     
25750     // the tabBox that this is attached to.
25751     tab : false,
25752      
25753     getAutoCreate : function() 
25754     {
25755         var cfg = {
25756             tag: 'div',
25757             cls: 'tab-pane'
25758         };
25759         
25760         if(this.active){
25761             cfg.cls += ' active';
25762         }
25763         
25764         return cfg;
25765     },
25766     initEvents  : function()
25767     {
25768         //Roo.log('trigger add pane handler');
25769         this.parent().fireEvent('addpane', this)
25770     },
25771     
25772      /**
25773      * Updates the tab title 
25774      * @param {String} html to set the title to.
25775      */
25776     setTitle: function(str)
25777     {
25778         if (!this.tab) {
25779             return;
25780         }
25781         this.title = str;
25782         this.tab.select('a', true).first().dom.innerHTML = str;
25783         
25784     }
25785     
25786     
25787     
25788 });
25789
25790  
25791
25792
25793  /*
25794  * - LGPL
25795  *
25796  * menu
25797  * 
25798  */
25799 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25800
25801 /**
25802  * @class Roo.bootstrap.menu.Menu
25803  * @extends Roo.bootstrap.Component
25804  * Bootstrap Menu class - container for Menu
25805  * @cfg {String} html Text of the menu
25806  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25807  * @cfg {String} icon Font awesome icon
25808  * @cfg {String} pos Menu align to (top | bottom) default bottom
25809  * 
25810  * 
25811  * @constructor
25812  * Create a new Menu
25813  * @param {Object} config The config object
25814  */
25815
25816
25817 Roo.bootstrap.menu.Menu = function(config){
25818     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25819     
25820     this.addEvents({
25821         /**
25822          * @event beforeshow
25823          * Fires before this menu is displayed
25824          * @param {Roo.bootstrap.menu.Menu} this
25825          */
25826         beforeshow : true,
25827         /**
25828          * @event beforehide
25829          * Fires before this menu is hidden
25830          * @param {Roo.bootstrap.menu.Menu} this
25831          */
25832         beforehide : true,
25833         /**
25834          * @event show
25835          * Fires after this menu is displayed
25836          * @param {Roo.bootstrap.menu.Menu} this
25837          */
25838         show : true,
25839         /**
25840          * @event hide
25841          * Fires after this menu is hidden
25842          * @param {Roo.bootstrap.menu.Menu} this
25843          */
25844         hide : true,
25845         /**
25846          * @event click
25847          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25848          * @param {Roo.bootstrap.menu.Menu} this
25849          * @param {Roo.EventObject} e
25850          */
25851         click : true
25852     });
25853     
25854 };
25855
25856 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25857     
25858     submenu : false,
25859     html : '',
25860     weight : 'default',
25861     icon : false,
25862     pos : 'bottom',
25863     
25864     
25865     getChildContainer : function() {
25866         if(this.isSubMenu){
25867             return this.el;
25868         }
25869         
25870         return this.el.select('ul.dropdown-menu', true).first();  
25871     },
25872     
25873     getAutoCreate : function()
25874     {
25875         var text = [
25876             {
25877                 tag : 'span',
25878                 cls : 'roo-menu-text',
25879                 html : this.html
25880             }
25881         ];
25882         
25883         if(this.icon){
25884             text.unshift({
25885                 tag : 'i',
25886                 cls : 'fa ' + this.icon
25887             })
25888         }
25889         
25890         
25891         var cfg = {
25892             tag : 'div',
25893             cls : 'btn-group',
25894             cn : [
25895                 {
25896                     tag : 'button',
25897                     cls : 'dropdown-button btn btn-' + this.weight,
25898                     cn : text
25899                 },
25900                 {
25901                     tag : 'button',
25902                     cls : 'dropdown-toggle btn btn-' + this.weight,
25903                     cn : [
25904                         {
25905                             tag : 'span',
25906                             cls : 'caret'
25907                         }
25908                     ]
25909                 },
25910                 {
25911                     tag : 'ul',
25912                     cls : 'dropdown-menu'
25913                 }
25914             ]
25915             
25916         };
25917         
25918         if(this.pos == 'top'){
25919             cfg.cls += ' dropup';
25920         }
25921         
25922         if(this.isSubMenu){
25923             cfg = {
25924                 tag : 'ul',
25925                 cls : 'dropdown-menu'
25926             }
25927         }
25928         
25929         return cfg;
25930     },
25931     
25932     onRender : function(ct, position)
25933     {
25934         this.isSubMenu = ct.hasClass('dropdown-submenu');
25935         
25936         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25937     },
25938     
25939     initEvents : function() 
25940     {
25941         if(this.isSubMenu){
25942             return;
25943         }
25944         
25945         this.hidden = true;
25946         
25947         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25948         this.triggerEl.on('click', this.onTriggerPress, this);
25949         
25950         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25951         this.buttonEl.on('click', this.onClick, this);
25952         
25953     },
25954     
25955     list : function()
25956     {
25957         if(this.isSubMenu){
25958             return this.el;
25959         }
25960         
25961         return this.el.select('ul.dropdown-menu', true).first();
25962     },
25963     
25964     onClick : function(e)
25965     {
25966         this.fireEvent("click", this, e);
25967     },
25968     
25969     onTriggerPress  : function(e)
25970     {   
25971         if (this.isVisible()) {
25972             this.hide();
25973         } else {
25974             this.show();
25975         }
25976     },
25977     
25978     isVisible : function(){
25979         return !this.hidden;
25980     },
25981     
25982     show : function()
25983     {
25984         this.fireEvent("beforeshow", this);
25985         
25986         this.hidden = false;
25987         this.el.addClass('open');
25988         
25989         Roo.get(document).on("mouseup", this.onMouseUp, this);
25990         
25991         this.fireEvent("show", this);
25992         
25993         
25994     },
25995     
25996     hide : function()
25997     {
25998         this.fireEvent("beforehide", this);
25999         
26000         this.hidden = true;
26001         this.el.removeClass('open');
26002         
26003         Roo.get(document).un("mouseup", this.onMouseUp);
26004         
26005         this.fireEvent("hide", this);
26006     },
26007     
26008     onMouseUp : function()
26009     {
26010         this.hide();
26011     }
26012     
26013 });
26014
26015  
26016  /*
26017  * - LGPL
26018  *
26019  * menu item
26020  * 
26021  */
26022 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26023
26024 /**
26025  * @class Roo.bootstrap.menu.Item
26026  * @extends Roo.bootstrap.Component
26027  * Bootstrap MenuItem class
26028  * @cfg {Boolean} submenu (true | false) default false
26029  * @cfg {String} html text of the item
26030  * @cfg {String} href the link
26031  * @cfg {Boolean} disable (true | false) default false
26032  * @cfg {Boolean} preventDefault (true | false) default true
26033  * @cfg {String} icon Font awesome icon
26034  * @cfg {String} pos Submenu align to (left | right) default right 
26035  * 
26036  * 
26037  * @constructor
26038  * Create a new Item
26039  * @param {Object} config The config object
26040  */
26041
26042
26043 Roo.bootstrap.menu.Item = function(config){
26044     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26045     this.addEvents({
26046         /**
26047          * @event mouseover
26048          * Fires when the mouse is hovering over this menu
26049          * @param {Roo.bootstrap.menu.Item} this
26050          * @param {Roo.EventObject} e
26051          */
26052         mouseover : true,
26053         /**
26054          * @event mouseout
26055          * Fires when the mouse exits this menu
26056          * @param {Roo.bootstrap.menu.Item} this
26057          * @param {Roo.EventObject} e
26058          */
26059         mouseout : true,
26060         // raw events
26061         /**
26062          * @event click
26063          * The raw click event for the entire grid.
26064          * @param {Roo.EventObject} e
26065          */
26066         click : true
26067     });
26068 };
26069
26070 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26071     
26072     submenu : false,
26073     href : '',
26074     html : '',
26075     preventDefault: true,
26076     disable : false,
26077     icon : false,
26078     pos : 'right',
26079     
26080     getAutoCreate : function()
26081     {
26082         var text = [
26083             {
26084                 tag : 'span',
26085                 cls : 'roo-menu-item-text',
26086                 html : this.html
26087             }
26088         ];
26089         
26090         if(this.icon){
26091             text.unshift({
26092                 tag : 'i',
26093                 cls : 'fa ' + this.icon
26094             })
26095         }
26096         
26097         var cfg = {
26098             tag : 'li',
26099             cn : [
26100                 {
26101                     tag : 'a',
26102                     href : this.href || '#',
26103                     cn : text
26104                 }
26105             ]
26106         };
26107         
26108         if(this.disable){
26109             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26110         }
26111         
26112         if(this.submenu){
26113             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26114             
26115             if(this.pos == 'left'){
26116                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26117             }
26118         }
26119         
26120         return cfg;
26121     },
26122     
26123     initEvents : function() 
26124     {
26125         this.el.on('mouseover', this.onMouseOver, this);
26126         this.el.on('mouseout', this.onMouseOut, this);
26127         
26128         this.el.select('a', true).first().on('click', this.onClick, this);
26129         
26130     },
26131     
26132     onClick : function(e)
26133     {
26134         if(this.preventDefault){
26135             e.preventDefault();
26136         }
26137         
26138         this.fireEvent("click", this, e);
26139     },
26140     
26141     onMouseOver : function(e)
26142     {
26143         if(this.submenu && this.pos == 'left'){
26144             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26145         }
26146         
26147         this.fireEvent("mouseover", this, e);
26148     },
26149     
26150     onMouseOut : function(e)
26151     {
26152         this.fireEvent("mouseout", this, e);
26153     }
26154 });
26155
26156  
26157
26158  /*
26159  * - LGPL
26160  *
26161  * menu separator
26162  * 
26163  */
26164 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26165
26166 /**
26167  * @class Roo.bootstrap.menu.Separator
26168  * @extends Roo.bootstrap.Component
26169  * Bootstrap Separator class
26170  * 
26171  * @constructor
26172  * Create a new Separator
26173  * @param {Object} config The config object
26174  */
26175
26176
26177 Roo.bootstrap.menu.Separator = function(config){
26178     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26179 };
26180
26181 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26182     
26183     getAutoCreate : function(){
26184         var cfg = {
26185             tag : 'li',
26186             cls: 'divider'
26187         };
26188         
26189         return cfg;
26190     }
26191    
26192 });
26193
26194  
26195
26196  /*
26197  * - LGPL
26198  *
26199  * Tooltip
26200  * 
26201  */
26202
26203 /**
26204  * @class Roo.bootstrap.Tooltip
26205  * Bootstrap Tooltip class
26206  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26207  * to determine which dom element triggers the tooltip.
26208  * 
26209  * It needs to add support for additional attributes like tooltip-position
26210  * 
26211  * @constructor
26212  * Create a new Toolti
26213  * @param {Object} config The config object
26214  */
26215
26216 Roo.bootstrap.Tooltip = function(config){
26217     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26218     
26219     this.alignment = Roo.bootstrap.Tooltip.alignment;
26220     
26221     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26222         this.alignment = config.alignment;
26223     }
26224     
26225 };
26226
26227 Roo.apply(Roo.bootstrap.Tooltip, {
26228     /**
26229      * @function init initialize tooltip monitoring.
26230      * @static
26231      */
26232     currentEl : false,
26233     currentTip : false,
26234     currentRegion : false,
26235     
26236     //  init : delay?
26237     
26238     init : function()
26239     {
26240         Roo.get(document).on('mouseover', this.enter ,this);
26241         Roo.get(document).on('mouseout', this.leave, this);
26242          
26243         
26244         this.currentTip = new Roo.bootstrap.Tooltip();
26245     },
26246     
26247     enter : function(ev)
26248     {
26249         var dom = ev.getTarget();
26250         
26251         //Roo.log(['enter',dom]);
26252         var el = Roo.fly(dom);
26253         if (this.currentEl) {
26254             //Roo.log(dom);
26255             //Roo.log(this.currentEl);
26256             //Roo.log(this.currentEl.contains(dom));
26257             if (this.currentEl == el) {
26258                 return;
26259             }
26260             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26261                 return;
26262             }
26263
26264         }
26265         
26266         if (this.currentTip.el) {
26267             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26268         }    
26269         //Roo.log(ev);
26270         
26271         if(!el || el.dom == document){
26272             return;
26273         }
26274         
26275         var bindEl = el;
26276         
26277         // you can not look for children, as if el is the body.. then everythign is the child..
26278         if (!el.attr('tooltip')) { //
26279             if (!el.select("[tooltip]").elements.length) {
26280                 return;
26281             }
26282             // is the mouse over this child...?
26283             bindEl = el.select("[tooltip]").first();
26284             var xy = ev.getXY();
26285             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26286                 //Roo.log("not in region.");
26287                 return;
26288             }
26289             //Roo.log("child element over..");
26290             
26291         }
26292         this.currentEl = bindEl;
26293         this.currentTip.bind(bindEl);
26294         this.currentRegion = Roo.lib.Region.getRegion(dom);
26295         this.currentTip.enter();
26296         
26297     },
26298     leave : function(ev)
26299     {
26300         var dom = ev.getTarget();
26301         //Roo.log(['leave',dom]);
26302         if (!this.currentEl) {
26303             return;
26304         }
26305         
26306         
26307         if (dom != this.currentEl.dom) {
26308             return;
26309         }
26310         var xy = ev.getXY();
26311         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26312             return;
26313         }
26314         // only activate leave if mouse cursor is outside... bounding box..
26315         
26316         
26317         
26318         
26319         if (this.currentTip) {
26320             this.currentTip.leave();
26321         }
26322         //Roo.log('clear currentEl');
26323         this.currentEl = false;
26324         
26325         
26326     },
26327     alignment : {
26328         'left' : ['r-l', [-2,0], 'right'],
26329         'right' : ['l-r', [2,0], 'left'],
26330         'bottom' : ['t-b', [0,2], 'top'],
26331         'top' : [ 'b-t', [0,-2], 'bottom']
26332     }
26333     
26334 });
26335
26336
26337 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26338     
26339     
26340     bindEl : false,
26341     
26342     delay : null, // can be { show : 300 , hide: 500}
26343     
26344     timeout : null,
26345     
26346     hoverState : null, //???
26347     
26348     placement : 'bottom', 
26349     
26350     alignment : false,
26351     
26352     getAutoCreate : function(){
26353     
26354         var cfg = {
26355            cls : 'tooltip',
26356            role : 'tooltip',
26357            cn : [
26358                 {
26359                     cls : 'tooltip-arrow'
26360                 },
26361                 {
26362                     cls : 'tooltip-inner'
26363                 }
26364            ]
26365         };
26366         
26367         return cfg;
26368     },
26369     bind : function(el)
26370     {
26371         this.bindEl = el;
26372     },
26373       
26374     
26375     enter : function () {
26376        
26377         if (this.timeout != null) {
26378             clearTimeout(this.timeout);
26379         }
26380         
26381         this.hoverState = 'in';
26382          //Roo.log("enter - show");
26383         if (!this.delay || !this.delay.show) {
26384             this.show();
26385             return;
26386         }
26387         var _t = this;
26388         this.timeout = setTimeout(function () {
26389             if (_t.hoverState == 'in') {
26390                 _t.show();
26391             }
26392         }, this.delay.show);
26393     },
26394     leave : function()
26395     {
26396         clearTimeout(this.timeout);
26397     
26398         this.hoverState = 'out';
26399          if (!this.delay || !this.delay.hide) {
26400             this.hide();
26401             return;
26402         }
26403        
26404         var _t = this;
26405         this.timeout = setTimeout(function () {
26406             //Roo.log("leave - timeout");
26407             
26408             if (_t.hoverState == 'out') {
26409                 _t.hide();
26410                 Roo.bootstrap.Tooltip.currentEl = false;
26411             }
26412         }, delay);
26413     },
26414     
26415     show : function (msg)
26416     {
26417         if (!this.el) {
26418             this.render(document.body);
26419         }
26420         // set content.
26421         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26422         
26423         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26424         
26425         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26426         
26427         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26428         
26429         var placement = typeof this.placement == 'function' ?
26430             this.placement.call(this, this.el, on_el) :
26431             this.placement;
26432             
26433         var autoToken = /\s?auto?\s?/i;
26434         var autoPlace = autoToken.test(placement);
26435         if (autoPlace) {
26436             placement = placement.replace(autoToken, '') || 'top';
26437         }
26438         
26439         //this.el.detach()
26440         //this.el.setXY([0,0]);
26441         this.el.show();
26442         //this.el.dom.style.display='block';
26443         
26444         //this.el.appendTo(on_el);
26445         
26446         var p = this.getPosition();
26447         var box = this.el.getBox();
26448         
26449         if (autoPlace) {
26450             // fixme..
26451         }
26452         
26453         var align = this.alignment[placement];
26454         
26455         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26456         
26457         if(placement == 'top' || placement == 'bottom'){
26458             if(xy[0] < 0){
26459                 placement = 'right';
26460             }
26461             
26462             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26463                 placement = 'left';
26464             }
26465             
26466             var scroll = Roo.select('body', true).first().getScroll();
26467             
26468             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26469                 placement = 'top';
26470             }
26471             
26472             align = this.alignment[placement];
26473         }
26474         
26475         this.el.alignTo(this.bindEl, align[0],align[1]);
26476         //var arrow = this.el.select('.arrow',true).first();
26477         //arrow.set(align[2], 
26478         
26479         this.el.addClass(placement);
26480         
26481         this.el.addClass('in fade');
26482         
26483         this.hoverState = null;
26484         
26485         if (this.el.hasClass('fade')) {
26486             // fade it?
26487         }
26488         
26489     },
26490     hide : function()
26491     {
26492          
26493         if (!this.el) {
26494             return;
26495         }
26496         //this.el.setXY([0,0]);
26497         this.el.removeClass('in');
26498         //this.el.hide();
26499         
26500     }
26501     
26502 });
26503  
26504
26505  /*
26506  * - LGPL
26507  *
26508  * Location Picker
26509  * 
26510  */
26511
26512 /**
26513  * @class Roo.bootstrap.LocationPicker
26514  * @extends Roo.bootstrap.Component
26515  * Bootstrap LocationPicker class
26516  * @cfg {Number} latitude Position when init default 0
26517  * @cfg {Number} longitude Position when init default 0
26518  * @cfg {Number} zoom default 15
26519  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26520  * @cfg {Boolean} mapTypeControl default false
26521  * @cfg {Boolean} disableDoubleClickZoom default false
26522  * @cfg {Boolean} scrollwheel default true
26523  * @cfg {Boolean} streetViewControl default false
26524  * @cfg {Number} radius default 0
26525  * @cfg {String} locationName
26526  * @cfg {Boolean} draggable default true
26527  * @cfg {Boolean} enableAutocomplete default false
26528  * @cfg {Boolean} enableReverseGeocode default true
26529  * @cfg {String} markerTitle
26530  * 
26531  * @constructor
26532  * Create a new LocationPicker
26533  * @param {Object} config The config object
26534  */
26535
26536
26537 Roo.bootstrap.LocationPicker = function(config){
26538     
26539     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26540     
26541     this.addEvents({
26542         /**
26543          * @event initial
26544          * Fires when the picker initialized.
26545          * @param {Roo.bootstrap.LocationPicker} this
26546          * @param {Google Location} location
26547          */
26548         initial : true,
26549         /**
26550          * @event positionchanged
26551          * Fires when the picker position changed.
26552          * @param {Roo.bootstrap.LocationPicker} this
26553          * @param {Google Location} location
26554          */
26555         positionchanged : true,
26556         /**
26557          * @event resize
26558          * Fires when the map resize.
26559          * @param {Roo.bootstrap.LocationPicker} this
26560          */
26561         resize : true,
26562         /**
26563          * @event show
26564          * Fires when the map show.
26565          * @param {Roo.bootstrap.LocationPicker} this
26566          */
26567         show : true,
26568         /**
26569          * @event hide
26570          * Fires when the map hide.
26571          * @param {Roo.bootstrap.LocationPicker} this
26572          */
26573         hide : true,
26574         /**
26575          * @event mapClick
26576          * Fires when click the map.
26577          * @param {Roo.bootstrap.LocationPicker} this
26578          * @param {Map event} e
26579          */
26580         mapClick : true,
26581         /**
26582          * @event mapRightClick
26583          * Fires when right click the map.
26584          * @param {Roo.bootstrap.LocationPicker} this
26585          * @param {Map event} e
26586          */
26587         mapRightClick : true,
26588         /**
26589          * @event markerClick
26590          * Fires when click the marker.
26591          * @param {Roo.bootstrap.LocationPicker} this
26592          * @param {Map event} e
26593          */
26594         markerClick : true,
26595         /**
26596          * @event markerRightClick
26597          * Fires when right click the marker.
26598          * @param {Roo.bootstrap.LocationPicker} this
26599          * @param {Map event} e
26600          */
26601         markerRightClick : true,
26602         /**
26603          * @event OverlayViewDraw
26604          * Fires when OverlayView Draw
26605          * @param {Roo.bootstrap.LocationPicker} this
26606          */
26607         OverlayViewDraw : true,
26608         /**
26609          * @event OverlayViewOnAdd
26610          * Fires when OverlayView Draw
26611          * @param {Roo.bootstrap.LocationPicker} this
26612          */
26613         OverlayViewOnAdd : true,
26614         /**
26615          * @event OverlayViewOnRemove
26616          * Fires when OverlayView Draw
26617          * @param {Roo.bootstrap.LocationPicker} this
26618          */
26619         OverlayViewOnRemove : true,
26620         /**
26621          * @event OverlayViewShow
26622          * Fires when OverlayView Draw
26623          * @param {Roo.bootstrap.LocationPicker} this
26624          * @param {Pixel} cpx
26625          */
26626         OverlayViewShow : true,
26627         /**
26628          * @event OverlayViewHide
26629          * Fires when OverlayView Draw
26630          * @param {Roo.bootstrap.LocationPicker} this
26631          */
26632         OverlayViewHide : true,
26633         /**
26634          * @event loadexception
26635          * Fires when load google lib failed.
26636          * @param {Roo.bootstrap.LocationPicker} this
26637          */
26638         loadexception : true
26639     });
26640         
26641 };
26642
26643 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26644     
26645     gMapContext: false,
26646     
26647     latitude: 0,
26648     longitude: 0,
26649     zoom: 15,
26650     mapTypeId: false,
26651     mapTypeControl: false,
26652     disableDoubleClickZoom: false,
26653     scrollwheel: true,
26654     streetViewControl: false,
26655     radius: 0,
26656     locationName: '',
26657     draggable: true,
26658     enableAutocomplete: false,
26659     enableReverseGeocode: true,
26660     markerTitle: '',
26661     
26662     getAutoCreate: function()
26663     {
26664
26665         var cfg = {
26666             tag: 'div',
26667             cls: 'roo-location-picker'
26668         };
26669         
26670         return cfg
26671     },
26672     
26673     initEvents: function(ct, position)
26674     {       
26675         if(!this.el.getWidth() || this.isApplied()){
26676             return;
26677         }
26678         
26679         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26680         
26681         this.initial();
26682     },
26683     
26684     initial: function()
26685     {
26686         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26687             this.fireEvent('loadexception', this);
26688             return;
26689         }
26690         
26691         if(!this.mapTypeId){
26692             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26693         }
26694         
26695         this.gMapContext = this.GMapContext();
26696         
26697         this.initOverlayView();
26698         
26699         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26700         
26701         var _this = this;
26702                 
26703         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26704             _this.setPosition(_this.gMapContext.marker.position);
26705         });
26706         
26707         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26708             _this.fireEvent('mapClick', this, event);
26709             
26710         });
26711
26712         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26713             _this.fireEvent('mapRightClick', this, event);
26714             
26715         });
26716         
26717         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26718             _this.fireEvent('markerClick', this, event);
26719             
26720         });
26721
26722         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26723             _this.fireEvent('markerRightClick', this, event);
26724             
26725         });
26726         
26727         this.setPosition(this.gMapContext.location);
26728         
26729         this.fireEvent('initial', this, this.gMapContext.location);
26730     },
26731     
26732     initOverlayView: function()
26733     {
26734         var _this = this;
26735         
26736         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26737             
26738             draw: function()
26739             {
26740                 _this.fireEvent('OverlayViewDraw', _this);
26741             },
26742             
26743             onAdd: function()
26744             {
26745                 _this.fireEvent('OverlayViewOnAdd', _this);
26746             },
26747             
26748             onRemove: function()
26749             {
26750                 _this.fireEvent('OverlayViewOnRemove', _this);
26751             },
26752             
26753             show: function(cpx)
26754             {
26755                 _this.fireEvent('OverlayViewShow', _this, cpx);
26756             },
26757             
26758             hide: function()
26759             {
26760                 _this.fireEvent('OverlayViewHide', _this);
26761             }
26762             
26763         });
26764     },
26765     
26766     fromLatLngToContainerPixel: function(event)
26767     {
26768         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26769     },
26770     
26771     isApplied: function() 
26772     {
26773         return this.getGmapContext() == false ? false : true;
26774     },
26775     
26776     getGmapContext: function() 
26777     {
26778         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26779     },
26780     
26781     GMapContext: function() 
26782     {
26783         var position = new google.maps.LatLng(this.latitude, this.longitude);
26784         
26785         var _map = new google.maps.Map(this.el.dom, {
26786             center: position,
26787             zoom: this.zoom,
26788             mapTypeId: this.mapTypeId,
26789             mapTypeControl: this.mapTypeControl,
26790             disableDoubleClickZoom: this.disableDoubleClickZoom,
26791             scrollwheel: this.scrollwheel,
26792             streetViewControl: this.streetViewControl,
26793             locationName: this.locationName,
26794             draggable: this.draggable,
26795             enableAutocomplete: this.enableAutocomplete,
26796             enableReverseGeocode: this.enableReverseGeocode
26797         });
26798         
26799         var _marker = new google.maps.Marker({
26800             position: position,
26801             map: _map,
26802             title: this.markerTitle,
26803             draggable: this.draggable
26804         });
26805         
26806         return {
26807             map: _map,
26808             marker: _marker,
26809             circle: null,
26810             location: position,
26811             radius: this.radius,
26812             locationName: this.locationName,
26813             addressComponents: {
26814                 formatted_address: null,
26815                 addressLine1: null,
26816                 addressLine2: null,
26817                 streetName: null,
26818                 streetNumber: null,
26819                 city: null,
26820                 district: null,
26821                 state: null,
26822                 stateOrProvince: null
26823             },
26824             settings: this,
26825             domContainer: this.el.dom,
26826             geodecoder: new google.maps.Geocoder()
26827         };
26828     },
26829     
26830     drawCircle: function(center, radius, options) 
26831     {
26832         if (this.gMapContext.circle != null) {
26833             this.gMapContext.circle.setMap(null);
26834         }
26835         if (radius > 0) {
26836             radius *= 1;
26837             options = Roo.apply({}, options, {
26838                 strokeColor: "#0000FF",
26839                 strokeOpacity: .35,
26840                 strokeWeight: 2,
26841                 fillColor: "#0000FF",
26842                 fillOpacity: .2
26843             });
26844             
26845             options.map = this.gMapContext.map;
26846             options.radius = radius;
26847             options.center = center;
26848             this.gMapContext.circle = new google.maps.Circle(options);
26849             return this.gMapContext.circle;
26850         }
26851         
26852         return null;
26853     },
26854     
26855     setPosition: function(location) 
26856     {
26857         this.gMapContext.location = location;
26858         this.gMapContext.marker.setPosition(location);
26859         this.gMapContext.map.panTo(location);
26860         this.drawCircle(location, this.gMapContext.radius, {});
26861         
26862         var _this = this;
26863         
26864         if (this.gMapContext.settings.enableReverseGeocode) {
26865             this.gMapContext.geodecoder.geocode({
26866                 latLng: this.gMapContext.location
26867             }, function(results, status) {
26868                 
26869                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26870                     _this.gMapContext.locationName = results[0].formatted_address;
26871                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26872                     
26873                     _this.fireEvent('positionchanged', this, location);
26874                 }
26875             });
26876             
26877             return;
26878         }
26879         
26880         this.fireEvent('positionchanged', this, location);
26881     },
26882     
26883     resize: function()
26884     {
26885         google.maps.event.trigger(this.gMapContext.map, "resize");
26886         
26887         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26888         
26889         this.fireEvent('resize', this);
26890     },
26891     
26892     setPositionByLatLng: function(latitude, longitude)
26893     {
26894         this.setPosition(new google.maps.LatLng(latitude, longitude));
26895     },
26896     
26897     getCurrentPosition: function() 
26898     {
26899         return {
26900             latitude: this.gMapContext.location.lat(),
26901             longitude: this.gMapContext.location.lng()
26902         };
26903     },
26904     
26905     getAddressName: function() 
26906     {
26907         return this.gMapContext.locationName;
26908     },
26909     
26910     getAddressComponents: function() 
26911     {
26912         return this.gMapContext.addressComponents;
26913     },
26914     
26915     address_component_from_google_geocode: function(address_components) 
26916     {
26917         var result = {};
26918         
26919         for (var i = 0; i < address_components.length; i++) {
26920             var component = address_components[i];
26921             if (component.types.indexOf("postal_code") >= 0) {
26922                 result.postalCode = component.short_name;
26923             } else if (component.types.indexOf("street_number") >= 0) {
26924                 result.streetNumber = component.short_name;
26925             } else if (component.types.indexOf("route") >= 0) {
26926                 result.streetName = component.short_name;
26927             } else if (component.types.indexOf("neighborhood") >= 0) {
26928                 result.city = component.short_name;
26929             } else if (component.types.indexOf("locality") >= 0) {
26930                 result.city = component.short_name;
26931             } else if (component.types.indexOf("sublocality") >= 0) {
26932                 result.district = component.short_name;
26933             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26934                 result.stateOrProvince = component.short_name;
26935             } else if (component.types.indexOf("country") >= 0) {
26936                 result.country = component.short_name;
26937             }
26938         }
26939         
26940         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26941         result.addressLine2 = "";
26942         return result;
26943     },
26944     
26945     setZoomLevel: function(zoom)
26946     {
26947         this.gMapContext.map.setZoom(zoom);
26948     },
26949     
26950     show: function()
26951     {
26952         if(!this.el){
26953             return;
26954         }
26955         
26956         this.el.show();
26957         
26958         this.resize();
26959         
26960         this.fireEvent('show', this);
26961     },
26962     
26963     hide: function()
26964     {
26965         if(!this.el){
26966             return;
26967         }
26968         
26969         this.el.hide();
26970         
26971         this.fireEvent('hide', this);
26972     }
26973     
26974 });
26975
26976 Roo.apply(Roo.bootstrap.LocationPicker, {
26977     
26978     OverlayView : function(map, options)
26979     {
26980         options = options || {};
26981         
26982         this.setMap(map);
26983     }
26984     
26985     
26986 });/*
26987  * - LGPL
26988  *
26989  * Alert
26990  * 
26991  */
26992
26993 /**
26994  * @class Roo.bootstrap.Alert
26995  * @extends Roo.bootstrap.Component
26996  * Bootstrap Alert class
26997  * @cfg {String} title The title of alert
26998  * @cfg {String} html The content of alert
26999  * @cfg {String} weight (  success | info | warning | danger )
27000  * @cfg {String} faicon font-awesomeicon
27001  * 
27002  * @constructor
27003  * Create a new alert
27004  * @param {Object} config The config object
27005  */
27006
27007
27008 Roo.bootstrap.Alert = function(config){
27009     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27010     
27011 };
27012
27013 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27014     
27015     title: '',
27016     html: '',
27017     weight: false,
27018     faicon: false,
27019     
27020     getAutoCreate : function()
27021     {
27022         
27023         var cfg = {
27024             tag : 'div',
27025             cls : 'alert',
27026             cn : [
27027                 {
27028                     tag : 'i',
27029                     cls : 'roo-alert-icon'
27030                     
27031                 },
27032                 {
27033                     tag : 'b',
27034                     cls : 'roo-alert-title',
27035                     html : this.title
27036                 },
27037                 {
27038                     tag : 'span',
27039                     cls : 'roo-alert-text',
27040                     html : this.html
27041                 }
27042             ]
27043         };
27044         
27045         if(this.faicon){
27046             cfg.cn[0].cls += ' fa ' + this.faicon;
27047         }
27048         
27049         if(this.weight){
27050             cfg.cls += ' alert-' + this.weight;
27051         }
27052         
27053         return cfg;
27054     },
27055     
27056     initEvents: function() 
27057     {
27058         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27059     },
27060     
27061     setTitle : function(str)
27062     {
27063         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27064     },
27065     
27066     setText : function(str)
27067     {
27068         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27069     },
27070     
27071     setWeight : function(weight)
27072     {
27073         if(this.weight){
27074             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27075         }
27076         
27077         this.weight = weight;
27078         
27079         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27080     },
27081     
27082     setIcon : function(icon)
27083     {
27084         if(this.faicon){
27085             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27086         }
27087         
27088         this.faicon = icon;
27089         
27090         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27091     },
27092     
27093     hide: function() 
27094     {
27095         this.el.hide();   
27096     },
27097     
27098     show: function() 
27099     {  
27100         this.el.show();   
27101     }
27102     
27103 });
27104
27105  
27106 /*
27107 * Licence: LGPL
27108 */
27109
27110 /**
27111  * @class Roo.bootstrap.UploadCropbox
27112  * @extends Roo.bootstrap.Component
27113  * Bootstrap UploadCropbox class
27114  * @cfg {String} emptyText show when image has been loaded
27115  * @cfg {String} rotateNotify show when image too small to rotate
27116  * @cfg {Number} errorTimeout default 3000
27117  * @cfg {Number} minWidth default 300
27118  * @cfg {Number} minHeight default 300
27119  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27120  * @cfg {Boolean} isDocument (true|false) default false
27121  * @cfg {String} url action url
27122  * @cfg {String} paramName default 'imageUpload'
27123  * @cfg {String} method default POST
27124  * @cfg {Boolean} loadMask (true|false) default true
27125  * @cfg {Boolean} loadingText default 'Loading...'
27126  * 
27127  * @constructor
27128  * Create a new UploadCropbox
27129  * @param {Object} config The config object
27130  */
27131
27132 Roo.bootstrap.UploadCropbox = function(config){
27133     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27134     
27135     this.addEvents({
27136         /**
27137          * @event beforeselectfile
27138          * Fire before select file
27139          * @param {Roo.bootstrap.UploadCropbox} this
27140          */
27141         "beforeselectfile" : true,
27142         /**
27143          * @event initial
27144          * Fire after initEvent
27145          * @param {Roo.bootstrap.UploadCropbox} this
27146          */
27147         "initial" : true,
27148         /**
27149          * @event crop
27150          * Fire after initEvent
27151          * @param {Roo.bootstrap.UploadCropbox} this
27152          * @param {String} data
27153          */
27154         "crop" : true,
27155         /**
27156          * @event prepare
27157          * Fire when preparing the file data
27158          * @param {Roo.bootstrap.UploadCropbox} this
27159          * @param {Object} file
27160          */
27161         "prepare" : true,
27162         /**
27163          * @event exception
27164          * Fire when get exception
27165          * @param {Roo.bootstrap.UploadCropbox} this
27166          * @param {XMLHttpRequest} xhr
27167          */
27168         "exception" : true,
27169         /**
27170          * @event beforeloadcanvas
27171          * Fire before load the canvas
27172          * @param {Roo.bootstrap.UploadCropbox} this
27173          * @param {String} src
27174          */
27175         "beforeloadcanvas" : true,
27176         /**
27177          * @event trash
27178          * Fire when trash image
27179          * @param {Roo.bootstrap.UploadCropbox} this
27180          */
27181         "trash" : true,
27182         /**
27183          * @event download
27184          * Fire when download the image
27185          * @param {Roo.bootstrap.UploadCropbox} this
27186          */
27187         "download" : true,
27188         /**
27189          * @event footerbuttonclick
27190          * Fire when footerbuttonclick
27191          * @param {Roo.bootstrap.UploadCropbox} this
27192          * @param {String} type
27193          */
27194         "footerbuttonclick" : true,
27195         /**
27196          * @event resize
27197          * Fire when resize
27198          * @param {Roo.bootstrap.UploadCropbox} this
27199          */
27200         "resize" : true,
27201         /**
27202          * @event rotate
27203          * Fire when rotate the image
27204          * @param {Roo.bootstrap.UploadCropbox} this
27205          * @param {String} pos
27206          */
27207         "rotate" : true,
27208         /**
27209          * @event inspect
27210          * Fire when inspect the file
27211          * @param {Roo.bootstrap.UploadCropbox} this
27212          * @param {Object} file
27213          */
27214         "inspect" : true,
27215         /**
27216          * @event upload
27217          * Fire when xhr upload the file
27218          * @param {Roo.bootstrap.UploadCropbox} this
27219          * @param {Object} data
27220          */
27221         "upload" : true,
27222         /**
27223          * @event arrange
27224          * Fire when arrange the file data
27225          * @param {Roo.bootstrap.UploadCropbox} this
27226          * @param {Object} formData
27227          */
27228         "arrange" : true
27229     });
27230     
27231     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27232 };
27233
27234 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27235     
27236     emptyText : 'Click to upload image',
27237     rotateNotify : 'Image is too small to rotate',
27238     errorTimeout : 3000,
27239     scale : 0,
27240     baseScale : 1,
27241     rotate : 0,
27242     dragable : false,
27243     pinching : false,
27244     mouseX : 0,
27245     mouseY : 0,
27246     cropData : false,
27247     minWidth : 300,
27248     minHeight : 300,
27249     file : false,
27250     exif : {},
27251     baseRotate : 1,
27252     cropType : 'image/jpeg',
27253     buttons : false,
27254     canvasLoaded : false,
27255     isDocument : false,
27256     method : 'POST',
27257     paramName : 'imageUpload',
27258     loadMask : true,
27259     loadingText : 'Loading...',
27260     maskEl : false,
27261     
27262     getAutoCreate : function()
27263     {
27264         var cfg = {
27265             tag : 'div',
27266             cls : 'roo-upload-cropbox',
27267             cn : [
27268                 {
27269                     tag : 'input',
27270                     cls : 'roo-upload-cropbox-selector',
27271                     type : 'file'
27272                 },
27273                 {
27274                     tag : 'div',
27275                     cls : 'roo-upload-cropbox-body',
27276                     style : 'cursor:pointer',
27277                     cn : [
27278                         {
27279                             tag : 'div',
27280                             cls : 'roo-upload-cropbox-preview'
27281                         },
27282                         {
27283                             tag : 'div',
27284                             cls : 'roo-upload-cropbox-thumb'
27285                         },
27286                         {
27287                             tag : 'div',
27288                             cls : 'roo-upload-cropbox-empty-notify',
27289                             html : this.emptyText
27290                         },
27291                         {
27292                             tag : 'div',
27293                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27294                             html : this.rotateNotify
27295                         }
27296                     ]
27297                 },
27298                 {
27299                     tag : 'div',
27300                     cls : 'roo-upload-cropbox-footer',
27301                     cn : {
27302                         tag : 'div',
27303                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27304                         cn : []
27305                     }
27306                 }
27307             ]
27308         };
27309         
27310         return cfg;
27311     },
27312     
27313     onRender : function(ct, position)
27314     {
27315         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27316         
27317         if (this.buttons.length) {
27318             
27319             Roo.each(this.buttons, function(bb) {
27320                 
27321                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27322                 
27323                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27324                 
27325             }, this);
27326         }
27327         
27328         if(this.loadMask){
27329             this.maskEl = this.el;
27330         }
27331     },
27332     
27333     initEvents : function()
27334     {
27335         this.urlAPI = (window.createObjectURL && window) || 
27336                                 (window.URL && URL.revokeObjectURL && URL) || 
27337                                 (window.webkitURL && webkitURL);
27338                         
27339         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27340         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27341         
27342         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27343         this.selectorEl.hide();
27344         
27345         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27346         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27347         
27348         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27349         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27350         this.thumbEl.hide();
27351         
27352         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27353         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27354         
27355         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27356         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27357         this.errorEl.hide();
27358         
27359         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27360         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27361         this.footerEl.hide();
27362         
27363         this.setThumbBoxSize();
27364         
27365         this.bind();
27366         
27367         this.resize();
27368         
27369         this.fireEvent('initial', this);
27370     },
27371
27372     bind : function()
27373     {
27374         var _this = this;
27375         
27376         window.addEventListener("resize", function() { _this.resize(); } );
27377         
27378         this.bodyEl.on('click', this.beforeSelectFile, this);
27379         
27380         if(Roo.isTouch){
27381             this.bodyEl.on('touchstart', this.onTouchStart, this);
27382             this.bodyEl.on('touchmove', this.onTouchMove, this);
27383             this.bodyEl.on('touchend', this.onTouchEnd, this);
27384         }
27385         
27386         if(!Roo.isTouch){
27387             this.bodyEl.on('mousedown', this.onMouseDown, this);
27388             this.bodyEl.on('mousemove', this.onMouseMove, this);
27389             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27390             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27391             Roo.get(document).on('mouseup', this.onMouseUp, this);
27392         }
27393         
27394         this.selectorEl.on('change', this.onFileSelected, this);
27395     },
27396     
27397     reset : function()
27398     {    
27399         this.scale = 0;
27400         this.baseScale = 1;
27401         this.rotate = 0;
27402         this.baseRotate = 1;
27403         this.dragable = false;
27404         this.pinching = false;
27405         this.mouseX = 0;
27406         this.mouseY = 0;
27407         this.cropData = false;
27408         this.notifyEl.dom.innerHTML = this.emptyText;
27409         
27410         this.selectorEl.dom.value = '';
27411         
27412     },
27413     
27414     resize : function()
27415     {
27416         if(this.fireEvent('resize', this) != false){
27417             this.setThumbBoxPosition();
27418             this.setCanvasPosition();
27419         }
27420     },
27421     
27422     onFooterButtonClick : function(e, el, o, type)
27423     {
27424         switch (type) {
27425             case 'rotate-left' :
27426                 this.onRotateLeft(e);
27427                 break;
27428             case 'rotate-right' :
27429                 this.onRotateRight(e);
27430                 break;
27431             case 'picture' :
27432                 this.beforeSelectFile(e);
27433                 break;
27434             case 'trash' :
27435                 this.trash(e);
27436                 break;
27437             case 'crop' :
27438                 this.crop(e);
27439                 break;
27440             case 'download' :
27441                 this.download(e);
27442                 break;
27443             default :
27444                 break;
27445         }
27446         
27447         this.fireEvent('footerbuttonclick', this, type);
27448     },
27449     
27450     beforeSelectFile : function(e)
27451     {
27452         e.preventDefault();
27453         
27454         if(this.fireEvent('beforeselectfile', this) != false){
27455             this.selectorEl.dom.click();
27456         }
27457     },
27458     
27459     onFileSelected : function(e)
27460     {
27461         e.preventDefault();
27462         
27463         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27464             return;
27465         }
27466         
27467         var file = this.selectorEl.dom.files[0];
27468         
27469         if(this.fireEvent('inspect', this, file) != false){
27470             this.prepare(file);
27471         }
27472         
27473     },
27474     
27475     trash : function(e)
27476     {
27477         this.fireEvent('trash', this);
27478     },
27479     
27480     download : function(e)
27481     {
27482         this.fireEvent('download', this);
27483     },
27484     
27485     loadCanvas : function(src)
27486     {   
27487         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27488             
27489             this.reset();
27490             
27491             this.imageEl = document.createElement('img');
27492             
27493             var _this = this;
27494             
27495             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27496             
27497             this.imageEl.src = src;
27498         }
27499     },
27500     
27501     onLoadCanvas : function()
27502     {   
27503         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27504         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27505         
27506         this.bodyEl.un('click', this.beforeSelectFile, this);
27507         
27508         this.notifyEl.hide();
27509         this.thumbEl.show();
27510         this.footerEl.show();
27511         
27512         this.baseRotateLevel();
27513         
27514         if(this.isDocument){
27515             this.setThumbBoxSize();
27516         }
27517         
27518         this.setThumbBoxPosition();
27519         
27520         this.baseScaleLevel();
27521         
27522         this.draw();
27523         
27524         this.resize();
27525         
27526         this.canvasLoaded = true;
27527         
27528         if(this.loadMask){
27529             this.maskEl.unmask();
27530         }
27531         
27532     },
27533     
27534     setCanvasPosition : function()
27535     {   
27536         if(!this.canvasEl){
27537             return;
27538         }
27539         
27540         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27541         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27542         
27543         this.previewEl.setLeft(pw);
27544         this.previewEl.setTop(ph);
27545         
27546     },
27547     
27548     onMouseDown : function(e)
27549     {   
27550         e.stopEvent();
27551         
27552         this.dragable = true;
27553         this.pinching = false;
27554         
27555         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27556             this.dragable = false;
27557             return;
27558         }
27559         
27560         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27561         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27562         
27563     },
27564     
27565     onMouseMove : function(e)
27566     {   
27567         e.stopEvent();
27568         
27569         if(!this.canvasLoaded){
27570             return;
27571         }
27572         
27573         if (!this.dragable){
27574             return;
27575         }
27576         
27577         var minX = Math.ceil(this.thumbEl.getLeft(true));
27578         var minY = Math.ceil(this.thumbEl.getTop(true));
27579         
27580         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27581         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27582         
27583         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27584         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27585         
27586         x = x - this.mouseX;
27587         y = y - this.mouseY;
27588         
27589         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27590         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27591         
27592         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27593         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27594         
27595         this.previewEl.setLeft(bgX);
27596         this.previewEl.setTop(bgY);
27597         
27598         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27599         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27600     },
27601     
27602     onMouseUp : function(e)
27603     {   
27604         e.stopEvent();
27605         
27606         this.dragable = false;
27607     },
27608     
27609     onMouseWheel : function(e)
27610     {   
27611         e.stopEvent();
27612         
27613         this.startScale = this.scale;
27614         
27615         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27616         
27617         if(!this.zoomable()){
27618             this.scale = this.startScale;
27619             return;
27620         }
27621         
27622         this.draw();
27623         
27624         return;
27625     },
27626     
27627     zoomable : function()
27628     {
27629         var minScale = this.thumbEl.getWidth() / this.minWidth;
27630         
27631         if(this.minWidth < this.minHeight){
27632             minScale = this.thumbEl.getHeight() / this.minHeight;
27633         }
27634         
27635         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27636         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27637         
27638         if(
27639                 this.isDocument &&
27640                 (this.rotate == 0 || this.rotate == 180) && 
27641                 (
27642                     width > this.imageEl.OriginWidth || 
27643                     height > this.imageEl.OriginHeight ||
27644                     (width < this.minWidth && height < this.minHeight)
27645                 )
27646         ){
27647             return false;
27648         }
27649         
27650         if(
27651                 this.isDocument &&
27652                 (this.rotate == 90 || this.rotate == 270) && 
27653                 (
27654                     width > this.imageEl.OriginWidth || 
27655                     height > this.imageEl.OriginHeight ||
27656                     (width < this.minHeight && height < this.minWidth)
27657                 )
27658         ){
27659             return false;
27660         }
27661         
27662         if(
27663                 !this.isDocument &&
27664                 (this.rotate == 0 || this.rotate == 180) && 
27665                 (
27666                     width < this.minWidth || 
27667                     width > this.imageEl.OriginWidth || 
27668                     height < this.minHeight || 
27669                     height > this.imageEl.OriginHeight
27670                 )
27671         ){
27672             return false;
27673         }
27674         
27675         if(
27676                 !this.isDocument &&
27677                 (this.rotate == 90 || this.rotate == 270) && 
27678                 (
27679                     width < this.minHeight || 
27680                     width > this.imageEl.OriginWidth || 
27681                     height < this.minWidth || 
27682                     height > this.imageEl.OriginHeight
27683                 )
27684         ){
27685             return false;
27686         }
27687         
27688         return true;
27689         
27690     },
27691     
27692     onRotateLeft : function(e)
27693     {   
27694         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27695             
27696             var minScale = this.thumbEl.getWidth() / this.minWidth;
27697             
27698             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27699             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27700             
27701             this.startScale = this.scale;
27702             
27703             while (this.getScaleLevel() < minScale){
27704             
27705                 this.scale = this.scale + 1;
27706                 
27707                 if(!this.zoomable()){
27708                     break;
27709                 }
27710                 
27711                 if(
27712                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27713                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27714                 ){
27715                     continue;
27716                 }
27717                 
27718                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27719
27720                 this.draw();
27721                 
27722                 return;
27723             }
27724             
27725             this.scale = this.startScale;
27726             
27727             this.onRotateFail();
27728             
27729             return false;
27730         }
27731         
27732         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27733
27734         if(this.isDocument){
27735             this.setThumbBoxSize();
27736             this.setThumbBoxPosition();
27737             this.setCanvasPosition();
27738         }
27739         
27740         this.draw();
27741         
27742         this.fireEvent('rotate', this, 'left');
27743         
27744     },
27745     
27746     onRotateRight : function(e)
27747     {
27748         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27749             
27750             var minScale = this.thumbEl.getWidth() / this.minWidth;
27751         
27752             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27753             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27754             
27755             this.startScale = this.scale;
27756             
27757             while (this.getScaleLevel() < minScale){
27758             
27759                 this.scale = this.scale + 1;
27760                 
27761                 if(!this.zoomable()){
27762                     break;
27763                 }
27764                 
27765                 if(
27766                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27767                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27768                 ){
27769                     continue;
27770                 }
27771                 
27772                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27773
27774                 this.draw();
27775                 
27776                 return;
27777             }
27778             
27779             this.scale = this.startScale;
27780             
27781             this.onRotateFail();
27782             
27783             return false;
27784         }
27785         
27786         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27787
27788         if(this.isDocument){
27789             this.setThumbBoxSize();
27790             this.setThumbBoxPosition();
27791             this.setCanvasPosition();
27792         }
27793         
27794         this.draw();
27795         
27796         this.fireEvent('rotate', this, 'right');
27797     },
27798     
27799     onRotateFail : function()
27800     {
27801         this.errorEl.show(true);
27802         
27803         var _this = this;
27804         
27805         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27806     },
27807     
27808     draw : function()
27809     {
27810         this.previewEl.dom.innerHTML = '';
27811         
27812         var canvasEl = document.createElement("canvas");
27813         
27814         var contextEl = canvasEl.getContext("2d");
27815         
27816         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27817         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27818         var center = this.imageEl.OriginWidth / 2;
27819         
27820         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27821             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27822             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27823             center = this.imageEl.OriginHeight / 2;
27824         }
27825         
27826         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27827         
27828         contextEl.translate(center, center);
27829         contextEl.rotate(this.rotate * Math.PI / 180);
27830
27831         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27832         
27833         this.canvasEl = document.createElement("canvas");
27834         
27835         this.contextEl = this.canvasEl.getContext("2d");
27836         
27837         switch (this.rotate) {
27838             case 0 :
27839                 
27840                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27841                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27842                 
27843                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27844                 
27845                 break;
27846             case 90 : 
27847                 
27848                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27849                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27850                 
27851                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27852                     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);
27853                     break;
27854                 }
27855                 
27856                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27857                 
27858                 break;
27859             case 180 :
27860                 
27861                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27862                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27863                 
27864                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27865                     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);
27866                     break;
27867                 }
27868                 
27869                 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);
27870                 
27871                 break;
27872             case 270 :
27873                 
27874                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27875                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27876         
27877                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27878                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27879                     break;
27880                 }
27881                 
27882                 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);
27883                 
27884                 break;
27885             default : 
27886                 break;
27887         }
27888         
27889         this.previewEl.appendChild(this.canvasEl);
27890         
27891         this.setCanvasPosition();
27892     },
27893     
27894     crop : function()
27895     {
27896         if(!this.canvasLoaded){
27897             return;
27898         }
27899         
27900         var imageCanvas = document.createElement("canvas");
27901         
27902         var imageContext = imageCanvas.getContext("2d");
27903         
27904         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27905         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27906         
27907         var center = imageCanvas.width / 2;
27908         
27909         imageContext.translate(center, center);
27910         
27911         imageContext.rotate(this.rotate * Math.PI / 180);
27912         
27913         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27914         
27915         var canvas = document.createElement("canvas");
27916         
27917         var context = canvas.getContext("2d");
27918                 
27919         canvas.width = this.minWidth;
27920         canvas.height = this.minHeight;
27921
27922         switch (this.rotate) {
27923             case 0 :
27924                 
27925                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27926                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27927                 
27928                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27929                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27930                 
27931                 var targetWidth = this.minWidth - 2 * x;
27932                 var targetHeight = this.minHeight - 2 * y;
27933                 
27934                 var scale = 1;
27935                 
27936                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27937                     scale = targetWidth / width;
27938                 }
27939                 
27940                 if(x > 0 && y == 0){
27941                     scale = targetHeight / height;
27942                 }
27943                 
27944                 if(x > 0 && y > 0){
27945                     scale = targetWidth / width;
27946                     
27947                     if(width < height){
27948                         scale = targetHeight / height;
27949                     }
27950                 }
27951                 
27952                 context.scale(scale, scale);
27953                 
27954                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27955                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27956
27957                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27958                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27959
27960                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27961                 
27962                 break;
27963             case 90 : 
27964                 
27965                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27966                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27967                 
27968                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27969                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27970                 
27971                 var targetWidth = this.minWidth - 2 * x;
27972                 var targetHeight = this.minHeight - 2 * y;
27973                 
27974                 var scale = 1;
27975                 
27976                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27977                     scale = targetWidth / width;
27978                 }
27979                 
27980                 if(x > 0 && y == 0){
27981                     scale = targetHeight / height;
27982                 }
27983                 
27984                 if(x > 0 && y > 0){
27985                     scale = targetWidth / width;
27986                     
27987                     if(width < height){
27988                         scale = targetHeight / height;
27989                     }
27990                 }
27991                 
27992                 context.scale(scale, scale);
27993                 
27994                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27995                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27996
27997                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27998                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27999                 
28000                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28001                 
28002                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28003                 
28004                 break;
28005             case 180 :
28006                 
28007                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28008                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28009                 
28010                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28011                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28012                 
28013                 var targetWidth = this.minWidth - 2 * x;
28014                 var targetHeight = this.minHeight - 2 * y;
28015                 
28016                 var scale = 1;
28017                 
28018                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28019                     scale = targetWidth / width;
28020                 }
28021                 
28022                 if(x > 0 && y == 0){
28023                     scale = targetHeight / height;
28024                 }
28025                 
28026                 if(x > 0 && y > 0){
28027                     scale = targetWidth / width;
28028                     
28029                     if(width < height){
28030                         scale = targetHeight / height;
28031                     }
28032                 }
28033                 
28034                 context.scale(scale, scale);
28035                 
28036                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28037                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28038
28039                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28040                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28041
28042                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28043                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28044                 
28045                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28046                 
28047                 break;
28048             case 270 :
28049                 
28050                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28051                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28052                 
28053                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28054                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28055                 
28056                 var targetWidth = this.minWidth - 2 * x;
28057                 var targetHeight = this.minHeight - 2 * y;
28058                 
28059                 var scale = 1;
28060                 
28061                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28062                     scale = targetWidth / width;
28063                 }
28064                 
28065                 if(x > 0 && y == 0){
28066                     scale = targetHeight / height;
28067                 }
28068                 
28069                 if(x > 0 && y > 0){
28070                     scale = targetWidth / width;
28071                     
28072                     if(width < height){
28073                         scale = targetHeight / height;
28074                     }
28075                 }
28076                 
28077                 context.scale(scale, scale);
28078                 
28079                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28080                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28081
28082                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28083                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28084                 
28085                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28086                 
28087                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28088                 
28089                 break;
28090             default : 
28091                 break;
28092         }
28093         
28094         this.cropData = canvas.toDataURL(this.cropType);
28095         
28096         if(this.fireEvent('crop', this, this.cropData) !== false){
28097             this.process(this.file, this.cropData);
28098         }
28099         
28100         return;
28101         
28102     },
28103     
28104     setThumbBoxSize : function()
28105     {
28106         var width, height;
28107         
28108         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28109             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28110             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28111             
28112             this.minWidth = width;
28113             this.minHeight = height;
28114             
28115             if(this.rotate == 90 || this.rotate == 270){
28116                 this.minWidth = height;
28117                 this.minHeight = width;
28118             }
28119         }
28120         
28121         height = 300;
28122         width = Math.ceil(this.minWidth * height / this.minHeight);
28123         
28124         if(this.minWidth > this.minHeight){
28125             width = 300;
28126             height = Math.ceil(this.minHeight * width / this.minWidth);
28127         }
28128         
28129         this.thumbEl.setStyle({
28130             width : width + 'px',
28131             height : height + 'px'
28132         });
28133
28134         return;
28135             
28136     },
28137     
28138     setThumbBoxPosition : function()
28139     {
28140         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28141         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28142         
28143         this.thumbEl.setLeft(x);
28144         this.thumbEl.setTop(y);
28145         
28146     },
28147     
28148     baseRotateLevel : function()
28149     {
28150         this.baseRotate = 1;
28151         
28152         if(
28153                 typeof(this.exif) != 'undefined' &&
28154                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28155                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28156         ){
28157             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28158         }
28159         
28160         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28161         
28162     },
28163     
28164     baseScaleLevel : function()
28165     {
28166         var width, height;
28167         
28168         if(this.isDocument){
28169             
28170             if(this.baseRotate == 6 || this.baseRotate == 8){
28171             
28172                 height = this.thumbEl.getHeight();
28173                 this.baseScale = height / this.imageEl.OriginWidth;
28174
28175                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28176                     width = this.thumbEl.getWidth();
28177                     this.baseScale = width / this.imageEl.OriginHeight;
28178                 }
28179
28180                 return;
28181             }
28182
28183             height = this.thumbEl.getHeight();
28184             this.baseScale = height / this.imageEl.OriginHeight;
28185
28186             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28187                 width = this.thumbEl.getWidth();
28188                 this.baseScale = width / this.imageEl.OriginWidth;
28189             }
28190
28191             return;
28192         }
28193         
28194         if(this.baseRotate == 6 || this.baseRotate == 8){
28195             
28196             width = this.thumbEl.getHeight();
28197             this.baseScale = width / this.imageEl.OriginHeight;
28198             
28199             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28200                 height = this.thumbEl.getWidth();
28201                 this.baseScale = height / this.imageEl.OriginHeight;
28202             }
28203             
28204             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28205                 height = this.thumbEl.getWidth();
28206                 this.baseScale = height / this.imageEl.OriginHeight;
28207                 
28208                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28209                     width = this.thumbEl.getHeight();
28210                     this.baseScale = width / this.imageEl.OriginWidth;
28211                 }
28212             }
28213             
28214             return;
28215         }
28216         
28217         width = this.thumbEl.getWidth();
28218         this.baseScale = width / this.imageEl.OriginWidth;
28219         
28220         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28221             height = this.thumbEl.getHeight();
28222             this.baseScale = height / this.imageEl.OriginHeight;
28223         }
28224         
28225         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28226             
28227             height = this.thumbEl.getHeight();
28228             this.baseScale = height / this.imageEl.OriginHeight;
28229             
28230             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28231                 width = this.thumbEl.getWidth();
28232                 this.baseScale = width / this.imageEl.OriginWidth;
28233             }
28234             
28235         }
28236         
28237         return;
28238     },
28239     
28240     getScaleLevel : function()
28241     {
28242         return this.baseScale * Math.pow(1.1, this.scale);
28243     },
28244     
28245     onTouchStart : function(e)
28246     {
28247         if(!this.canvasLoaded){
28248             this.beforeSelectFile(e);
28249             return;
28250         }
28251         
28252         var touches = e.browserEvent.touches;
28253         
28254         if(!touches){
28255             return;
28256         }
28257         
28258         if(touches.length == 1){
28259             this.onMouseDown(e);
28260             return;
28261         }
28262         
28263         if(touches.length != 2){
28264             return;
28265         }
28266         
28267         var coords = [];
28268         
28269         for(var i = 0, finger; finger = touches[i]; i++){
28270             coords.push(finger.pageX, finger.pageY);
28271         }
28272         
28273         var x = Math.pow(coords[0] - coords[2], 2);
28274         var y = Math.pow(coords[1] - coords[3], 2);
28275         
28276         this.startDistance = Math.sqrt(x + y);
28277         
28278         this.startScale = this.scale;
28279         
28280         this.pinching = true;
28281         this.dragable = false;
28282         
28283     },
28284     
28285     onTouchMove : function(e)
28286     {
28287         if(!this.pinching && !this.dragable){
28288             return;
28289         }
28290         
28291         var touches = e.browserEvent.touches;
28292         
28293         if(!touches){
28294             return;
28295         }
28296         
28297         if(this.dragable){
28298             this.onMouseMove(e);
28299             return;
28300         }
28301         
28302         var coords = [];
28303         
28304         for(var i = 0, finger; finger = touches[i]; i++){
28305             coords.push(finger.pageX, finger.pageY);
28306         }
28307         
28308         var x = Math.pow(coords[0] - coords[2], 2);
28309         var y = Math.pow(coords[1] - coords[3], 2);
28310         
28311         this.endDistance = Math.sqrt(x + y);
28312         
28313         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28314         
28315         if(!this.zoomable()){
28316             this.scale = this.startScale;
28317             return;
28318         }
28319         
28320         this.draw();
28321         
28322     },
28323     
28324     onTouchEnd : function(e)
28325     {
28326         this.pinching = false;
28327         this.dragable = false;
28328         
28329     },
28330     
28331     process : function(file, crop)
28332     {
28333         if(this.loadMask){
28334             this.maskEl.mask(this.loadingText);
28335         }
28336         
28337         this.xhr = new XMLHttpRequest();
28338         
28339         file.xhr = this.xhr;
28340
28341         this.xhr.open(this.method, this.url, true);
28342         
28343         var headers = {
28344             "Accept": "application/json",
28345             "Cache-Control": "no-cache",
28346             "X-Requested-With": "XMLHttpRequest"
28347         };
28348         
28349         for (var headerName in headers) {
28350             var headerValue = headers[headerName];
28351             if (headerValue) {
28352                 this.xhr.setRequestHeader(headerName, headerValue);
28353             }
28354         }
28355         
28356         var _this = this;
28357         
28358         this.xhr.onload = function()
28359         {
28360             _this.xhrOnLoad(_this.xhr);
28361         }
28362         
28363         this.xhr.onerror = function()
28364         {
28365             _this.xhrOnError(_this.xhr);
28366         }
28367         
28368         var formData = new FormData();
28369
28370         formData.append('returnHTML', 'NO');
28371         
28372         if(crop){
28373             formData.append('crop', crop);
28374         }
28375         
28376         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28377             formData.append(this.paramName, file, file.name);
28378         }
28379         
28380         if(typeof(file.filename) != 'undefined'){
28381             formData.append('filename', file.filename);
28382         }
28383         
28384         if(typeof(file.mimetype) != 'undefined'){
28385             formData.append('mimetype', file.mimetype);
28386         }
28387         
28388         if(this.fireEvent('arrange', this, formData) != false){
28389             this.xhr.send(formData);
28390         };
28391     },
28392     
28393     xhrOnLoad : function(xhr)
28394     {
28395         if(this.loadMask){
28396             this.maskEl.unmask();
28397         }
28398         
28399         if (xhr.readyState !== 4) {
28400             this.fireEvent('exception', this, xhr);
28401             return;
28402         }
28403
28404         var response = Roo.decode(xhr.responseText);
28405         
28406         if(!response.success){
28407             this.fireEvent('exception', this, xhr);
28408             return;
28409         }
28410         
28411         var response = Roo.decode(xhr.responseText);
28412         
28413         this.fireEvent('upload', this, response);
28414         
28415     },
28416     
28417     xhrOnError : function()
28418     {
28419         if(this.loadMask){
28420             this.maskEl.unmask();
28421         }
28422         
28423         Roo.log('xhr on error');
28424         
28425         var response = Roo.decode(xhr.responseText);
28426           
28427         Roo.log(response);
28428         
28429     },
28430     
28431     prepare : function(file)
28432     {   
28433         if(this.loadMask){
28434             this.maskEl.mask(this.loadingText);
28435         }
28436         
28437         this.file = false;
28438         this.exif = {};
28439         
28440         if(typeof(file) === 'string'){
28441             this.loadCanvas(file);
28442             return;
28443         }
28444         
28445         if(!file || !this.urlAPI){
28446             return;
28447         }
28448         
28449         this.file = file;
28450         this.cropType = file.type;
28451         
28452         var _this = this;
28453         
28454         if(this.fireEvent('prepare', this, this.file) != false){
28455             
28456             var reader = new FileReader();
28457             
28458             reader.onload = function (e) {
28459                 if (e.target.error) {
28460                     Roo.log(e.target.error);
28461                     return;
28462                 }
28463                 
28464                 var buffer = e.target.result,
28465                     dataView = new DataView(buffer),
28466                     offset = 2,
28467                     maxOffset = dataView.byteLength - 4,
28468                     markerBytes,
28469                     markerLength;
28470                 
28471                 if (dataView.getUint16(0) === 0xffd8) {
28472                     while (offset < maxOffset) {
28473                         markerBytes = dataView.getUint16(offset);
28474                         
28475                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28476                             markerLength = dataView.getUint16(offset + 2) + 2;
28477                             if (offset + markerLength > dataView.byteLength) {
28478                                 Roo.log('Invalid meta data: Invalid segment size.');
28479                                 break;
28480                             }
28481                             
28482                             if(markerBytes == 0xffe1){
28483                                 _this.parseExifData(
28484                                     dataView,
28485                                     offset,
28486                                     markerLength
28487                                 );
28488                             }
28489                             
28490                             offset += markerLength;
28491                             
28492                             continue;
28493                         }
28494                         
28495                         break;
28496                     }
28497                     
28498                 }
28499                 
28500                 var url = _this.urlAPI.createObjectURL(_this.file);
28501                 
28502                 _this.loadCanvas(url);
28503                 
28504                 return;
28505             }
28506             
28507             reader.readAsArrayBuffer(this.file);
28508             
28509         }
28510         
28511     },
28512     
28513     parseExifData : function(dataView, offset, length)
28514     {
28515         var tiffOffset = offset + 10,
28516             littleEndian,
28517             dirOffset;
28518     
28519         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28520             // No Exif data, might be XMP data instead
28521             return;
28522         }
28523         
28524         // Check for the ASCII code for "Exif" (0x45786966):
28525         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28526             // No Exif data, might be XMP data instead
28527             return;
28528         }
28529         if (tiffOffset + 8 > dataView.byteLength) {
28530             Roo.log('Invalid Exif data: Invalid segment size.');
28531             return;
28532         }
28533         // Check for the two null bytes:
28534         if (dataView.getUint16(offset + 8) !== 0x0000) {
28535             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28536             return;
28537         }
28538         // Check the byte alignment:
28539         switch (dataView.getUint16(tiffOffset)) {
28540         case 0x4949:
28541             littleEndian = true;
28542             break;
28543         case 0x4D4D:
28544             littleEndian = false;
28545             break;
28546         default:
28547             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28548             return;
28549         }
28550         // Check for the TIFF tag marker (0x002A):
28551         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28552             Roo.log('Invalid Exif data: Missing TIFF marker.');
28553             return;
28554         }
28555         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28556         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28557         
28558         this.parseExifTags(
28559             dataView,
28560             tiffOffset,
28561             tiffOffset + dirOffset,
28562             littleEndian
28563         );
28564     },
28565     
28566     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28567     {
28568         var tagsNumber,
28569             dirEndOffset,
28570             i;
28571         if (dirOffset + 6 > dataView.byteLength) {
28572             Roo.log('Invalid Exif data: Invalid directory offset.');
28573             return;
28574         }
28575         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28576         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28577         if (dirEndOffset + 4 > dataView.byteLength) {
28578             Roo.log('Invalid Exif data: Invalid directory size.');
28579             return;
28580         }
28581         for (i = 0; i < tagsNumber; i += 1) {
28582             this.parseExifTag(
28583                 dataView,
28584                 tiffOffset,
28585                 dirOffset + 2 + 12 * i, // tag offset
28586                 littleEndian
28587             );
28588         }
28589         // Return the offset to the next directory:
28590         return dataView.getUint32(dirEndOffset, littleEndian);
28591     },
28592     
28593     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28594     {
28595         var tag = dataView.getUint16(offset, littleEndian);
28596         
28597         this.exif[tag] = this.getExifValue(
28598             dataView,
28599             tiffOffset,
28600             offset,
28601             dataView.getUint16(offset + 2, littleEndian), // tag type
28602             dataView.getUint32(offset + 4, littleEndian), // tag length
28603             littleEndian
28604         );
28605     },
28606     
28607     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28608     {
28609         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28610             tagSize,
28611             dataOffset,
28612             values,
28613             i,
28614             str,
28615             c;
28616     
28617         if (!tagType) {
28618             Roo.log('Invalid Exif data: Invalid tag type.');
28619             return;
28620         }
28621         
28622         tagSize = tagType.size * length;
28623         // Determine if the value is contained in the dataOffset bytes,
28624         // or if the value at the dataOffset is a pointer to the actual data:
28625         dataOffset = tagSize > 4 ?
28626                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28627         if (dataOffset + tagSize > dataView.byteLength) {
28628             Roo.log('Invalid Exif data: Invalid data offset.');
28629             return;
28630         }
28631         if (length === 1) {
28632             return tagType.getValue(dataView, dataOffset, littleEndian);
28633         }
28634         values = [];
28635         for (i = 0; i < length; i += 1) {
28636             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28637         }
28638         
28639         if (tagType.ascii) {
28640             str = '';
28641             // Concatenate the chars:
28642             for (i = 0; i < values.length; i += 1) {
28643                 c = values[i];
28644                 // Ignore the terminating NULL byte(s):
28645                 if (c === '\u0000') {
28646                     break;
28647                 }
28648                 str += c;
28649             }
28650             return str;
28651         }
28652         return values;
28653     }
28654     
28655 });
28656
28657 Roo.apply(Roo.bootstrap.UploadCropbox, {
28658     tags : {
28659         'Orientation': 0x0112
28660     },
28661     
28662     Orientation: {
28663             1: 0, //'top-left',
28664 //            2: 'top-right',
28665             3: 180, //'bottom-right',
28666 //            4: 'bottom-left',
28667 //            5: 'left-top',
28668             6: 90, //'right-top',
28669 //            7: 'right-bottom',
28670             8: 270 //'left-bottom'
28671     },
28672     
28673     exifTagTypes : {
28674         // byte, 8-bit unsigned int:
28675         1: {
28676             getValue: function (dataView, dataOffset) {
28677                 return dataView.getUint8(dataOffset);
28678             },
28679             size: 1
28680         },
28681         // ascii, 8-bit byte:
28682         2: {
28683             getValue: function (dataView, dataOffset) {
28684                 return String.fromCharCode(dataView.getUint8(dataOffset));
28685             },
28686             size: 1,
28687             ascii: true
28688         },
28689         // short, 16 bit int:
28690         3: {
28691             getValue: function (dataView, dataOffset, littleEndian) {
28692                 return dataView.getUint16(dataOffset, littleEndian);
28693             },
28694             size: 2
28695         },
28696         // long, 32 bit int:
28697         4: {
28698             getValue: function (dataView, dataOffset, littleEndian) {
28699                 return dataView.getUint32(dataOffset, littleEndian);
28700             },
28701             size: 4
28702         },
28703         // rational = two long values, first is numerator, second is denominator:
28704         5: {
28705             getValue: function (dataView, dataOffset, littleEndian) {
28706                 return dataView.getUint32(dataOffset, littleEndian) /
28707                     dataView.getUint32(dataOffset + 4, littleEndian);
28708             },
28709             size: 8
28710         },
28711         // slong, 32 bit signed int:
28712         9: {
28713             getValue: function (dataView, dataOffset, littleEndian) {
28714                 return dataView.getInt32(dataOffset, littleEndian);
28715             },
28716             size: 4
28717         },
28718         // srational, two slongs, first is numerator, second is denominator:
28719         10: {
28720             getValue: function (dataView, dataOffset, littleEndian) {
28721                 return dataView.getInt32(dataOffset, littleEndian) /
28722                     dataView.getInt32(dataOffset + 4, littleEndian);
28723             },
28724             size: 8
28725         }
28726     },
28727     
28728     footer : {
28729         STANDARD : [
28730             {
28731                 tag : 'div',
28732                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28733                 action : 'rotate-left',
28734                 cn : [
28735                     {
28736                         tag : 'button',
28737                         cls : 'btn btn-default',
28738                         html : '<i class="fa fa-undo"></i>'
28739                     }
28740                 ]
28741             },
28742             {
28743                 tag : 'div',
28744                 cls : 'btn-group roo-upload-cropbox-picture',
28745                 action : 'picture',
28746                 cn : [
28747                     {
28748                         tag : 'button',
28749                         cls : 'btn btn-default',
28750                         html : '<i class="fa fa-picture-o"></i>'
28751                     }
28752                 ]
28753             },
28754             {
28755                 tag : 'div',
28756                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28757                 action : 'rotate-right',
28758                 cn : [
28759                     {
28760                         tag : 'button',
28761                         cls : 'btn btn-default',
28762                         html : '<i class="fa fa-repeat"></i>'
28763                     }
28764                 ]
28765             }
28766         ],
28767         DOCUMENT : [
28768             {
28769                 tag : 'div',
28770                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28771                 action : 'rotate-left',
28772                 cn : [
28773                     {
28774                         tag : 'button',
28775                         cls : 'btn btn-default',
28776                         html : '<i class="fa fa-undo"></i>'
28777                     }
28778                 ]
28779             },
28780             {
28781                 tag : 'div',
28782                 cls : 'btn-group roo-upload-cropbox-download',
28783                 action : 'download',
28784                 cn : [
28785                     {
28786                         tag : 'button',
28787                         cls : 'btn btn-default',
28788                         html : '<i class="fa fa-download"></i>'
28789                     }
28790                 ]
28791             },
28792             {
28793                 tag : 'div',
28794                 cls : 'btn-group roo-upload-cropbox-crop',
28795                 action : 'crop',
28796                 cn : [
28797                     {
28798                         tag : 'button',
28799                         cls : 'btn btn-default',
28800                         html : '<i class="fa fa-crop"></i>'
28801                     }
28802                 ]
28803             },
28804             {
28805                 tag : 'div',
28806                 cls : 'btn-group roo-upload-cropbox-trash',
28807                 action : 'trash',
28808                 cn : [
28809                     {
28810                         tag : 'button',
28811                         cls : 'btn btn-default',
28812                         html : '<i class="fa fa-trash"></i>'
28813                     }
28814                 ]
28815             },
28816             {
28817                 tag : 'div',
28818                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28819                 action : 'rotate-right',
28820                 cn : [
28821                     {
28822                         tag : 'button',
28823                         cls : 'btn btn-default',
28824                         html : '<i class="fa fa-repeat"></i>'
28825                     }
28826                 ]
28827             }
28828         ],
28829         ROTATOR : [
28830             {
28831                 tag : 'div',
28832                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28833                 action : 'rotate-left',
28834                 cn : [
28835                     {
28836                         tag : 'button',
28837                         cls : 'btn btn-default',
28838                         html : '<i class="fa fa-undo"></i>'
28839                     }
28840                 ]
28841             },
28842             {
28843                 tag : 'div',
28844                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28845                 action : 'rotate-right',
28846                 cn : [
28847                     {
28848                         tag : 'button',
28849                         cls : 'btn btn-default',
28850                         html : '<i class="fa fa-repeat"></i>'
28851                     }
28852                 ]
28853             }
28854         ]
28855     }
28856 });
28857
28858 /*
28859 * Licence: LGPL
28860 */
28861
28862 /**
28863  * @class Roo.bootstrap.DocumentManager
28864  * @extends Roo.bootstrap.Component
28865  * Bootstrap DocumentManager class
28866  * @cfg {String} paramName default 'imageUpload'
28867  * @cfg {String} toolTipName default 'filename'
28868  * @cfg {String} method default POST
28869  * @cfg {String} url action url
28870  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28871  * @cfg {Boolean} multiple multiple upload default true
28872  * @cfg {Number} thumbSize default 300
28873  * @cfg {String} fieldLabel
28874  * @cfg {Number} labelWidth default 4
28875  * @cfg {String} labelAlign (left|top) default left
28876  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28877 * @cfg {Number} labellg set the width of label (1-12)
28878  * @cfg {Number} labelmd set the width of label (1-12)
28879  * @cfg {Number} labelsm set the width of label (1-12)
28880  * @cfg {Number} labelxs set the width of label (1-12)
28881  * 
28882  * @constructor
28883  * Create a new DocumentManager
28884  * @param {Object} config The config object
28885  */
28886
28887 Roo.bootstrap.DocumentManager = function(config){
28888     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28889     
28890     this.files = [];
28891     this.delegates = [];
28892     
28893     this.addEvents({
28894         /**
28895          * @event initial
28896          * Fire when initial the DocumentManager
28897          * @param {Roo.bootstrap.DocumentManager} this
28898          */
28899         "initial" : true,
28900         /**
28901          * @event inspect
28902          * inspect selected file
28903          * @param {Roo.bootstrap.DocumentManager} this
28904          * @param {File} file
28905          */
28906         "inspect" : true,
28907         /**
28908          * @event exception
28909          * Fire when xhr load exception
28910          * @param {Roo.bootstrap.DocumentManager} this
28911          * @param {XMLHttpRequest} xhr
28912          */
28913         "exception" : true,
28914         /**
28915          * @event afterupload
28916          * Fire when xhr load exception
28917          * @param {Roo.bootstrap.DocumentManager} this
28918          * @param {XMLHttpRequest} xhr
28919          */
28920         "afterupload" : true,
28921         /**
28922          * @event prepare
28923          * prepare the form data
28924          * @param {Roo.bootstrap.DocumentManager} this
28925          * @param {Object} formData
28926          */
28927         "prepare" : true,
28928         /**
28929          * @event remove
28930          * Fire when remove the file
28931          * @param {Roo.bootstrap.DocumentManager} this
28932          * @param {Object} file
28933          */
28934         "remove" : true,
28935         /**
28936          * @event refresh
28937          * Fire after refresh the file
28938          * @param {Roo.bootstrap.DocumentManager} this
28939          */
28940         "refresh" : true,
28941         /**
28942          * @event click
28943          * Fire after click the image
28944          * @param {Roo.bootstrap.DocumentManager} this
28945          * @param {Object} file
28946          */
28947         "click" : true,
28948         /**
28949          * @event edit
28950          * Fire when upload a image and editable set to true
28951          * @param {Roo.bootstrap.DocumentManager} this
28952          * @param {Object} file
28953          */
28954         "edit" : true,
28955         /**
28956          * @event beforeselectfile
28957          * Fire before select file
28958          * @param {Roo.bootstrap.DocumentManager} this
28959          */
28960         "beforeselectfile" : true,
28961         /**
28962          * @event process
28963          * Fire before process file
28964          * @param {Roo.bootstrap.DocumentManager} this
28965          * @param {Object} file
28966          */
28967         "process" : true,
28968         /**
28969          * @event previewrendered
28970          * Fire when preview rendered
28971          * @param {Roo.bootstrap.DocumentManager} this
28972          * @param {Object} file
28973          */
28974         "previewrendered" : true,
28975         /**
28976          */
28977         "previewResize" : true
28978         
28979     });
28980 };
28981
28982 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28983     
28984     boxes : 0,
28985     inputName : '',
28986     thumbSize : 300,
28987     multiple : true,
28988     files : false,
28989     method : 'POST',
28990     url : '',
28991     paramName : 'imageUpload',
28992     toolTipName : 'filename',
28993     fieldLabel : '',
28994     labelWidth : 4,
28995     labelAlign : 'left',
28996     editable : true,
28997     delegates : false,
28998     xhr : false, 
28999     
29000     labellg : 0,
29001     labelmd : 0,
29002     labelsm : 0,
29003     labelxs : 0,
29004     
29005     getAutoCreate : function()
29006     {   
29007         var managerWidget = {
29008             tag : 'div',
29009             cls : 'roo-document-manager',
29010             cn : [
29011                 {
29012                     tag : 'input',
29013                     cls : 'roo-document-manager-selector',
29014                     type : 'file'
29015                 },
29016                 {
29017                     tag : 'div',
29018                     cls : 'roo-document-manager-uploader',
29019                     cn : [
29020                         {
29021                             tag : 'div',
29022                             cls : 'roo-document-manager-upload-btn',
29023                             html : '<i class="fa fa-plus"></i>'
29024                         }
29025                     ]
29026                     
29027                 }
29028             ]
29029         };
29030         
29031         var content = [
29032             {
29033                 tag : 'div',
29034                 cls : 'column col-md-12',
29035                 cn : managerWidget
29036             }
29037         ];
29038         
29039         if(this.fieldLabel.length){
29040             
29041             content = [
29042                 {
29043                     tag : 'div',
29044                     cls : 'column col-md-12',
29045                     html : this.fieldLabel
29046                 },
29047                 {
29048                     tag : 'div',
29049                     cls : 'column col-md-12',
29050                     cn : managerWidget
29051                 }
29052             ];
29053
29054             if(this.labelAlign == 'left'){
29055                 content = [
29056                     {
29057                         tag : 'div',
29058                         cls : 'column',
29059                         html : this.fieldLabel
29060                     },
29061                     {
29062                         tag : 'div',
29063                         cls : 'column',
29064                         cn : managerWidget
29065                     }
29066                 ];
29067                 
29068                 if(this.labelWidth > 12){
29069                     content[0].style = "width: " + this.labelWidth + 'px';
29070                 }
29071
29072                 if(this.labelWidth < 13 && this.labelmd == 0){
29073                     this.labelmd = this.labelWidth;
29074                 }
29075
29076                 if(this.labellg > 0){
29077                     content[0].cls += ' col-lg-' + this.labellg;
29078                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29079                 }
29080
29081                 if(this.labelmd > 0){
29082                     content[0].cls += ' col-md-' + this.labelmd;
29083                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29084                 }
29085
29086                 if(this.labelsm > 0){
29087                     content[0].cls += ' col-sm-' + this.labelsm;
29088                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29089                 }
29090
29091                 if(this.labelxs > 0){
29092                     content[0].cls += ' col-xs-' + this.labelxs;
29093                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29094                 }
29095                 
29096             }
29097         }
29098         
29099         var cfg = {
29100             tag : 'div',
29101             cls : 'row clearfix',
29102             cn : content
29103         };
29104         
29105         return cfg;
29106         
29107     },
29108     
29109     initEvents : function()
29110     {
29111         this.managerEl = this.el.select('.roo-document-manager', true).first();
29112         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29113         
29114         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29115         this.selectorEl.hide();
29116         
29117         if(this.multiple){
29118             this.selectorEl.attr('multiple', 'multiple');
29119         }
29120         
29121         this.selectorEl.on('change', this.onFileSelected, this);
29122         
29123         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29124         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29125         
29126         this.uploader.on('click', this.onUploaderClick, this);
29127         
29128         this.renderProgressDialog();
29129         
29130         var _this = this;
29131         
29132         window.addEventListener("resize", function() { _this.refresh(); } );
29133         
29134         this.fireEvent('initial', this);
29135     },
29136     
29137     renderProgressDialog : function()
29138     {
29139         var _this = this;
29140         
29141         this.progressDialog = new Roo.bootstrap.Modal({
29142             cls : 'roo-document-manager-progress-dialog',
29143             allow_close : false,
29144             title : '',
29145             buttons : [
29146                 {
29147                     name  :'cancel',
29148                     weight : 'danger',
29149                     html : 'Cancel'
29150                 }
29151             ], 
29152             listeners : { 
29153                 btnclick : function() {
29154                     _this.uploadCancel();
29155                     this.hide();
29156                 }
29157             }
29158         });
29159          
29160         this.progressDialog.render(Roo.get(document.body));
29161          
29162         this.progress = new Roo.bootstrap.Progress({
29163             cls : 'roo-document-manager-progress',
29164             active : true,
29165             striped : true
29166         });
29167         
29168         this.progress.render(this.progressDialog.getChildContainer());
29169         
29170         this.progressBar = new Roo.bootstrap.ProgressBar({
29171             cls : 'roo-document-manager-progress-bar',
29172             aria_valuenow : 0,
29173             aria_valuemin : 0,
29174             aria_valuemax : 12,
29175             panel : 'success'
29176         });
29177         
29178         this.progressBar.render(this.progress.getChildContainer());
29179     },
29180     
29181     onUploaderClick : function(e)
29182     {
29183         e.preventDefault();
29184      
29185         if(this.fireEvent('beforeselectfile', this) != false){
29186             this.selectorEl.dom.click();
29187         }
29188         
29189     },
29190     
29191     onFileSelected : function(e)
29192     {
29193         e.preventDefault();
29194         
29195         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29196             return;
29197         }
29198         
29199         Roo.each(this.selectorEl.dom.files, function(file){
29200             if(this.fireEvent('inspect', this, file) != false){
29201                 this.files.push(file);
29202             }
29203         }, this);
29204         
29205         this.queue();
29206         
29207     },
29208     
29209     queue : function()
29210     {
29211         this.selectorEl.dom.value = '';
29212         
29213         if(!this.files || !this.files.length){
29214             return;
29215         }
29216         
29217         if(this.boxes > 0 && this.files.length > this.boxes){
29218             this.files = this.files.slice(0, this.boxes);
29219         }
29220         
29221         this.uploader.show();
29222         
29223         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29224             this.uploader.hide();
29225         }
29226         
29227         var _this = this;
29228         
29229         var files = [];
29230         
29231         var docs = [];
29232         
29233         Roo.each(this.files, function(file){
29234             
29235             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29236                 var f = this.renderPreview(file);
29237                 files.push(f);
29238                 return;
29239             }
29240             
29241             if(file.type.indexOf('image') != -1){
29242                 this.delegates.push(
29243                     (function(){
29244                         _this.process(file);
29245                     }).createDelegate(this)
29246                 );
29247         
29248                 return;
29249             }
29250             
29251             docs.push(
29252                 (function(){
29253                     _this.process(file);
29254                 }).createDelegate(this)
29255             );
29256             
29257         }, this);
29258         
29259         this.files = files;
29260         
29261         this.delegates = this.delegates.concat(docs);
29262         
29263         if(!this.delegates.length){
29264             this.refresh();
29265             return;
29266         }
29267         
29268         this.progressBar.aria_valuemax = this.delegates.length;
29269         
29270         this.arrange();
29271         
29272         return;
29273     },
29274     
29275     arrange : function()
29276     {
29277         if(!this.delegates.length){
29278             this.progressDialog.hide();
29279             this.refresh();
29280             return;
29281         }
29282         
29283         var delegate = this.delegates.shift();
29284         
29285         this.progressDialog.show();
29286         
29287         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29288         
29289         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29290         
29291         delegate();
29292     },
29293     
29294     refresh : function()
29295     {
29296         this.uploader.show();
29297         
29298         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29299             this.uploader.hide();
29300         }
29301         
29302         Roo.isTouch ? this.closable(false) : this.closable(true);
29303         
29304         this.fireEvent('refresh', this);
29305     },
29306     
29307     onRemove : function(e, el, o)
29308     {
29309         e.preventDefault();
29310         
29311         this.fireEvent('remove', this, o);
29312         
29313     },
29314     
29315     remove : function(o)
29316     {
29317         var files = [];
29318         
29319         Roo.each(this.files, function(file){
29320             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29321                 files.push(file);
29322                 return;
29323             }
29324
29325             o.target.remove();
29326
29327         }, this);
29328         
29329         this.files = files;
29330         
29331         this.refresh();
29332     },
29333     
29334     clear : function()
29335     {
29336         Roo.each(this.files, function(file){
29337             if(!file.target){
29338                 return;
29339             }
29340             
29341             file.target.remove();
29342
29343         }, this);
29344         
29345         this.files = [];
29346         
29347         this.refresh();
29348     },
29349     
29350     onClick : function(e, el, o)
29351     {
29352         e.preventDefault();
29353         
29354         this.fireEvent('click', this, o);
29355         
29356     },
29357     
29358     closable : function(closable)
29359     {
29360         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29361             
29362             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29363             
29364             if(closable){
29365                 el.show();
29366                 return;
29367             }
29368             
29369             el.hide();
29370             
29371         }, this);
29372     },
29373     
29374     xhrOnLoad : function(xhr)
29375     {
29376         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29377             el.remove();
29378         }, this);
29379         
29380         if (xhr.readyState !== 4) {
29381             this.arrange();
29382             this.fireEvent('exception', this, xhr);
29383             return;
29384         }
29385
29386         var response = Roo.decode(xhr.responseText);
29387         
29388         if(!response.success){
29389             this.arrange();
29390             this.fireEvent('exception', this, xhr);
29391             return;
29392         }
29393         
29394         var file = this.renderPreview(response.data);
29395         
29396         this.files.push(file);
29397         
29398         this.arrange();
29399         
29400         this.fireEvent('afterupload', this, xhr);
29401         
29402     },
29403     
29404     xhrOnError : function(xhr)
29405     {
29406         Roo.log('xhr on error');
29407         
29408         var response = Roo.decode(xhr.responseText);
29409           
29410         Roo.log(response);
29411         
29412         this.arrange();
29413     },
29414     
29415     process : function(file)
29416     {
29417         if(this.fireEvent('process', this, file) !== false){
29418             if(this.editable && file.type.indexOf('image') != -1){
29419                 this.fireEvent('edit', this, file);
29420                 return;
29421             }
29422
29423             this.uploadStart(file, false);
29424
29425             return;
29426         }
29427         
29428     },
29429     
29430     uploadStart : function(file, crop)
29431     {
29432         this.xhr = new XMLHttpRequest();
29433         
29434         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29435             this.arrange();
29436             return;
29437         }
29438         
29439         file.xhr = this.xhr;
29440             
29441         this.managerEl.createChild({
29442             tag : 'div',
29443             cls : 'roo-document-manager-loading',
29444             cn : [
29445                 {
29446                     tag : 'div',
29447                     tooltip : file.name,
29448                     cls : 'roo-document-manager-thumb',
29449                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29450                 }
29451             ]
29452
29453         });
29454
29455         this.xhr.open(this.method, this.url, true);
29456         
29457         var headers = {
29458             "Accept": "application/json",
29459             "Cache-Control": "no-cache",
29460             "X-Requested-With": "XMLHttpRequest"
29461         };
29462         
29463         for (var headerName in headers) {
29464             var headerValue = headers[headerName];
29465             if (headerValue) {
29466                 this.xhr.setRequestHeader(headerName, headerValue);
29467             }
29468         }
29469         
29470         var _this = this;
29471         
29472         this.xhr.onload = function()
29473         {
29474             _this.xhrOnLoad(_this.xhr);
29475         }
29476         
29477         this.xhr.onerror = function()
29478         {
29479             _this.xhrOnError(_this.xhr);
29480         }
29481         
29482         var formData = new FormData();
29483
29484         formData.append('returnHTML', 'NO');
29485         
29486         if(crop){
29487             formData.append('crop', crop);
29488         }
29489         
29490         formData.append(this.paramName, file, file.name);
29491         
29492         var options = {
29493             file : file, 
29494             manually : false
29495         };
29496         
29497         if(this.fireEvent('prepare', this, formData, options) != false){
29498             
29499             if(options.manually){
29500                 return;
29501             }
29502             
29503             this.xhr.send(formData);
29504             return;
29505         };
29506         
29507         this.uploadCancel();
29508     },
29509     
29510     uploadCancel : function()
29511     {
29512         if (this.xhr) {
29513             this.xhr.abort();
29514         }
29515         
29516         this.delegates = [];
29517         
29518         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29519             el.remove();
29520         }, this);
29521         
29522         this.arrange();
29523     },
29524     
29525     renderPreview : function(file)
29526     {
29527         if(typeof(file.target) != 'undefined' && file.target){
29528             return file;
29529         }
29530         
29531         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29532         
29533         var previewEl = this.managerEl.createChild({
29534             tag : 'div',
29535             cls : 'roo-document-manager-preview',
29536             cn : [
29537                 {
29538                     tag : 'div',
29539                     tooltip : file[this.toolTipName],
29540                     cls : 'roo-document-manager-thumb',
29541                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29542                 },
29543                 {
29544                     tag : 'button',
29545                     cls : 'close',
29546                     html : '<i class="fa fa-times-circle"></i>'
29547                 }
29548             ]
29549         });
29550
29551         var close = previewEl.select('button.close', true).first();
29552
29553         close.on('click', this.onRemove, this, file);
29554
29555         file.target = previewEl;
29556
29557         var image = previewEl.select('img', true).first();
29558         
29559         var _this = this;
29560         
29561         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29562         
29563         image.on('click', this.onClick, this, file);
29564         
29565         this.fireEvent('previewrendered', this, file);
29566         
29567         return file;
29568         
29569     },
29570     
29571     onPreviewLoad : function(file, image)
29572     {
29573         if(typeof(file.target) == 'undefined' || !file.target){
29574             return;
29575         }
29576         
29577         var width = image.dom.naturalWidth || image.dom.width;
29578         var height = image.dom.naturalHeight || image.dom.height;
29579         
29580         if(!this.previewResize) {
29581             return;
29582         }
29583         
29584         if(width > height){
29585             file.target.addClass('wide');
29586             return;
29587         }
29588         
29589         file.target.addClass('tall');
29590         return;
29591         
29592     },
29593     
29594     uploadFromSource : function(file, crop)
29595     {
29596         this.xhr = new XMLHttpRequest();
29597         
29598         this.managerEl.createChild({
29599             tag : 'div',
29600             cls : 'roo-document-manager-loading',
29601             cn : [
29602                 {
29603                     tag : 'div',
29604                     tooltip : file.name,
29605                     cls : 'roo-document-manager-thumb',
29606                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29607                 }
29608             ]
29609
29610         });
29611
29612         this.xhr.open(this.method, this.url, true);
29613         
29614         var headers = {
29615             "Accept": "application/json",
29616             "Cache-Control": "no-cache",
29617             "X-Requested-With": "XMLHttpRequest"
29618         };
29619         
29620         for (var headerName in headers) {
29621             var headerValue = headers[headerName];
29622             if (headerValue) {
29623                 this.xhr.setRequestHeader(headerName, headerValue);
29624             }
29625         }
29626         
29627         var _this = this;
29628         
29629         this.xhr.onload = function()
29630         {
29631             _this.xhrOnLoad(_this.xhr);
29632         }
29633         
29634         this.xhr.onerror = function()
29635         {
29636             _this.xhrOnError(_this.xhr);
29637         }
29638         
29639         var formData = new FormData();
29640
29641         formData.append('returnHTML', 'NO');
29642         
29643         formData.append('crop', crop);
29644         
29645         if(typeof(file.filename) != 'undefined'){
29646             formData.append('filename', file.filename);
29647         }
29648         
29649         if(typeof(file.mimetype) != 'undefined'){
29650             formData.append('mimetype', file.mimetype);
29651         }
29652         
29653         Roo.log(formData);
29654         
29655         if(this.fireEvent('prepare', this, formData) != false){
29656             this.xhr.send(formData);
29657         };
29658     }
29659 });
29660
29661 /*
29662 * Licence: LGPL
29663 */
29664
29665 /**
29666  * @class Roo.bootstrap.DocumentViewer
29667  * @extends Roo.bootstrap.Component
29668  * Bootstrap DocumentViewer class
29669  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29670  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29671  * 
29672  * @constructor
29673  * Create a new DocumentViewer
29674  * @param {Object} config The config object
29675  */
29676
29677 Roo.bootstrap.DocumentViewer = function(config){
29678     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29679     
29680     this.addEvents({
29681         /**
29682          * @event initial
29683          * Fire after initEvent
29684          * @param {Roo.bootstrap.DocumentViewer} this
29685          */
29686         "initial" : true,
29687         /**
29688          * @event click
29689          * Fire after click
29690          * @param {Roo.bootstrap.DocumentViewer} this
29691          */
29692         "click" : true,
29693         /**
29694          * @event download
29695          * Fire after download button
29696          * @param {Roo.bootstrap.DocumentViewer} this
29697          */
29698         "download" : true,
29699         /**
29700          * @event trash
29701          * Fire after trash button
29702          * @param {Roo.bootstrap.DocumentViewer} this
29703          */
29704         "trash" : true
29705         
29706     });
29707 };
29708
29709 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29710     
29711     showDownload : true,
29712     
29713     showTrash : true,
29714     
29715     getAutoCreate : function()
29716     {
29717         var cfg = {
29718             tag : 'div',
29719             cls : 'roo-document-viewer',
29720             cn : [
29721                 {
29722                     tag : 'div',
29723                     cls : 'roo-document-viewer-body',
29724                     cn : [
29725                         {
29726                             tag : 'div',
29727                             cls : 'roo-document-viewer-thumb',
29728                             cn : [
29729                                 {
29730                                     tag : 'img',
29731                                     cls : 'roo-document-viewer-image'
29732                                 }
29733                             ]
29734                         }
29735                     ]
29736                 },
29737                 {
29738                     tag : 'div',
29739                     cls : 'roo-document-viewer-footer',
29740                     cn : {
29741                         tag : 'div',
29742                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29743                         cn : [
29744                             {
29745                                 tag : 'div',
29746                                 cls : 'btn-group roo-document-viewer-download',
29747                                 cn : [
29748                                     {
29749                                         tag : 'button',
29750                                         cls : 'btn btn-default',
29751                                         html : '<i class="fa fa-download"></i>'
29752                                     }
29753                                 ]
29754                             },
29755                             {
29756                                 tag : 'div',
29757                                 cls : 'btn-group roo-document-viewer-trash',
29758                                 cn : [
29759                                     {
29760                                         tag : 'button',
29761                                         cls : 'btn btn-default',
29762                                         html : '<i class="fa fa-trash"></i>'
29763                                     }
29764                                 ]
29765                             }
29766                         ]
29767                     }
29768                 }
29769             ]
29770         };
29771         
29772         return cfg;
29773     },
29774     
29775     initEvents : function()
29776     {
29777         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29778         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29779         
29780         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29781         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29782         
29783         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29784         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29785         
29786         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29787         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29788         
29789         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29790         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29791         
29792         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29793         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29794         
29795         this.bodyEl.on('click', this.onClick, this);
29796         this.downloadBtn.on('click', this.onDownload, this);
29797         this.trashBtn.on('click', this.onTrash, this);
29798         
29799         this.downloadBtn.hide();
29800         this.trashBtn.hide();
29801         
29802         if(this.showDownload){
29803             this.downloadBtn.show();
29804         }
29805         
29806         if(this.showTrash){
29807             this.trashBtn.show();
29808         }
29809         
29810         if(!this.showDownload && !this.showTrash) {
29811             this.footerEl.hide();
29812         }
29813         
29814     },
29815     
29816     initial : function()
29817     {
29818         this.fireEvent('initial', this);
29819         
29820     },
29821     
29822     onClick : function(e)
29823     {
29824         e.preventDefault();
29825         
29826         this.fireEvent('click', this);
29827     },
29828     
29829     onDownload : function(e)
29830     {
29831         e.preventDefault();
29832         
29833         this.fireEvent('download', this);
29834     },
29835     
29836     onTrash : function(e)
29837     {
29838         e.preventDefault();
29839         
29840         this.fireEvent('trash', this);
29841     }
29842     
29843 });
29844 /*
29845  * - LGPL
29846  *
29847  * nav progress bar
29848  * 
29849  */
29850
29851 /**
29852  * @class Roo.bootstrap.NavProgressBar
29853  * @extends Roo.bootstrap.Component
29854  * Bootstrap NavProgressBar class
29855  * 
29856  * @constructor
29857  * Create a new nav progress bar
29858  * @param {Object} config The config object
29859  */
29860
29861 Roo.bootstrap.NavProgressBar = function(config){
29862     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29863
29864     this.bullets = this.bullets || [];
29865    
29866 //    Roo.bootstrap.NavProgressBar.register(this);
29867      this.addEvents({
29868         /**
29869              * @event changed
29870              * Fires when the active item changes
29871              * @param {Roo.bootstrap.NavProgressBar} this
29872              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29873              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29874          */
29875         'changed': true
29876      });
29877     
29878 };
29879
29880 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29881     
29882     bullets : [],
29883     barItems : [],
29884     
29885     getAutoCreate : function()
29886     {
29887         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29888         
29889         cfg = {
29890             tag : 'div',
29891             cls : 'roo-navigation-bar-group',
29892             cn : [
29893                 {
29894                     tag : 'div',
29895                     cls : 'roo-navigation-top-bar'
29896                 },
29897                 {
29898                     tag : 'div',
29899                     cls : 'roo-navigation-bullets-bar',
29900                     cn : [
29901                         {
29902                             tag : 'ul',
29903                             cls : 'roo-navigation-bar'
29904                         }
29905                     ]
29906                 },
29907                 
29908                 {
29909                     tag : 'div',
29910                     cls : 'roo-navigation-bottom-bar'
29911                 }
29912             ]
29913             
29914         };
29915         
29916         return cfg;
29917         
29918     },
29919     
29920     initEvents: function() 
29921     {
29922         
29923     },
29924     
29925     onRender : function(ct, position) 
29926     {
29927         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29928         
29929         if(this.bullets.length){
29930             Roo.each(this.bullets, function(b){
29931                this.addItem(b);
29932             }, this);
29933         }
29934         
29935         this.format();
29936         
29937     },
29938     
29939     addItem : function(cfg)
29940     {
29941         var item = new Roo.bootstrap.NavProgressItem(cfg);
29942         
29943         item.parentId = this.id;
29944         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29945         
29946         if(cfg.html){
29947             var top = new Roo.bootstrap.Element({
29948                 tag : 'div',
29949                 cls : 'roo-navigation-bar-text'
29950             });
29951             
29952             var bottom = new Roo.bootstrap.Element({
29953                 tag : 'div',
29954                 cls : 'roo-navigation-bar-text'
29955             });
29956             
29957             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29958             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29959             
29960             var topText = new Roo.bootstrap.Element({
29961                 tag : 'span',
29962                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29963             });
29964             
29965             var bottomText = new Roo.bootstrap.Element({
29966                 tag : 'span',
29967                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29968             });
29969             
29970             topText.onRender(top.el, null);
29971             bottomText.onRender(bottom.el, null);
29972             
29973             item.topEl = top;
29974             item.bottomEl = bottom;
29975         }
29976         
29977         this.barItems.push(item);
29978         
29979         return item;
29980     },
29981     
29982     getActive : function()
29983     {
29984         var active = false;
29985         
29986         Roo.each(this.barItems, function(v){
29987             
29988             if (!v.isActive()) {
29989                 return;
29990             }
29991             
29992             active = v;
29993             return false;
29994             
29995         });
29996         
29997         return active;
29998     },
29999     
30000     setActiveItem : function(item)
30001     {
30002         var prev = false;
30003         
30004         Roo.each(this.barItems, function(v){
30005             if (v.rid == item.rid) {
30006                 return ;
30007             }
30008             
30009             if (v.isActive()) {
30010                 v.setActive(false);
30011                 prev = v;
30012             }
30013         });
30014
30015         item.setActive(true);
30016         
30017         this.fireEvent('changed', this, item, prev);
30018     },
30019     
30020     getBarItem: function(rid)
30021     {
30022         var ret = false;
30023         
30024         Roo.each(this.barItems, function(e) {
30025             if (e.rid != rid) {
30026                 return;
30027             }
30028             
30029             ret =  e;
30030             return false;
30031         });
30032         
30033         return ret;
30034     },
30035     
30036     indexOfItem : function(item)
30037     {
30038         var index = false;
30039         
30040         Roo.each(this.barItems, function(v, i){
30041             
30042             if (v.rid != item.rid) {
30043                 return;
30044             }
30045             
30046             index = i;
30047             return false
30048         });
30049         
30050         return index;
30051     },
30052     
30053     setActiveNext : function()
30054     {
30055         var i = this.indexOfItem(this.getActive());
30056         
30057         if (i > this.barItems.length) {
30058             return;
30059         }
30060         
30061         this.setActiveItem(this.barItems[i+1]);
30062     },
30063     
30064     setActivePrev : function()
30065     {
30066         var i = this.indexOfItem(this.getActive());
30067         
30068         if (i  < 1) {
30069             return;
30070         }
30071         
30072         this.setActiveItem(this.barItems[i-1]);
30073     },
30074     
30075     format : function()
30076     {
30077         if(!this.barItems.length){
30078             return;
30079         }
30080      
30081         var width = 100 / this.barItems.length;
30082         
30083         Roo.each(this.barItems, function(i){
30084             i.el.setStyle('width', width + '%');
30085             i.topEl.el.setStyle('width', width + '%');
30086             i.bottomEl.el.setStyle('width', width + '%');
30087         }, this);
30088         
30089     }
30090     
30091 });
30092 /*
30093  * - LGPL
30094  *
30095  * Nav Progress Item
30096  * 
30097  */
30098
30099 /**
30100  * @class Roo.bootstrap.NavProgressItem
30101  * @extends Roo.bootstrap.Component
30102  * Bootstrap NavProgressItem class
30103  * @cfg {String} rid the reference id
30104  * @cfg {Boolean} active (true|false) Is item active default false
30105  * @cfg {Boolean} disabled (true|false) Is item active default false
30106  * @cfg {String} html
30107  * @cfg {String} position (top|bottom) text position default bottom
30108  * @cfg {String} icon show icon instead of number
30109  * 
30110  * @constructor
30111  * Create a new NavProgressItem
30112  * @param {Object} config The config object
30113  */
30114 Roo.bootstrap.NavProgressItem = function(config){
30115     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30116     this.addEvents({
30117         // raw events
30118         /**
30119          * @event click
30120          * The raw click event for the entire grid.
30121          * @param {Roo.bootstrap.NavProgressItem} this
30122          * @param {Roo.EventObject} e
30123          */
30124         "click" : true
30125     });
30126    
30127 };
30128
30129 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30130     
30131     rid : '',
30132     active : false,
30133     disabled : false,
30134     html : '',
30135     position : 'bottom',
30136     icon : false,
30137     
30138     getAutoCreate : function()
30139     {
30140         var iconCls = 'roo-navigation-bar-item-icon';
30141         
30142         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30143         
30144         var cfg = {
30145             tag: 'li',
30146             cls: 'roo-navigation-bar-item',
30147             cn : [
30148                 {
30149                     tag : 'i',
30150                     cls : iconCls
30151                 }
30152             ]
30153         };
30154         
30155         if(this.active){
30156             cfg.cls += ' active';
30157         }
30158         if(this.disabled){
30159             cfg.cls += ' disabled';
30160         }
30161         
30162         return cfg;
30163     },
30164     
30165     disable : function()
30166     {
30167         this.setDisabled(true);
30168     },
30169     
30170     enable : function()
30171     {
30172         this.setDisabled(false);
30173     },
30174     
30175     initEvents: function() 
30176     {
30177         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30178         
30179         this.iconEl.on('click', this.onClick, this);
30180     },
30181     
30182     onClick : function(e)
30183     {
30184         e.preventDefault();
30185         
30186         if(this.disabled){
30187             return;
30188         }
30189         
30190         if(this.fireEvent('click', this, e) === false){
30191             return;
30192         };
30193         
30194         this.parent().setActiveItem(this);
30195     },
30196     
30197     isActive: function () 
30198     {
30199         return this.active;
30200     },
30201     
30202     setActive : function(state)
30203     {
30204         if(this.active == state){
30205             return;
30206         }
30207         
30208         this.active = state;
30209         
30210         if (state) {
30211             this.el.addClass('active');
30212             return;
30213         }
30214         
30215         this.el.removeClass('active');
30216         
30217         return;
30218     },
30219     
30220     setDisabled : function(state)
30221     {
30222         if(this.disabled == state){
30223             return;
30224         }
30225         
30226         this.disabled = state;
30227         
30228         if (state) {
30229             this.el.addClass('disabled');
30230             return;
30231         }
30232         
30233         this.el.removeClass('disabled');
30234     },
30235     
30236     tooltipEl : function()
30237     {
30238         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30239     }
30240 });
30241  
30242
30243  /*
30244  * - LGPL
30245  *
30246  * FieldLabel
30247  * 
30248  */
30249
30250 /**
30251  * @class Roo.bootstrap.FieldLabel
30252  * @extends Roo.bootstrap.Component
30253  * Bootstrap FieldLabel class
30254  * @cfg {String} html contents of the element
30255  * @cfg {String} tag tag of the element default label
30256  * @cfg {String} cls class of the element
30257  * @cfg {String} target label target 
30258  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30259  * @cfg {String} invalidClass default "text-warning"
30260  * @cfg {String} validClass default "text-success"
30261  * @cfg {String} iconTooltip default "This field is required"
30262  * @cfg {String} indicatorpos (left|right) default left
30263  * 
30264  * @constructor
30265  * Create a new FieldLabel
30266  * @param {Object} config The config object
30267  */
30268
30269 Roo.bootstrap.FieldLabel = function(config){
30270     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30271     
30272     this.addEvents({
30273             /**
30274              * @event invalid
30275              * Fires after the field has been marked as invalid.
30276              * @param {Roo.form.FieldLabel} this
30277              * @param {String} msg The validation message
30278              */
30279             invalid : true,
30280             /**
30281              * @event valid
30282              * Fires after the field has been validated with no errors.
30283              * @param {Roo.form.FieldLabel} this
30284              */
30285             valid : true
30286         });
30287 };
30288
30289 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30290     
30291     tag: 'label',
30292     cls: '',
30293     html: '',
30294     target: '',
30295     allowBlank : true,
30296     invalidClass : 'has-warning',
30297     validClass : 'has-success',
30298     iconTooltip : 'This field is required',
30299     indicatorpos : 'left',
30300     
30301     getAutoCreate : function(){
30302         
30303         var cls = "";
30304         if (!this.allowBlank) {
30305             cls  = "visible";
30306         }
30307         
30308         var cfg = {
30309             tag : this.tag,
30310             cls : 'roo-bootstrap-field-label ' + this.cls,
30311             for : this.target,
30312             cn : [
30313                 {
30314                     tag : 'i',
30315                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30316                     tooltip : this.iconTooltip
30317                 },
30318                 {
30319                     tag : 'span',
30320                     html : this.html
30321                 }
30322             ] 
30323         };
30324         
30325         if(this.indicatorpos == 'right'){
30326             var cfg = {
30327                 tag : this.tag,
30328                 cls : 'roo-bootstrap-field-label ' + this.cls,
30329                 for : this.target,
30330                 cn : [
30331                     {
30332                         tag : 'span',
30333                         html : this.html
30334                     },
30335                     {
30336                         tag : 'i',
30337                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30338                         tooltip : this.iconTooltip
30339                     }
30340                 ] 
30341             };
30342         }
30343         
30344         return cfg;
30345     },
30346     
30347     initEvents: function() 
30348     {
30349         Roo.bootstrap.Element.superclass.initEvents.call(this);
30350         
30351         this.indicator = this.indicatorEl();
30352         
30353         if(this.indicator){
30354             this.indicator.removeClass('visible');
30355             this.indicator.addClass('invisible');
30356         }
30357         
30358         Roo.bootstrap.FieldLabel.register(this);
30359     },
30360     
30361     indicatorEl : function()
30362     {
30363         var indicator = this.el.select('i.roo-required-indicator',true).first();
30364         
30365         if(!indicator){
30366             return false;
30367         }
30368         
30369         return indicator;
30370         
30371     },
30372     
30373     /**
30374      * Mark this field as valid
30375      */
30376     markValid : function()
30377     {
30378         if(this.indicator){
30379             this.indicator.removeClass('visible');
30380             this.indicator.addClass('invisible');
30381         }
30382         
30383         this.el.removeClass(this.invalidClass);
30384         
30385         this.el.addClass(this.validClass);
30386         
30387         this.fireEvent('valid', this);
30388     },
30389     
30390     /**
30391      * Mark this field as invalid
30392      * @param {String} msg The validation message
30393      */
30394     markInvalid : function(msg)
30395     {
30396         if(this.indicator){
30397             this.indicator.removeClass('invisible');
30398             this.indicator.addClass('visible');
30399         }
30400         
30401         this.el.removeClass(this.validClass);
30402         
30403         this.el.addClass(this.invalidClass);
30404         
30405         this.fireEvent('invalid', this, msg);
30406     }
30407     
30408    
30409 });
30410
30411 Roo.apply(Roo.bootstrap.FieldLabel, {
30412     
30413     groups: {},
30414     
30415      /**
30416     * register a FieldLabel Group
30417     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30418     */
30419     register : function(label)
30420     {
30421         if(this.groups.hasOwnProperty(label.target)){
30422             return;
30423         }
30424      
30425         this.groups[label.target] = label;
30426         
30427     },
30428     /**
30429     * fetch a FieldLabel Group based on the target
30430     * @param {string} target
30431     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30432     */
30433     get: function(target) {
30434         if (typeof(this.groups[target]) == 'undefined') {
30435             return false;
30436         }
30437         
30438         return this.groups[target] ;
30439     }
30440 });
30441
30442  
30443
30444  /*
30445  * - LGPL
30446  *
30447  * page DateSplitField.
30448  * 
30449  */
30450
30451
30452 /**
30453  * @class Roo.bootstrap.DateSplitField
30454  * @extends Roo.bootstrap.Component
30455  * Bootstrap DateSplitField class
30456  * @cfg {string} fieldLabel - the label associated
30457  * @cfg {Number} labelWidth set the width of label (0-12)
30458  * @cfg {String} labelAlign (top|left)
30459  * @cfg {Boolean} dayAllowBlank (true|false) default false
30460  * @cfg {Boolean} monthAllowBlank (true|false) default false
30461  * @cfg {Boolean} yearAllowBlank (true|false) default false
30462  * @cfg {string} dayPlaceholder 
30463  * @cfg {string} monthPlaceholder
30464  * @cfg {string} yearPlaceholder
30465  * @cfg {string} dayFormat default 'd'
30466  * @cfg {string} monthFormat default 'm'
30467  * @cfg {string} yearFormat default 'Y'
30468  * @cfg {Number} labellg set the width of label (1-12)
30469  * @cfg {Number} labelmd set the width of label (1-12)
30470  * @cfg {Number} labelsm set the width of label (1-12)
30471  * @cfg {Number} labelxs set the width of label (1-12)
30472
30473  *     
30474  * @constructor
30475  * Create a new DateSplitField
30476  * @param {Object} config The config object
30477  */
30478
30479 Roo.bootstrap.DateSplitField = function(config){
30480     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30481     
30482     this.addEvents({
30483         // raw events
30484          /**
30485          * @event years
30486          * getting the data of years
30487          * @param {Roo.bootstrap.DateSplitField} this
30488          * @param {Object} years
30489          */
30490         "years" : true,
30491         /**
30492          * @event days
30493          * getting the data of days
30494          * @param {Roo.bootstrap.DateSplitField} this
30495          * @param {Object} days
30496          */
30497         "days" : true,
30498         /**
30499          * @event invalid
30500          * Fires after the field has been marked as invalid.
30501          * @param {Roo.form.Field} this
30502          * @param {String} msg The validation message
30503          */
30504         invalid : true,
30505        /**
30506          * @event valid
30507          * Fires after the field has been validated with no errors.
30508          * @param {Roo.form.Field} this
30509          */
30510         valid : true
30511     });
30512 };
30513
30514 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30515     
30516     fieldLabel : '',
30517     labelAlign : 'top',
30518     labelWidth : 3,
30519     dayAllowBlank : false,
30520     monthAllowBlank : false,
30521     yearAllowBlank : false,
30522     dayPlaceholder : '',
30523     monthPlaceholder : '',
30524     yearPlaceholder : '',
30525     dayFormat : 'd',
30526     monthFormat : 'm',
30527     yearFormat : 'Y',
30528     isFormField : true,
30529     labellg : 0,
30530     labelmd : 0,
30531     labelsm : 0,
30532     labelxs : 0,
30533     
30534     getAutoCreate : function()
30535     {
30536         var cfg = {
30537             tag : 'div',
30538             cls : 'row roo-date-split-field-group',
30539             cn : [
30540                 {
30541                     tag : 'input',
30542                     type : 'hidden',
30543                     cls : 'form-hidden-field roo-date-split-field-group-value',
30544                     name : this.name
30545                 }
30546             ]
30547         };
30548         
30549         var labelCls = 'col-md-12';
30550         var contentCls = 'col-md-4';
30551         
30552         if(this.fieldLabel){
30553             
30554             var label = {
30555                 tag : 'div',
30556                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30557                 cn : [
30558                     {
30559                         tag : 'label',
30560                         html : this.fieldLabel
30561                     }
30562                 ]
30563             };
30564             
30565             if(this.labelAlign == 'left'){
30566             
30567                 if(this.labelWidth > 12){
30568                     label.style = "width: " + this.labelWidth + 'px';
30569                 }
30570
30571                 if(this.labelWidth < 13 && this.labelmd == 0){
30572                     this.labelmd = this.labelWidth;
30573                 }
30574
30575                 if(this.labellg > 0){
30576                     labelCls = ' col-lg-' + this.labellg;
30577                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30578                 }
30579
30580                 if(this.labelmd > 0){
30581                     labelCls = ' col-md-' + this.labelmd;
30582                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30583                 }
30584
30585                 if(this.labelsm > 0){
30586                     labelCls = ' col-sm-' + this.labelsm;
30587                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30588                 }
30589
30590                 if(this.labelxs > 0){
30591                     labelCls = ' col-xs-' + this.labelxs;
30592                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30593                 }
30594             }
30595             
30596             label.cls += ' ' + labelCls;
30597             
30598             cfg.cn.push(label);
30599         }
30600         
30601         Roo.each(['day', 'month', 'year'], function(t){
30602             cfg.cn.push({
30603                 tag : 'div',
30604                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30605             });
30606         }, this);
30607         
30608         return cfg;
30609     },
30610     
30611     inputEl: function ()
30612     {
30613         return this.el.select('.roo-date-split-field-group-value', true).first();
30614     },
30615     
30616     onRender : function(ct, position) 
30617     {
30618         var _this = this;
30619         
30620         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30621         
30622         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30623         
30624         this.dayField = new Roo.bootstrap.ComboBox({
30625             allowBlank : this.dayAllowBlank,
30626             alwaysQuery : true,
30627             displayField : 'value',
30628             editable : false,
30629             fieldLabel : '',
30630             forceSelection : true,
30631             mode : 'local',
30632             placeholder : this.dayPlaceholder,
30633             selectOnFocus : true,
30634             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30635             triggerAction : 'all',
30636             typeAhead : true,
30637             valueField : 'value',
30638             store : new Roo.data.SimpleStore({
30639                 data : (function() {    
30640                     var days = [];
30641                     _this.fireEvent('days', _this, days);
30642                     return days;
30643                 })(),
30644                 fields : [ 'value' ]
30645             }),
30646             listeners : {
30647                 select : function (_self, record, index)
30648                 {
30649                     _this.setValue(_this.getValue());
30650                 }
30651             }
30652         });
30653
30654         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30655         
30656         this.monthField = new Roo.bootstrap.MonthField({
30657             after : '<i class=\"fa fa-calendar\"></i>',
30658             allowBlank : this.monthAllowBlank,
30659             placeholder : this.monthPlaceholder,
30660             readOnly : true,
30661             listeners : {
30662                 render : function (_self)
30663                 {
30664                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30665                         e.preventDefault();
30666                         _self.focus();
30667                     });
30668                 },
30669                 select : function (_self, oldvalue, newvalue)
30670                 {
30671                     _this.setValue(_this.getValue());
30672                 }
30673             }
30674         });
30675         
30676         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30677         
30678         this.yearField = new Roo.bootstrap.ComboBox({
30679             allowBlank : this.yearAllowBlank,
30680             alwaysQuery : true,
30681             displayField : 'value',
30682             editable : false,
30683             fieldLabel : '',
30684             forceSelection : true,
30685             mode : 'local',
30686             placeholder : this.yearPlaceholder,
30687             selectOnFocus : true,
30688             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30689             triggerAction : 'all',
30690             typeAhead : true,
30691             valueField : 'value',
30692             store : new Roo.data.SimpleStore({
30693                 data : (function() {
30694                     var years = [];
30695                     _this.fireEvent('years', _this, years);
30696                     return years;
30697                 })(),
30698                 fields : [ 'value' ]
30699             }),
30700             listeners : {
30701                 select : function (_self, record, index)
30702                 {
30703                     _this.setValue(_this.getValue());
30704                 }
30705             }
30706         });
30707
30708         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30709     },
30710     
30711     setValue : function(v, format)
30712     {
30713         this.inputEl.dom.value = v;
30714         
30715         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30716         
30717         var d = Date.parseDate(v, f);
30718         
30719         if(!d){
30720             this.validate();
30721             return;
30722         }
30723         
30724         this.setDay(d.format(this.dayFormat));
30725         this.setMonth(d.format(this.monthFormat));
30726         this.setYear(d.format(this.yearFormat));
30727         
30728         this.validate();
30729         
30730         return;
30731     },
30732     
30733     setDay : function(v)
30734     {
30735         this.dayField.setValue(v);
30736         this.inputEl.dom.value = this.getValue();
30737         this.validate();
30738         return;
30739     },
30740     
30741     setMonth : function(v)
30742     {
30743         this.monthField.setValue(v, true);
30744         this.inputEl.dom.value = this.getValue();
30745         this.validate();
30746         return;
30747     },
30748     
30749     setYear : function(v)
30750     {
30751         this.yearField.setValue(v);
30752         this.inputEl.dom.value = this.getValue();
30753         this.validate();
30754         return;
30755     },
30756     
30757     getDay : function()
30758     {
30759         return this.dayField.getValue();
30760     },
30761     
30762     getMonth : function()
30763     {
30764         return this.monthField.getValue();
30765     },
30766     
30767     getYear : function()
30768     {
30769         return this.yearField.getValue();
30770     },
30771     
30772     getValue : function()
30773     {
30774         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30775         
30776         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30777         
30778         return date;
30779     },
30780     
30781     reset : function()
30782     {
30783         this.setDay('');
30784         this.setMonth('');
30785         this.setYear('');
30786         this.inputEl.dom.value = '';
30787         this.validate();
30788         return;
30789     },
30790     
30791     validate : function()
30792     {
30793         var d = this.dayField.validate();
30794         var m = this.monthField.validate();
30795         var y = this.yearField.validate();
30796         
30797         var valid = true;
30798         
30799         if(
30800                 (!this.dayAllowBlank && !d) ||
30801                 (!this.monthAllowBlank && !m) ||
30802                 (!this.yearAllowBlank && !y)
30803         ){
30804             valid = false;
30805         }
30806         
30807         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30808             return valid;
30809         }
30810         
30811         if(valid){
30812             this.markValid();
30813             return valid;
30814         }
30815         
30816         this.markInvalid();
30817         
30818         return valid;
30819     },
30820     
30821     markValid : function()
30822     {
30823         
30824         var label = this.el.select('label', true).first();
30825         var icon = this.el.select('i.fa-star', true).first();
30826
30827         if(label && icon){
30828             icon.remove();
30829         }
30830         
30831         this.fireEvent('valid', this);
30832     },
30833     
30834      /**
30835      * Mark this field as invalid
30836      * @param {String} msg The validation message
30837      */
30838     markInvalid : function(msg)
30839     {
30840         
30841         var label = this.el.select('label', true).first();
30842         var icon = this.el.select('i.fa-star', true).first();
30843
30844         if(label && !icon){
30845             this.el.select('.roo-date-split-field-label', true).createChild({
30846                 tag : 'i',
30847                 cls : 'text-danger fa fa-lg fa-star',
30848                 tooltip : 'This field is required',
30849                 style : 'margin-right:5px;'
30850             }, label, true);
30851         }
30852         
30853         this.fireEvent('invalid', this, msg);
30854     },
30855     
30856     clearInvalid : function()
30857     {
30858         var label = this.el.select('label', true).first();
30859         var icon = this.el.select('i.fa-star', true).first();
30860
30861         if(label && icon){
30862             icon.remove();
30863         }
30864         
30865         this.fireEvent('valid', this);
30866     },
30867     
30868     getName: function()
30869     {
30870         return this.name;
30871     }
30872     
30873 });
30874
30875  /**
30876  *
30877  * This is based on 
30878  * http://masonry.desandro.com
30879  *
30880  * The idea is to render all the bricks based on vertical width...
30881  *
30882  * The original code extends 'outlayer' - we might need to use that....
30883  * 
30884  */
30885
30886
30887 /**
30888  * @class Roo.bootstrap.LayoutMasonry
30889  * @extends Roo.bootstrap.Component
30890  * Bootstrap Layout Masonry class
30891  * 
30892  * @constructor
30893  * Create a new Element
30894  * @param {Object} config The config object
30895  */
30896
30897 Roo.bootstrap.LayoutMasonry = function(config){
30898     
30899     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30900     
30901     this.bricks = [];
30902     
30903     Roo.bootstrap.LayoutMasonry.register(this);
30904     
30905     this.addEvents({
30906         // raw events
30907         /**
30908          * @event layout
30909          * Fire after layout the items
30910          * @param {Roo.bootstrap.LayoutMasonry} this
30911          * @param {Roo.EventObject} e
30912          */
30913         "layout" : true
30914     });
30915     
30916 };
30917
30918 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30919     
30920     /**
30921      * @cfg {Boolean} isLayoutInstant = no animation?
30922      */   
30923     isLayoutInstant : false, // needed?
30924    
30925     /**
30926      * @cfg {Number} boxWidth  width of the columns
30927      */   
30928     boxWidth : 450,
30929     
30930       /**
30931      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30932      */   
30933     boxHeight : 0,
30934     
30935     /**
30936      * @cfg {Number} padWidth padding below box..
30937      */   
30938     padWidth : 10, 
30939     
30940     /**
30941      * @cfg {Number} gutter gutter width..
30942      */   
30943     gutter : 10,
30944     
30945      /**
30946      * @cfg {Number} maxCols maximum number of columns
30947      */   
30948     
30949     maxCols: 0,
30950     
30951     /**
30952      * @cfg {Boolean} isAutoInitial defalut true
30953      */   
30954     isAutoInitial : true, 
30955     
30956     containerWidth: 0,
30957     
30958     /**
30959      * @cfg {Boolean} isHorizontal defalut false
30960      */   
30961     isHorizontal : false, 
30962
30963     currentSize : null,
30964     
30965     tag: 'div',
30966     
30967     cls: '',
30968     
30969     bricks: null, //CompositeElement
30970     
30971     cols : 1,
30972     
30973     _isLayoutInited : false,
30974     
30975 //    isAlternative : false, // only use for vertical layout...
30976     
30977     /**
30978      * @cfg {Number} alternativePadWidth padding below box..
30979      */   
30980     alternativePadWidth : 50,
30981     
30982     selectedBrick : [],
30983     
30984     getAutoCreate : function(){
30985         
30986         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30987         
30988         var cfg = {
30989             tag: this.tag,
30990             cls: 'blog-masonary-wrapper ' + this.cls,
30991             cn : {
30992                 cls : 'mas-boxes masonary'
30993             }
30994         };
30995         
30996         return cfg;
30997     },
30998     
30999     getChildContainer: function( )
31000     {
31001         if (this.boxesEl) {
31002             return this.boxesEl;
31003         }
31004         
31005         this.boxesEl = this.el.select('.mas-boxes').first();
31006         
31007         return this.boxesEl;
31008     },
31009     
31010     
31011     initEvents : function()
31012     {
31013         var _this = this;
31014         
31015         if(this.isAutoInitial){
31016             Roo.log('hook children rendered');
31017             this.on('childrenrendered', function() {
31018                 Roo.log('children rendered');
31019                 _this.initial();
31020             } ,this);
31021         }
31022     },
31023     
31024     initial : function()
31025     {
31026         this.selectedBrick = [];
31027         
31028         this.currentSize = this.el.getBox(true);
31029         
31030         Roo.EventManager.onWindowResize(this.resize, this); 
31031
31032         if(!this.isAutoInitial){
31033             this.layout();
31034             return;
31035         }
31036         
31037         this.layout();
31038         
31039         return;
31040         //this.layout.defer(500,this);
31041         
31042     },
31043     
31044     resize : function()
31045     {
31046         var cs = this.el.getBox(true);
31047         
31048         if (
31049                 this.currentSize.width == cs.width && 
31050                 this.currentSize.x == cs.x && 
31051                 this.currentSize.height == cs.height && 
31052                 this.currentSize.y == cs.y 
31053         ) {
31054             Roo.log("no change in with or X or Y");
31055             return;
31056         }
31057         
31058         this.currentSize = cs;
31059         
31060         this.layout();
31061         
31062     },
31063     
31064     layout : function()
31065     {   
31066         this._resetLayout();
31067         
31068         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31069         
31070         this.layoutItems( isInstant );
31071       
31072         this._isLayoutInited = true;
31073         
31074         this.fireEvent('layout', this);
31075         
31076     },
31077     
31078     _resetLayout : function()
31079     {
31080         if(this.isHorizontal){
31081             this.horizontalMeasureColumns();
31082             return;
31083         }
31084         
31085         this.verticalMeasureColumns();
31086         
31087     },
31088     
31089     verticalMeasureColumns : function()
31090     {
31091         this.getContainerWidth();
31092         
31093 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31094 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31095 //            return;
31096 //        }
31097         
31098         var boxWidth = this.boxWidth + this.padWidth;
31099         
31100         if(this.containerWidth < this.boxWidth){
31101             boxWidth = this.containerWidth
31102         }
31103         
31104         var containerWidth = this.containerWidth;
31105         
31106         var cols = Math.floor(containerWidth / boxWidth);
31107         
31108         this.cols = Math.max( cols, 1 );
31109         
31110         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31111         
31112         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31113         
31114         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31115         
31116         this.colWidth = boxWidth + avail - this.padWidth;
31117         
31118         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31119         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31120     },
31121     
31122     horizontalMeasureColumns : function()
31123     {
31124         this.getContainerWidth();
31125         
31126         var boxWidth = this.boxWidth;
31127         
31128         if(this.containerWidth < boxWidth){
31129             boxWidth = this.containerWidth;
31130         }
31131         
31132         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31133         
31134         this.el.setHeight(boxWidth);
31135         
31136     },
31137     
31138     getContainerWidth : function()
31139     {
31140         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31141     },
31142     
31143     layoutItems : function( isInstant )
31144     {
31145         Roo.log(this.bricks);
31146         
31147         var items = Roo.apply([], this.bricks);
31148         
31149         if(this.isHorizontal){
31150             this._horizontalLayoutItems( items , isInstant );
31151             return;
31152         }
31153         
31154 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31155 //            this._verticalAlternativeLayoutItems( items , isInstant );
31156 //            return;
31157 //        }
31158         
31159         this._verticalLayoutItems( items , isInstant );
31160         
31161     },
31162     
31163     _verticalLayoutItems : function ( items , isInstant)
31164     {
31165         if ( !items || !items.length ) {
31166             return;
31167         }
31168         
31169         var standard = [
31170             ['xs', 'xs', 'xs', 'tall'],
31171             ['xs', 'xs', 'tall'],
31172             ['xs', 'xs', 'sm'],
31173             ['xs', 'xs', 'xs'],
31174             ['xs', 'tall'],
31175             ['xs', 'sm'],
31176             ['xs', 'xs'],
31177             ['xs'],
31178             
31179             ['sm', 'xs', 'xs'],
31180             ['sm', 'xs'],
31181             ['sm'],
31182             
31183             ['tall', 'xs', 'xs', 'xs'],
31184             ['tall', 'xs', 'xs'],
31185             ['tall', 'xs'],
31186             ['tall']
31187             
31188         ];
31189         
31190         var queue = [];
31191         
31192         var boxes = [];
31193         
31194         var box = [];
31195         
31196         Roo.each(items, function(item, k){
31197             
31198             switch (item.size) {
31199                 // these layouts take up a full box,
31200                 case 'md' :
31201                 case 'md-left' :
31202                 case 'md-right' :
31203                 case 'wide' :
31204                     
31205                     if(box.length){
31206                         boxes.push(box);
31207                         box = [];
31208                     }
31209                     
31210                     boxes.push([item]);
31211                     
31212                     break;
31213                     
31214                 case 'xs' :
31215                 case 'sm' :
31216                 case 'tall' :
31217                     
31218                     box.push(item);
31219                     
31220                     break;
31221                 default :
31222                     break;
31223                     
31224             }
31225             
31226         }, this);
31227         
31228         if(box.length){
31229             boxes.push(box);
31230             box = [];
31231         }
31232         
31233         var filterPattern = function(box, length)
31234         {
31235             if(!box.length){
31236                 return;
31237             }
31238             
31239             var match = false;
31240             
31241             var pattern = box.slice(0, length);
31242             
31243             var format = [];
31244             
31245             Roo.each(pattern, function(i){
31246                 format.push(i.size);
31247             }, this);
31248             
31249             Roo.each(standard, function(s){
31250                 
31251                 if(String(s) != String(format)){
31252                     return;
31253                 }
31254                 
31255                 match = true;
31256                 return false;
31257                 
31258             }, this);
31259             
31260             if(!match && length == 1){
31261                 return;
31262             }
31263             
31264             if(!match){
31265                 filterPattern(box, length - 1);
31266                 return;
31267             }
31268                 
31269             queue.push(pattern);
31270
31271             box = box.slice(length, box.length);
31272
31273             filterPattern(box, 4);
31274
31275             return;
31276             
31277         }
31278         
31279         Roo.each(boxes, function(box, k){
31280             
31281             if(!box.length){
31282                 return;
31283             }
31284             
31285             if(box.length == 1){
31286                 queue.push(box);
31287                 return;
31288             }
31289             
31290             filterPattern(box, 4);
31291             
31292         }, this);
31293         
31294         this._processVerticalLayoutQueue( queue, isInstant );
31295         
31296     },
31297     
31298 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31299 //    {
31300 //        if ( !items || !items.length ) {
31301 //            return;
31302 //        }
31303 //
31304 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31305 //        
31306 //    },
31307     
31308     _horizontalLayoutItems : function ( items , isInstant)
31309     {
31310         if ( !items || !items.length || items.length < 3) {
31311             return;
31312         }
31313         
31314         items.reverse();
31315         
31316         var eItems = items.slice(0, 3);
31317         
31318         items = items.slice(3, items.length);
31319         
31320         var standard = [
31321             ['xs', 'xs', 'xs', 'wide'],
31322             ['xs', 'xs', 'wide'],
31323             ['xs', 'xs', 'sm'],
31324             ['xs', 'xs', 'xs'],
31325             ['xs', 'wide'],
31326             ['xs', 'sm'],
31327             ['xs', 'xs'],
31328             ['xs'],
31329             
31330             ['sm', 'xs', 'xs'],
31331             ['sm', 'xs'],
31332             ['sm'],
31333             
31334             ['wide', 'xs', 'xs', 'xs'],
31335             ['wide', 'xs', 'xs'],
31336             ['wide', 'xs'],
31337             ['wide'],
31338             
31339             ['wide-thin']
31340         ];
31341         
31342         var queue = [];
31343         
31344         var boxes = [];
31345         
31346         var box = [];
31347         
31348         Roo.each(items, function(item, k){
31349             
31350             switch (item.size) {
31351                 case 'md' :
31352                 case 'md-left' :
31353                 case 'md-right' :
31354                 case 'tall' :
31355                     
31356                     if(box.length){
31357                         boxes.push(box);
31358                         box = [];
31359                     }
31360                     
31361                     boxes.push([item]);
31362                     
31363                     break;
31364                     
31365                 case 'xs' :
31366                 case 'sm' :
31367                 case 'wide' :
31368                 case 'wide-thin' :
31369                     
31370                     box.push(item);
31371                     
31372                     break;
31373                 default :
31374                     break;
31375                     
31376             }
31377             
31378         }, this);
31379         
31380         if(box.length){
31381             boxes.push(box);
31382             box = [];
31383         }
31384         
31385         var filterPattern = function(box, length)
31386         {
31387             if(!box.length){
31388                 return;
31389             }
31390             
31391             var match = false;
31392             
31393             var pattern = box.slice(0, length);
31394             
31395             var format = [];
31396             
31397             Roo.each(pattern, function(i){
31398                 format.push(i.size);
31399             }, this);
31400             
31401             Roo.each(standard, function(s){
31402                 
31403                 if(String(s) != String(format)){
31404                     return;
31405                 }
31406                 
31407                 match = true;
31408                 return false;
31409                 
31410             }, this);
31411             
31412             if(!match && length == 1){
31413                 return;
31414             }
31415             
31416             if(!match){
31417                 filterPattern(box, length - 1);
31418                 return;
31419             }
31420                 
31421             queue.push(pattern);
31422
31423             box = box.slice(length, box.length);
31424
31425             filterPattern(box, 4);
31426
31427             return;
31428             
31429         }
31430         
31431         Roo.each(boxes, function(box, k){
31432             
31433             if(!box.length){
31434                 return;
31435             }
31436             
31437             if(box.length == 1){
31438                 queue.push(box);
31439                 return;
31440             }
31441             
31442             filterPattern(box, 4);
31443             
31444         }, this);
31445         
31446         
31447         var prune = [];
31448         
31449         var pos = this.el.getBox(true);
31450         
31451         var minX = pos.x;
31452         
31453         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31454         
31455         var hit_end = false;
31456         
31457         Roo.each(queue, function(box){
31458             
31459             if(hit_end){
31460                 
31461                 Roo.each(box, function(b){
31462                 
31463                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31464                     b.el.hide();
31465
31466                 }, this);
31467
31468                 return;
31469             }
31470             
31471             var mx = 0;
31472             
31473             Roo.each(box, function(b){
31474                 
31475                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31476                 b.el.show();
31477
31478                 mx = Math.max(mx, b.x);
31479                 
31480             }, this);
31481             
31482             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31483             
31484             if(maxX < minX){
31485                 
31486                 Roo.each(box, function(b){
31487                 
31488                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31489                     b.el.hide();
31490                     
31491                 }, this);
31492                 
31493                 hit_end = true;
31494                 
31495                 return;
31496             }
31497             
31498             prune.push(box);
31499             
31500         }, this);
31501         
31502         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31503     },
31504     
31505     /** Sets position of item in DOM
31506     * @param {Element} item
31507     * @param {Number} x - horizontal position
31508     * @param {Number} y - vertical position
31509     * @param {Boolean} isInstant - disables transitions
31510     */
31511     _processVerticalLayoutQueue : function( queue, isInstant )
31512     {
31513         var pos = this.el.getBox(true);
31514         var x = pos.x;
31515         var y = pos.y;
31516         var maxY = [];
31517         
31518         for (var i = 0; i < this.cols; i++){
31519             maxY[i] = pos.y;
31520         }
31521         
31522         Roo.each(queue, function(box, k){
31523             
31524             var col = k % this.cols;
31525             
31526             Roo.each(box, function(b,kk){
31527                 
31528                 b.el.position('absolute');
31529                 
31530                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31531                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31532                 
31533                 if(b.size == 'md-left' || b.size == 'md-right'){
31534                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31535                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31536                 }
31537                 
31538                 b.el.setWidth(width);
31539                 b.el.setHeight(height);
31540                 // iframe?
31541                 b.el.select('iframe',true).setSize(width,height);
31542                 
31543             }, this);
31544             
31545             for (var i = 0; i < this.cols; i++){
31546                 
31547                 if(maxY[i] < maxY[col]){
31548                     col = i;
31549                     continue;
31550                 }
31551                 
31552                 col = Math.min(col, i);
31553                 
31554             }
31555             
31556             x = pos.x + col * (this.colWidth + this.padWidth);
31557             
31558             y = maxY[col];
31559             
31560             var positions = [];
31561             
31562             switch (box.length){
31563                 case 1 :
31564                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31565                     break;
31566                 case 2 :
31567                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31568                     break;
31569                 case 3 :
31570                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31571                     break;
31572                 case 4 :
31573                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31574                     break;
31575                 default :
31576                     break;
31577             }
31578             
31579             Roo.each(box, function(b,kk){
31580                 
31581                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31582                 
31583                 var sz = b.el.getSize();
31584                 
31585                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31586                 
31587             }, this);
31588             
31589         }, this);
31590         
31591         var mY = 0;
31592         
31593         for (var i = 0; i < this.cols; i++){
31594             mY = Math.max(mY, maxY[i]);
31595         }
31596         
31597         this.el.setHeight(mY - pos.y);
31598         
31599     },
31600     
31601 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31602 //    {
31603 //        var pos = this.el.getBox(true);
31604 //        var x = pos.x;
31605 //        var y = pos.y;
31606 //        var maxX = pos.right;
31607 //        
31608 //        var maxHeight = 0;
31609 //        
31610 //        Roo.each(items, function(item, k){
31611 //            
31612 //            var c = k % 2;
31613 //            
31614 //            item.el.position('absolute');
31615 //                
31616 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31617 //
31618 //            item.el.setWidth(width);
31619 //
31620 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31621 //
31622 //            item.el.setHeight(height);
31623 //            
31624 //            if(c == 0){
31625 //                item.el.setXY([x, y], isInstant ? false : true);
31626 //            } else {
31627 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31628 //            }
31629 //            
31630 //            y = y + height + this.alternativePadWidth;
31631 //            
31632 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31633 //            
31634 //        }, this);
31635 //        
31636 //        this.el.setHeight(maxHeight);
31637 //        
31638 //    },
31639     
31640     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31641     {
31642         var pos = this.el.getBox(true);
31643         
31644         var minX = pos.x;
31645         var minY = pos.y;
31646         
31647         var maxX = pos.right;
31648         
31649         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31650         
31651         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31652         
31653         Roo.each(queue, function(box, k){
31654             
31655             Roo.each(box, function(b, kk){
31656                 
31657                 b.el.position('absolute');
31658                 
31659                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31660                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31661                 
31662                 if(b.size == 'md-left' || b.size == 'md-right'){
31663                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31664                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31665                 }
31666                 
31667                 b.el.setWidth(width);
31668                 b.el.setHeight(height);
31669                 
31670             }, this);
31671             
31672             if(!box.length){
31673                 return;
31674             }
31675             
31676             var positions = [];
31677             
31678             switch (box.length){
31679                 case 1 :
31680                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31681                     break;
31682                 case 2 :
31683                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31684                     break;
31685                 case 3 :
31686                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31687                     break;
31688                 case 4 :
31689                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31690                     break;
31691                 default :
31692                     break;
31693             }
31694             
31695             Roo.each(box, function(b,kk){
31696                 
31697                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31698                 
31699                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31700                 
31701             }, this);
31702             
31703         }, this);
31704         
31705     },
31706     
31707     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31708     {
31709         Roo.each(eItems, function(b,k){
31710             
31711             b.size = (k == 0) ? 'sm' : 'xs';
31712             b.x = (k == 0) ? 2 : 1;
31713             b.y = (k == 0) ? 2 : 1;
31714             
31715             b.el.position('absolute');
31716             
31717             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31718                 
31719             b.el.setWidth(width);
31720             
31721             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31722             
31723             b.el.setHeight(height);
31724             
31725         }, this);
31726
31727         var positions = [];
31728         
31729         positions.push({
31730             x : maxX - this.unitWidth * 2 - this.gutter,
31731             y : minY
31732         });
31733         
31734         positions.push({
31735             x : maxX - this.unitWidth,
31736             y : minY + (this.unitWidth + this.gutter) * 2
31737         });
31738         
31739         positions.push({
31740             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31741             y : minY
31742         });
31743         
31744         Roo.each(eItems, function(b,k){
31745             
31746             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31747
31748         }, this);
31749         
31750     },
31751     
31752     getVerticalOneBoxColPositions : function(x, y, box)
31753     {
31754         var pos = [];
31755         
31756         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31757         
31758         if(box[0].size == 'md-left'){
31759             rand = 0;
31760         }
31761         
31762         if(box[0].size == 'md-right'){
31763             rand = 1;
31764         }
31765         
31766         pos.push({
31767             x : x + (this.unitWidth + this.gutter) * rand,
31768             y : y
31769         });
31770         
31771         return pos;
31772     },
31773     
31774     getVerticalTwoBoxColPositions : function(x, y, box)
31775     {
31776         var pos = [];
31777         
31778         if(box[0].size == 'xs'){
31779             
31780             pos.push({
31781                 x : x,
31782                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31783             });
31784
31785             pos.push({
31786                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31787                 y : y
31788             });
31789             
31790             return pos;
31791             
31792         }
31793         
31794         pos.push({
31795             x : x,
31796             y : y
31797         });
31798
31799         pos.push({
31800             x : x + (this.unitWidth + this.gutter) * 2,
31801             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31802         });
31803         
31804         return pos;
31805         
31806     },
31807     
31808     getVerticalThreeBoxColPositions : function(x, y, box)
31809     {
31810         var pos = [];
31811         
31812         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31813             
31814             pos.push({
31815                 x : x,
31816                 y : y
31817             });
31818
31819             pos.push({
31820                 x : x + (this.unitWidth + this.gutter) * 1,
31821                 y : y
31822             });
31823             
31824             pos.push({
31825                 x : x + (this.unitWidth + this.gutter) * 2,
31826                 y : y
31827             });
31828             
31829             return pos;
31830             
31831         }
31832         
31833         if(box[0].size == 'xs' && box[1].size == 'xs'){
31834             
31835             pos.push({
31836                 x : x,
31837                 y : y
31838             });
31839
31840             pos.push({
31841                 x : x,
31842                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31843             });
31844             
31845             pos.push({
31846                 x : x + (this.unitWidth + this.gutter) * 1,
31847                 y : y
31848             });
31849             
31850             return pos;
31851             
31852         }
31853         
31854         pos.push({
31855             x : x,
31856             y : y
31857         });
31858
31859         pos.push({
31860             x : x + (this.unitWidth + this.gutter) * 2,
31861             y : y
31862         });
31863
31864         pos.push({
31865             x : x + (this.unitWidth + this.gutter) * 2,
31866             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31867         });
31868             
31869         return pos;
31870         
31871     },
31872     
31873     getVerticalFourBoxColPositions : function(x, y, box)
31874     {
31875         var pos = [];
31876         
31877         if(box[0].size == 'xs'){
31878             
31879             pos.push({
31880                 x : x,
31881                 y : y
31882             });
31883
31884             pos.push({
31885                 x : x,
31886                 y : y + (this.unitHeight + this.gutter) * 1
31887             });
31888             
31889             pos.push({
31890                 x : x,
31891                 y : y + (this.unitHeight + this.gutter) * 2
31892             });
31893             
31894             pos.push({
31895                 x : x + (this.unitWidth + this.gutter) * 1,
31896                 y : y
31897             });
31898             
31899             return pos;
31900             
31901         }
31902         
31903         pos.push({
31904             x : x,
31905             y : y
31906         });
31907
31908         pos.push({
31909             x : x + (this.unitWidth + this.gutter) * 2,
31910             y : y
31911         });
31912
31913         pos.push({
31914             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31915             y : y + (this.unitHeight + this.gutter) * 1
31916         });
31917
31918         pos.push({
31919             x : x + (this.unitWidth + this.gutter) * 2,
31920             y : y + (this.unitWidth + this.gutter) * 2
31921         });
31922
31923         return pos;
31924         
31925     },
31926     
31927     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31928     {
31929         var pos = [];
31930         
31931         if(box[0].size == 'md-left'){
31932             pos.push({
31933                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31934                 y : minY
31935             });
31936             
31937             return pos;
31938         }
31939         
31940         if(box[0].size == 'md-right'){
31941             pos.push({
31942                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31943                 y : minY + (this.unitWidth + this.gutter) * 1
31944             });
31945             
31946             return pos;
31947         }
31948         
31949         var rand = Math.floor(Math.random() * (4 - box[0].y));
31950         
31951         pos.push({
31952             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31953             y : minY + (this.unitWidth + this.gutter) * rand
31954         });
31955         
31956         return pos;
31957         
31958     },
31959     
31960     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31961     {
31962         var pos = [];
31963         
31964         if(box[0].size == 'xs'){
31965             
31966             pos.push({
31967                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31968                 y : minY
31969             });
31970
31971             pos.push({
31972                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31973                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31974             });
31975             
31976             return pos;
31977             
31978         }
31979         
31980         pos.push({
31981             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31982             y : minY
31983         });
31984
31985         pos.push({
31986             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31987             y : minY + (this.unitWidth + this.gutter) * 2
31988         });
31989         
31990         return pos;
31991         
31992     },
31993     
31994     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31995     {
31996         var pos = [];
31997         
31998         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31999             
32000             pos.push({
32001                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32002                 y : minY
32003             });
32004
32005             pos.push({
32006                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32007                 y : minY + (this.unitWidth + this.gutter) * 1
32008             });
32009             
32010             pos.push({
32011                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32012                 y : minY + (this.unitWidth + this.gutter) * 2
32013             });
32014             
32015             return pos;
32016             
32017         }
32018         
32019         if(box[0].size == 'xs' && box[1].size == 'xs'){
32020             
32021             pos.push({
32022                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32023                 y : minY
32024             });
32025
32026             pos.push({
32027                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32028                 y : minY
32029             });
32030             
32031             pos.push({
32032                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32033                 y : minY + (this.unitWidth + this.gutter) * 1
32034             });
32035             
32036             return pos;
32037             
32038         }
32039         
32040         pos.push({
32041             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32042             y : minY
32043         });
32044
32045         pos.push({
32046             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32047             y : minY + (this.unitWidth + this.gutter) * 2
32048         });
32049
32050         pos.push({
32051             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32052             y : minY + (this.unitWidth + this.gutter) * 2
32053         });
32054             
32055         return pos;
32056         
32057     },
32058     
32059     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32060     {
32061         var pos = [];
32062         
32063         if(box[0].size == 'xs'){
32064             
32065             pos.push({
32066                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32067                 y : minY
32068             });
32069
32070             pos.push({
32071                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32072                 y : minY
32073             });
32074             
32075             pos.push({
32076                 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),
32077                 y : minY
32078             });
32079             
32080             pos.push({
32081                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32082                 y : minY + (this.unitWidth + this.gutter) * 1
32083             });
32084             
32085             return pos;
32086             
32087         }
32088         
32089         pos.push({
32090             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32091             y : minY
32092         });
32093         
32094         pos.push({
32095             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32096             y : minY + (this.unitWidth + this.gutter) * 2
32097         });
32098         
32099         pos.push({
32100             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32101             y : minY + (this.unitWidth + this.gutter) * 2
32102         });
32103         
32104         pos.push({
32105             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),
32106             y : minY + (this.unitWidth + this.gutter) * 2
32107         });
32108
32109         return pos;
32110         
32111     },
32112     
32113     /**
32114     * remove a Masonry Brick
32115     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32116     */
32117     removeBrick : function(brick_id)
32118     {
32119         if (!brick_id) {
32120             return;
32121         }
32122         
32123         for (var i = 0; i<this.bricks.length; i++) {
32124             if (this.bricks[i].id == brick_id) {
32125                 this.bricks.splice(i,1);
32126                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32127                 this.initial();
32128             }
32129         }
32130     },
32131     
32132     /**
32133     * adds a Masonry Brick
32134     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32135     */
32136     addBrick : function(cfg)
32137     {
32138         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32139         //this.register(cn);
32140         cn.parentId = this.id;
32141         cn.render(this.el);
32142         return cn;
32143     },
32144     
32145     /**
32146     * register a Masonry Brick
32147     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32148     */
32149     
32150     register : function(brick)
32151     {
32152         this.bricks.push(brick);
32153         brick.masonryId = this.id;
32154     },
32155     
32156     /**
32157     * clear all the Masonry Brick
32158     */
32159     clearAll : function()
32160     {
32161         this.bricks = [];
32162         //this.getChildContainer().dom.innerHTML = "";
32163         this.el.dom.innerHTML = '';
32164     },
32165     
32166     getSelected : function()
32167     {
32168         if (!this.selectedBrick) {
32169             return false;
32170         }
32171         
32172         return this.selectedBrick;
32173     }
32174 });
32175
32176 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32177     
32178     groups: {},
32179      /**
32180     * register a Masonry Layout
32181     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32182     */
32183     
32184     register : function(layout)
32185     {
32186         this.groups[layout.id] = layout;
32187     },
32188     /**
32189     * fetch a  Masonry Layout based on the masonry layout ID
32190     * @param {string} the masonry layout to add
32191     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32192     */
32193     
32194     get: function(layout_id) {
32195         if (typeof(this.groups[layout_id]) == 'undefined') {
32196             return false;
32197         }
32198         return this.groups[layout_id] ;
32199     }
32200     
32201     
32202     
32203 });
32204
32205  
32206
32207  /**
32208  *
32209  * This is based on 
32210  * http://masonry.desandro.com
32211  *
32212  * The idea is to render all the bricks based on vertical width...
32213  *
32214  * The original code extends 'outlayer' - we might need to use that....
32215  * 
32216  */
32217
32218
32219 /**
32220  * @class Roo.bootstrap.LayoutMasonryAuto
32221  * @extends Roo.bootstrap.Component
32222  * Bootstrap Layout Masonry class
32223  * 
32224  * @constructor
32225  * Create a new Element
32226  * @param {Object} config The config object
32227  */
32228
32229 Roo.bootstrap.LayoutMasonryAuto = function(config){
32230     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32231 };
32232
32233 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32234     
32235       /**
32236      * @cfg {Boolean} isFitWidth  - resize the width..
32237      */   
32238     isFitWidth : false,  // options..
32239     /**
32240      * @cfg {Boolean} isOriginLeft = left align?
32241      */   
32242     isOriginLeft : true,
32243     /**
32244      * @cfg {Boolean} isOriginTop = top align?
32245      */   
32246     isOriginTop : false,
32247     /**
32248      * @cfg {Boolean} isLayoutInstant = no animation?
32249      */   
32250     isLayoutInstant : false, // needed?
32251     /**
32252      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32253      */   
32254     isResizingContainer : true,
32255     /**
32256      * @cfg {Number} columnWidth  width of the columns 
32257      */   
32258     
32259     columnWidth : 0,
32260     
32261     /**
32262      * @cfg {Number} maxCols maximum number of columns
32263      */   
32264     
32265     maxCols: 0,
32266     /**
32267      * @cfg {Number} padHeight padding below box..
32268      */   
32269     
32270     padHeight : 10, 
32271     
32272     /**
32273      * @cfg {Boolean} isAutoInitial defalut true
32274      */   
32275     
32276     isAutoInitial : true, 
32277     
32278     // private?
32279     gutter : 0,
32280     
32281     containerWidth: 0,
32282     initialColumnWidth : 0,
32283     currentSize : null,
32284     
32285     colYs : null, // array.
32286     maxY : 0,
32287     padWidth: 10,
32288     
32289     
32290     tag: 'div',
32291     cls: '',
32292     bricks: null, //CompositeElement
32293     cols : 0, // array?
32294     // element : null, // wrapped now this.el
32295     _isLayoutInited : null, 
32296     
32297     
32298     getAutoCreate : function(){
32299         
32300         var cfg = {
32301             tag: this.tag,
32302             cls: 'blog-masonary-wrapper ' + this.cls,
32303             cn : {
32304                 cls : 'mas-boxes masonary'
32305             }
32306         };
32307         
32308         return cfg;
32309     },
32310     
32311     getChildContainer: function( )
32312     {
32313         if (this.boxesEl) {
32314             return this.boxesEl;
32315         }
32316         
32317         this.boxesEl = this.el.select('.mas-boxes').first();
32318         
32319         return this.boxesEl;
32320     },
32321     
32322     
32323     initEvents : function()
32324     {
32325         var _this = this;
32326         
32327         if(this.isAutoInitial){
32328             Roo.log('hook children rendered');
32329             this.on('childrenrendered', function() {
32330                 Roo.log('children rendered');
32331                 _this.initial();
32332             } ,this);
32333         }
32334         
32335     },
32336     
32337     initial : function()
32338     {
32339         this.reloadItems();
32340
32341         this.currentSize = this.el.getBox(true);
32342
32343         /// was window resize... - let's see if this works..
32344         Roo.EventManager.onWindowResize(this.resize, this); 
32345
32346         if(!this.isAutoInitial){
32347             this.layout();
32348             return;
32349         }
32350         
32351         this.layout.defer(500,this);
32352     },
32353     
32354     reloadItems: function()
32355     {
32356         this.bricks = this.el.select('.masonry-brick', true);
32357         
32358         this.bricks.each(function(b) {
32359             //Roo.log(b.getSize());
32360             if (!b.attr('originalwidth')) {
32361                 b.attr('originalwidth',  b.getSize().width);
32362             }
32363             
32364         });
32365         
32366         Roo.log(this.bricks.elements.length);
32367     },
32368     
32369     resize : function()
32370     {
32371         Roo.log('resize');
32372         var cs = this.el.getBox(true);
32373         
32374         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32375             Roo.log("no change in with or X");
32376             return;
32377         }
32378         this.currentSize = cs;
32379         this.layout();
32380     },
32381     
32382     layout : function()
32383     {
32384          Roo.log('layout');
32385         this._resetLayout();
32386         //this._manageStamps();
32387       
32388         // don't animate first layout
32389         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32390         this.layoutItems( isInstant );
32391       
32392         // flag for initalized
32393         this._isLayoutInited = true;
32394     },
32395     
32396     layoutItems : function( isInstant )
32397     {
32398         //var items = this._getItemsForLayout( this.items );
32399         // original code supports filtering layout items.. we just ignore it..
32400         
32401         this._layoutItems( this.bricks , isInstant );
32402       
32403         this._postLayout();
32404     },
32405     _layoutItems : function ( items , isInstant)
32406     {
32407        //this.fireEvent( 'layout', this, items );
32408     
32409
32410         if ( !items || !items.elements.length ) {
32411           // no items, emit event with empty array
32412             return;
32413         }
32414
32415         var queue = [];
32416         items.each(function(item) {
32417             Roo.log("layout item");
32418             Roo.log(item);
32419             // get x/y object from method
32420             var position = this._getItemLayoutPosition( item );
32421             // enqueue
32422             position.item = item;
32423             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32424             queue.push( position );
32425         }, this);
32426       
32427         this._processLayoutQueue( queue );
32428     },
32429     /** Sets position of item in DOM
32430     * @param {Element} item
32431     * @param {Number} x - horizontal position
32432     * @param {Number} y - vertical position
32433     * @param {Boolean} isInstant - disables transitions
32434     */
32435     _processLayoutQueue : function( queue )
32436     {
32437         for ( var i=0, len = queue.length; i < len; i++ ) {
32438             var obj = queue[i];
32439             obj.item.position('absolute');
32440             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32441         }
32442     },
32443       
32444     
32445     /**
32446     * Any logic you want to do after each layout,
32447     * i.e. size the container
32448     */
32449     _postLayout : function()
32450     {
32451         this.resizeContainer();
32452     },
32453     
32454     resizeContainer : function()
32455     {
32456         if ( !this.isResizingContainer ) {
32457             return;
32458         }
32459         var size = this._getContainerSize();
32460         if ( size ) {
32461             this.el.setSize(size.width,size.height);
32462             this.boxesEl.setSize(size.width,size.height);
32463         }
32464     },
32465     
32466     
32467     
32468     _resetLayout : function()
32469     {
32470         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32471         this.colWidth = this.el.getWidth();
32472         //this.gutter = this.el.getWidth(); 
32473         
32474         this.measureColumns();
32475
32476         // reset column Y
32477         var i = this.cols;
32478         this.colYs = [];
32479         while (i--) {
32480             this.colYs.push( 0 );
32481         }
32482     
32483         this.maxY = 0;
32484     },
32485
32486     measureColumns : function()
32487     {
32488         this.getContainerWidth();
32489       // if columnWidth is 0, default to outerWidth of first item
32490         if ( !this.columnWidth ) {
32491             var firstItem = this.bricks.first();
32492             Roo.log(firstItem);
32493             this.columnWidth  = this.containerWidth;
32494             if (firstItem && firstItem.attr('originalwidth') ) {
32495                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32496             }
32497             // columnWidth fall back to item of first element
32498             Roo.log("set column width?");
32499                         this.initialColumnWidth = this.columnWidth  ;
32500
32501             // if first elem has no width, default to size of container
32502             
32503         }
32504         
32505         
32506         if (this.initialColumnWidth) {
32507             this.columnWidth = this.initialColumnWidth;
32508         }
32509         
32510         
32511             
32512         // column width is fixed at the top - however if container width get's smaller we should
32513         // reduce it...
32514         
32515         // this bit calcs how man columns..
32516             
32517         var columnWidth = this.columnWidth += this.gutter;
32518       
32519         // calculate columns
32520         var containerWidth = this.containerWidth + this.gutter;
32521         
32522         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32523         // fix rounding errors, typically with gutters
32524         var excess = columnWidth - containerWidth % columnWidth;
32525         
32526         
32527         // if overshoot is less than a pixel, round up, otherwise floor it
32528         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32529         cols = Math[ mathMethod ]( cols );
32530         this.cols = Math.max( cols, 1 );
32531         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32532         
32533          // padding positioning..
32534         var totalColWidth = this.cols * this.columnWidth;
32535         var padavail = this.containerWidth - totalColWidth;
32536         // so for 2 columns - we need 3 'pads'
32537         
32538         var padNeeded = (1+this.cols) * this.padWidth;
32539         
32540         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32541         
32542         this.columnWidth += padExtra
32543         //this.padWidth = Math.floor(padavail /  ( this.cols));
32544         
32545         // adjust colum width so that padding is fixed??
32546         
32547         // we have 3 columns ... total = width * 3
32548         // we have X left over... that should be used by 
32549         
32550         //if (this.expandC) {
32551             
32552         //}
32553         
32554         
32555         
32556     },
32557     
32558     getContainerWidth : function()
32559     {
32560        /* // container is parent if fit width
32561         var container = this.isFitWidth ? this.element.parentNode : this.element;
32562         // check that this.size and size are there
32563         // IE8 triggers resize on body size change, so they might not be
32564         
32565         var size = getSize( container );  //FIXME
32566         this.containerWidth = size && size.innerWidth; //FIXME
32567         */
32568          
32569         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32570         
32571     },
32572     
32573     _getItemLayoutPosition : function( item )  // what is item?
32574     {
32575         // we resize the item to our columnWidth..
32576       
32577         item.setWidth(this.columnWidth);
32578         item.autoBoxAdjust  = false;
32579         
32580         var sz = item.getSize();
32581  
32582         // how many columns does this brick span
32583         var remainder = this.containerWidth % this.columnWidth;
32584         
32585         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32586         // round if off by 1 pixel, otherwise use ceil
32587         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32588         colSpan = Math.min( colSpan, this.cols );
32589         
32590         // normally this should be '1' as we dont' currently allow multi width columns..
32591         
32592         var colGroup = this._getColGroup( colSpan );
32593         // get the minimum Y value from the columns
32594         var minimumY = Math.min.apply( Math, colGroup );
32595         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32596         
32597         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32598          
32599         // position the brick
32600         var position = {
32601             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32602             y: this.currentSize.y + minimumY + this.padHeight
32603         };
32604         
32605         Roo.log(position);
32606         // apply setHeight to necessary columns
32607         var setHeight = minimumY + sz.height + this.padHeight;
32608         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32609         
32610         var setSpan = this.cols + 1 - colGroup.length;
32611         for ( var i = 0; i < setSpan; i++ ) {
32612           this.colYs[ shortColIndex + i ] = setHeight ;
32613         }
32614       
32615         return position;
32616     },
32617     
32618     /**
32619      * @param {Number} colSpan - number of columns the element spans
32620      * @returns {Array} colGroup
32621      */
32622     _getColGroup : function( colSpan )
32623     {
32624         if ( colSpan < 2 ) {
32625           // if brick spans only one column, use all the column Ys
32626           return this.colYs;
32627         }
32628       
32629         var colGroup = [];
32630         // how many different places could this brick fit horizontally
32631         var groupCount = this.cols + 1 - colSpan;
32632         // for each group potential horizontal position
32633         for ( var i = 0; i < groupCount; i++ ) {
32634           // make an array of colY values for that one group
32635           var groupColYs = this.colYs.slice( i, i + colSpan );
32636           // and get the max value of the array
32637           colGroup[i] = Math.max.apply( Math, groupColYs );
32638         }
32639         return colGroup;
32640     },
32641     /*
32642     _manageStamp : function( stamp )
32643     {
32644         var stampSize =  stamp.getSize();
32645         var offset = stamp.getBox();
32646         // get the columns that this stamp affects
32647         var firstX = this.isOriginLeft ? offset.x : offset.right;
32648         var lastX = firstX + stampSize.width;
32649         var firstCol = Math.floor( firstX / this.columnWidth );
32650         firstCol = Math.max( 0, firstCol );
32651         
32652         var lastCol = Math.floor( lastX / this.columnWidth );
32653         // lastCol should not go over if multiple of columnWidth #425
32654         lastCol -= lastX % this.columnWidth ? 0 : 1;
32655         lastCol = Math.min( this.cols - 1, lastCol );
32656         
32657         // set colYs to bottom of the stamp
32658         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32659             stampSize.height;
32660             
32661         for ( var i = firstCol; i <= lastCol; i++ ) {
32662           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32663         }
32664     },
32665     */
32666     
32667     _getContainerSize : function()
32668     {
32669         this.maxY = Math.max.apply( Math, this.colYs );
32670         var size = {
32671             height: this.maxY
32672         };
32673       
32674         if ( this.isFitWidth ) {
32675             size.width = this._getContainerFitWidth();
32676         }
32677       
32678         return size;
32679     },
32680     
32681     _getContainerFitWidth : function()
32682     {
32683         var unusedCols = 0;
32684         // count unused columns
32685         var i = this.cols;
32686         while ( --i ) {
32687           if ( this.colYs[i] !== 0 ) {
32688             break;
32689           }
32690           unusedCols++;
32691         }
32692         // fit container to columns that have been used
32693         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32694     },
32695     
32696     needsResizeLayout : function()
32697     {
32698         var previousWidth = this.containerWidth;
32699         this.getContainerWidth();
32700         return previousWidth !== this.containerWidth;
32701     }
32702  
32703 });
32704
32705  
32706
32707  /*
32708  * - LGPL
32709  *
32710  * element
32711  * 
32712  */
32713
32714 /**
32715  * @class Roo.bootstrap.MasonryBrick
32716  * @extends Roo.bootstrap.Component
32717  * Bootstrap MasonryBrick class
32718  * 
32719  * @constructor
32720  * Create a new MasonryBrick
32721  * @param {Object} config The config object
32722  */
32723
32724 Roo.bootstrap.MasonryBrick = function(config){
32725     
32726     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32727     
32728     Roo.bootstrap.MasonryBrick.register(this);
32729     
32730     this.addEvents({
32731         // raw events
32732         /**
32733          * @event click
32734          * When a MasonryBrick is clcik
32735          * @param {Roo.bootstrap.MasonryBrick} this
32736          * @param {Roo.EventObject} e
32737          */
32738         "click" : true
32739     });
32740 };
32741
32742 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32743     
32744     /**
32745      * @cfg {String} title
32746      */   
32747     title : '',
32748     /**
32749      * @cfg {String} html
32750      */   
32751     html : '',
32752     /**
32753      * @cfg {String} bgimage
32754      */   
32755     bgimage : '',
32756     /**
32757      * @cfg {String} videourl
32758      */   
32759     videourl : '',
32760     /**
32761      * @cfg {String} cls
32762      */   
32763     cls : '',
32764     /**
32765      * @cfg {String} href
32766      */   
32767     href : '',
32768     /**
32769      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32770      */   
32771     size : 'xs',
32772     
32773     /**
32774      * @cfg {String} placetitle (center|bottom)
32775      */   
32776     placetitle : '',
32777     
32778     /**
32779      * @cfg {Boolean} isFitContainer defalut true
32780      */   
32781     isFitContainer : true, 
32782     
32783     /**
32784      * @cfg {Boolean} preventDefault defalut false
32785      */   
32786     preventDefault : false, 
32787     
32788     /**
32789      * @cfg {Boolean} inverse defalut false
32790      */   
32791     maskInverse : false, 
32792     
32793     getAutoCreate : function()
32794     {
32795         if(!this.isFitContainer){
32796             return this.getSplitAutoCreate();
32797         }
32798         
32799         var cls = 'masonry-brick masonry-brick-full';
32800         
32801         if(this.href.length){
32802             cls += ' masonry-brick-link';
32803         }
32804         
32805         if(this.bgimage.length){
32806             cls += ' masonry-brick-image';
32807         }
32808         
32809         if(this.maskInverse){
32810             cls += ' mask-inverse';
32811         }
32812         
32813         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32814             cls += ' enable-mask';
32815         }
32816         
32817         if(this.size){
32818             cls += ' masonry-' + this.size + '-brick';
32819         }
32820         
32821         if(this.placetitle.length){
32822             
32823             switch (this.placetitle) {
32824                 case 'center' :
32825                     cls += ' masonry-center-title';
32826                     break;
32827                 case 'bottom' :
32828                     cls += ' masonry-bottom-title';
32829                     break;
32830                 default:
32831                     break;
32832             }
32833             
32834         } else {
32835             if(!this.html.length && !this.bgimage.length){
32836                 cls += ' masonry-center-title';
32837             }
32838
32839             if(!this.html.length && this.bgimage.length){
32840                 cls += ' masonry-bottom-title';
32841             }
32842         }
32843         
32844         if(this.cls){
32845             cls += ' ' + this.cls;
32846         }
32847         
32848         var cfg = {
32849             tag: (this.href.length) ? 'a' : 'div',
32850             cls: cls,
32851             cn: [
32852                 {
32853                     tag: 'div',
32854                     cls: 'masonry-brick-mask'
32855                 },
32856                 {
32857                     tag: 'div',
32858                     cls: 'masonry-brick-paragraph',
32859                     cn: []
32860                 }
32861             ]
32862         };
32863         
32864         if(this.href.length){
32865             cfg.href = this.href;
32866         }
32867         
32868         var cn = cfg.cn[1].cn;
32869         
32870         if(this.title.length){
32871             cn.push({
32872                 tag: 'h4',
32873                 cls: 'masonry-brick-title',
32874                 html: this.title
32875             });
32876         }
32877         
32878         if(this.html.length){
32879             cn.push({
32880                 tag: 'p',
32881                 cls: 'masonry-brick-text',
32882                 html: this.html
32883             });
32884         }
32885         
32886         if (!this.title.length && !this.html.length) {
32887             cfg.cn[1].cls += ' hide';
32888         }
32889         
32890         if(this.bgimage.length){
32891             cfg.cn.push({
32892                 tag: 'img',
32893                 cls: 'masonry-brick-image-view',
32894                 src: this.bgimage
32895             });
32896         }
32897         
32898         if(this.videourl.length){
32899             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32900             // youtube support only?
32901             cfg.cn.push({
32902                 tag: 'iframe',
32903                 cls: 'masonry-brick-image-view',
32904                 src: vurl,
32905                 frameborder : 0,
32906                 allowfullscreen : true
32907             });
32908         }
32909         
32910         return cfg;
32911         
32912     },
32913     
32914     getSplitAutoCreate : function()
32915     {
32916         var cls = 'masonry-brick masonry-brick-split';
32917         
32918         if(this.href.length){
32919             cls += ' masonry-brick-link';
32920         }
32921         
32922         if(this.bgimage.length){
32923             cls += ' masonry-brick-image';
32924         }
32925         
32926         if(this.size){
32927             cls += ' masonry-' + this.size + '-brick';
32928         }
32929         
32930         switch (this.placetitle) {
32931             case 'center' :
32932                 cls += ' masonry-center-title';
32933                 break;
32934             case 'bottom' :
32935                 cls += ' masonry-bottom-title';
32936                 break;
32937             default:
32938                 if(!this.bgimage.length){
32939                     cls += ' masonry-center-title';
32940                 }
32941
32942                 if(this.bgimage.length){
32943                     cls += ' masonry-bottom-title';
32944                 }
32945                 break;
32946         }
32947         
32948         if(this.cls){
32949             cls += ' ' + this.cls;
32950         }
32951         
32952         var cfg = {
32953             tag: (this.href.length) ? 'a' : 'div',
32954             cls: cls,
32955             cn: [
32956                 {
32957                     tag: 'div',
32958                     cls: 'masonry-brick-split-head',
32959                     cn: [
32960                         {
32961                             tag: 'div',
32962                             cls: 'masonry-brick-paragraph',
32963                             cn: []
32964                         }
32965                     ]
32966                 },
32967                 {
32968                     tag: 'div',
32969                     cls: 'masonry-brick-split-body',
32970                     cn: []
32971                 }
32972             ]
32973         };
32974         
32975         if(this.href.length){
32976             cfg.href = this.href;
32977         }
32978         
32979         if(this.title.length){
32980             cfg.cn[0].cn[0].cn.push({
32981                 tag: 'h4',
32982                 cls: 'masonry-brick-title',
32983                 html: this.title
32984             });
32985         }
32986         
32987         if(this.html.length){
32988             cfg.cn[1].cn.push({
32989                 tag: 'p',
32990                 cls: 'masonry-brick-text',
32991                 html: this.html
32992             });
32993         }
32994
32995         if(this.bgimage.length){
32996             cfg.cn[0].cn.push({
32997                 tag: 'img',
32998                 cls: 'masonry-brick-image-view',
32999                 src: this.bgimage
33000             });
33001         }
33002         
33003         if(this.videourl.length){
33004             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33005             // youtube support only?
33006             cfg.cn[0].cn.cn.push({
33007                 tag: 'iframe',
33008                 cls: 'masonry-brick-image-view',
33009                 src: vurl,
33010                 frameborder : 0,
33011                 allowfullscreen : true
33012             });
33013         }
33014         
33015         return cfg;
33016     },
33017     
33018     initEvents: function() 
33019     {
33020         switch (this.size) {
33021             case 'xs' :
33022                 this.x = 1;
33023                 this.y = 1;
33024                 break;
33025             case 'sm' :
33026                 this.x = 2;
33027                 this.y = 2;
33028                 break;
33029             case 'md' :
33030             case 'md-left' :
33031             case 'md-right' :
33032                 this.x = 3;
33033                 this.y = 3;
33034                 break;
33035             case 'tall' :
33036                 this.x = 2;
33037                 this.y = 3;
33038                 break;
33039             case 'wide' :
33040                 this.x = 3;
33041                 this.y = 2;
33042                 break;
33043             case 'wide-thin' :
33044                 this.x = 3;
33045                 this.y = 1;
33046                 break;
33047                         
33048             default :
33049                 break;
33050         }
33051         
33052         if(Roo.isTouch){
33053             this.el.on('touchstart', this.onTouchStart, this);
33054             this.el.on('touchmove', this.onTouchMove, this);
33055             this.el.on('touchend', this.onTouchEnd, this);
33056             this.el.on('contextmenu', this.onContextMenu, this);
33057         } else {
33058             this.el.on('mouseenter'  ,this.enter, this);
33059             this.el.on('mouseleave', this.leave, this);
33060             this.el.on('click', this.onClick, this);
33061         }
33062         
33063         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33064             this.parent().bricks.push(this);   
33065         }
33066         
33067     },
33068     
33069     onClick: function(e, el)
33070     {
33071         var time = this.endTimer - this.startTimer;
33072         // Roo.log(e.preventDefault());
33073         if(Roo.isTouch){
33074             if(time > 1000){
33075                 e.preventDefault();
33076                 return;
33077             }
33078         }
33079         
33080         if(!this.preventDefault){
33081             return;
33082         }
33083         
33084         e.preventDefault();
33085         
33086         if (this.activeClass != '') {
33087             this.selectBrick();
33088         }
33089         
33090         this.fireEvent('click', this, e);
33091     },
33092     
33093     enter: function(e, el)
33094     {
33095         e.preventDefault();
33096         
33097         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33098             return;
33099         }
33100         
33101         if(this.bgimage.length && this.html.length){
33102             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33103         }
33104     },
33105     
33106     leave: function(e, el)
33107     {
33108         e.preventDefault();
33109         
33110         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33111             return;
33112         }
33113         
33114         if(this.bgimage.length && this.html.length){
33115             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33116         }
33117     },
33118     
33119     onTouchStart: function(e, el)
33120     {
33121 //        e.preventDefault();
33122         
33123         this.touchmoved = false;
33124         
33125         if(!this.isFitContainer){
33126             return;
33127         }
33128         
33129         if(!this.bgimage.length || !this.html.length){
33130             return;
33131         }
33132         
33133         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33134         
33135         this.timer = new Date().getTime();
33136         
33137     },
33138     
33139     onTouchMove: function(e, el)
33140     {
33141         this.touchmoved = true;
33142     },
33143     
33144     onContextMenu : function(e,el)
33145     {
33146         e.preventDefault();
33147         e.stopPropagation();
33148         return false;
33149     },
33150     
33151     onTouchEnd: function(e, el)
33152     {
33153 //        e.preventDefault();
33154         
33155         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33156         
33157             this.leave(e,el);
33158             
33159             return;
33160         }
33161         
33162         if(!this.bgimage.length || !this.html.length){
33163             
33164             if(this.href.length){
33165                 window.location.href = this.href;
33166             }
33167             
33168             return;
33169         }
33170         
33171         if(!this.isFitContainer){
33172             return;
33173         }
33174         
33175         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33176         
33177         window.location.href = this.href;
33178     },
33179     
33180     //selection on single brick only
33181     selectBrick : function() {
33182         
33183         if (!this.parentId) {
33184             return;
33185         }
33186         
33187         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33188         var index = m.selectedBrick.indexOf(this.id);
33189         
33190         if ( index > -1) {
33191             m.selectedBrick.splice(index,1);
33192             this.el.removeClass(this.activeClass);
33193             return;
33194         }
33195         
33196         for(var i = 0; i < m.selectedBrick.length; i++) {
33197             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33198             b.el.removeClass(b.activeClass);
33199         }
33200         
33201         m.selectedBrick = [];
33202         
33203         m.selectedBrick.push(this.id);
33204         this.el.addClass(this.activeClass);
33205         return;
33206     },
33207     
33208     isSelected : function(){
33209         return this.el.hasClass(this.activeClass);
33210         
33211     }
33212 });
33213
33214 Roo.apply(Roo.bootstrap.MasonryBrick, {
33215     
33216     //groups: {},
33217     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33218      /**
33219     * register a Masonry Brick
33220     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33221     */
33222     
33223     register : function(brick)
33224     {
33225         //this.groups[brick.id] = brick;
33226         this.groups.add(brick.id, brick);
33227     },
33228     /**
33229     * fetch a  masonry brick based on the masonry brick ID
33230     * @param {string} the masonry brick to add
33231     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33232     */
33233     
33234     get: function(brick_id) 
33235     {
33236         // if (typeof(this.groups[brick_id]) == 'undefined') {
33237         //     return false;
33238         // }
33239         // return this.groups[brick_id] ;
33240         
33241         if(this.groups.key(brick_id)) {
33242             return this.groups.key(brick_id);
33243         }
33244         
33245         return false;
33246     }
33247     
33248     
33249     
33250 });
33251
33252  /*
33253  * - LGPL
33254  *
33255  * element
33256  * 
33257  */
33258
33259 /**
33260  * @class Roo.bootstrap.Brick
33261  * @extends Roo.bootstrap.Component
33262  * Bootstrap Brick class
33263  * 
33264  * @constructor
33265  * Create a new Brick
33266  * @param {Object} config The config object
33267  */
33268
33269 Roo.bootstrap.Brick = function(config){
33270     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33271     
33272     this.addEvents({
33273         // raw events
33274         /**
33275          * @event click
33276          * When a Brick is click
33277          * @param {Roo.bootstrap.Brick} this
33278          * @param {Roo.EventObject} e
33279          */
33280         "click" : true
33281     });
33282 };
33283
33284 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33285     
33286     /**
33287      * @cfg {String} title
33288      */   
33289     title : '',
33290     /**
33291      * @cfg {String} html
33292      */   
33293     html : '',
33294     /**
33295      * @cfg {String} bgimage
33296      */   
33297     bgimage : '',
33298     /**
33299      * @cfg {String} cls
33300      */   
33301     cls : '',
33302     /**
33303      * @cfg {String} href
33304      */   
33305     href : '',
33306     /**
33307      * @cfg {String} video
33308      */   
33309     video : '',
33310     /**
33311      * @cfg {Boolean} square
33312      */   
33313     square : true,
33314     
33315     getAutoCreate : function()
33316     {
33317         var cls = 'roo-brick';
33318         
33319         if(this.href.length){
33320             cls += ' roo-brick-link';
33321         }
33322         
33323         if(this.bgimage.length){
33324             cls += ' roo-brick-image';
33325         }
33326         
33327         if(!this.html.length && !this.bgimage.length){
33328             cls += ' roo-brick-center-title';
33329         }
33330         
33331         if(!this.html.length && this.bgimage.length){
33332             cls += ' roo-brick-bottom-title';
33333         }
33334         
33335         if(this.cls){
33336             cls += ' ' + this.cls;
33337         }
33338         
33339         var cfg = {
33340             tag: (this.href.length) ? 'a' : 'div',
33341             cls: cls,
33342             cn: [
33343                 {
33344                     tag: 'div',
33345                     cls: 'roo-brick-paragraph',
33346                     cn: []
33347                 }
33348             ]
33349         };
33350         
33351         if(this.href.length){
33352             cfg.href = this.href;
33353         }
33354         
33355         var cn = cfg.cn[0].cn;
33356         
33357         if(this.title.length){
33358             cn.push({
33359                 tag: 'h4',
33360                 cls: 'roo-brick-title',
33361                 html: this.title
33362             });
33363         }
33364         
33365         if(this.html.length){
33366             cn.push({
33367                 tag: 'p',
33368                 cls: 'roo-brick-text',
33369                 html: this.html
33370             });
33371         } else {
33372             cn.cls += ' hide';
33373         }
33374         
33375         if(this.bgimage.length){
33376             cfg.cn.push({
33377                 tag: 'img',
33378                 cls: 'roo-brick-image-view',
33379                 src: this.bgimage
33380             });
33381         }
33382         
33383         return cfg;
33384     },
33385     
33386     initEvents: function() 
33387     {
33388         if(this.title.length || this.html.length){
33389             this.el.on('mouseenter'  ,this.enter, this);
33390             this.el.on('mouseleave', this.leave, this);
33391         }
33392         
33393         Roo.EventManager.onWindowResize(this.resize, this); 
33394         
33395         if(this.bgimage.length){
33396             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33397             this.imageEl.on('load', this.onImageLoad, this);
33398             return;
33399         }
33400         
33401         this.resize();
33402     },
33403     
33404     onImageLoad : function()
33405     {
33406         this.resize();
33407     },
33408     
33409     resize : function()
33410     {
33411         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33412         
33413         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33414         
33415         if(this.bgimage.length){
33416             var image = this.el.select('.roo-brick-image-view', true).first();
33417             
33418             image.setWidth(paragraph.getWidth());
33419             
33420             if(this.square){
33421                 image.setHeight(paragraph.getWidth());
33422             }
33423             
33424             this.el.setHeight(image.getHeight());
33425             paragraph.setHeight(image.getHeight());
33426             
33427         }
33428         
33429     },
33430     
33431     enter: function(e, el)
33432     {
33433         e.preventDefault();
33434         
33435         if(this.bgimage.length){
33436             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33437             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33438         }
33439     },
33440     
33441     leave: function(e, el)
33442     {
33443         e.preventDefault();
33444         
33445         if(this.bgimage.length){
33446             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33447             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33448         }
33449     }
33450     
33451 });
33452
33453  
33454
33455  /*
33456  * - LGPL
33457  *
33458  * Number field 
33459  */
33460
33461 /**
33462  * @class Roo.bootstrap.NumberField
33463  * @extends Roo.bootstrap.Input
33464  * Bootstrap NumberField class
33465  * 
33466  * 
33467  * 
33468  * 
33469  * @constructor
33470  * Create a new NumberField
33471  * @param {Object} config The config object
33472  */
33473
33474 Roo.bootstrap.NumberField = function(config){
33475     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33476 };
33477
33478 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33479     
33480     /**
33481      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33482      */
33483     allowDecimals : true,
33484     /**
33485      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33486      */
33487     decimalSeparator : ".",
33488     /**
33489      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33490      */
33491     decimalPrecision : 2,
33492     /**
33493      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33494      */
33495     allowNegative : true,
33496     
33497     /**
33498      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33499      */
33500     allowZero: true,
33501     /**
33502      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33503      */
33504     minValue : Number.NEGATIVE_INFINITY,
33505     /**
33506      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33507      */
33508     maxValue : Number.MAX_VALUE,
33509     /**
33510      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33511      */
33512     minText : "The minimum value for this field is {0}",
33513     /**
33514      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33515      */
33516     maxText : "The maximum value for this field is {0}",
33517     /**
33518      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33519      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33520      */
33521     nanText : "{0} is not a valid number",
33522     /**
33523      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33524      */
33525     thousandsDelimiter : false,
33526     /**
33527      * @cfg {String} valueAlign alignment of value
33528      */
33529     valueAlign : "left",
33530
33531     getAutoCreate : function()
33532     {
33533         var hiddenInput = {
33534             tag: 'input',
33535             type: 'hidden',
33536             id: Roo.id(),
33537             cls: 'hidden-number-input'
33538         };
33539         
33540         if (this.name) {
33541             hiddenInput.name = this.name;
33542         }
33543         
33544         this.name = '';
33545         
33546         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33547         
33548         this.name = hiddenInput.name;
33549         
33550         if(cfg.cn.length > 0) {
33551             cfg.cn.push(hiddenInput);
33552         }
33553         
33554         return cfg;
33555     },
33556
33557     // private
33558     initEvents : function()
33559     {   
33560         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33561         
33562         var allowed = "0123456789";
33563         
33564         if(this.allowDecimals){
33565             allowed += this.decimalSeparator;
33566         }
33567         
33568         if(this.allowNegative){
33569             allowed += "-";
33570         }
33571         
33572         if(this.thousandsDelimiter) {
33573             allowed += ",";
33574         }
33575         
33576         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33577         
33578         var keyPress = function(e){
33579             
33580             var k = e.getKey();
33581             
33582             var c = e.getCharCode();
33583             
33584             if(
33585                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33586                     allowed.indexOf(String.fromCharCode(c)) === -1
33587             ){
33588                 e.stopEvent();
33589                 return;
33590             }
33591             
33592             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33593                 return;
33594             }
33595             
33596             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33597                 e.stopEvent();
33598             }
33599         };
33600         
33601         this.el.on("keypress", keyPress, this);
33602     },
33603     
33604     validateValue : function(value)
33605     {
33606         
33607         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33608             return false;
33609         }
33610         
33611         var num = this.parseValue(value);
33612         
33613         if(isNaN(num)){
33614             this.markInvalid(String.format(this.nanText, value));
33615             return false;
33616         }
33617         
33618         if(num < this.minValue){
33619             this.markInvalid(String.format(this.minText, this.minValue));
33620             return false;
33621         }
33622         
33623         if(num > this.maxValue){
33624             this.markInvalid(String.format(this.maxText, this.maxValue));
33625             return false;
33626         }
33627         
33628         return true;
33629     },
33630
33631     getValue : function()
33632     {
33633         var v = this.hiddenEl().getValue();
33634         
33635         return this.fixPrecision(this.parseValue(v));
33636     },
33637
33638     parseValue : function(value)
33639     {
33640         if(this.thousandsDelimiter) {
33641             value += "";
33642             r = new RegExp(",", "g");
33643             value = value.replace(r, "");
33644         }
33645         
33646         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33647         return isNaN(value) ? '' : value;
33648     },
33649
33650     fixPrecision : function(value)
33651     {
33652         if(this.thousandsDelimiter) {
33653             value += "";
33654             r = new RegExp(",", "g");
33655             value = value.replace(r, "");
33656         }
33657         
33658         var nan = isNaN(value);
33659         
33660         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33661             return nan ? '' : value;
33662         }
33663         return parseFloat(value).toFixed(this.decimalPrecision);
33664     },
33665
33666     setValue : function(v)
33667     {
33668         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33669         
33670         this.value = v;
33671         
33672         if(this.rendered){
33673             
33674             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33675             
33676             this.inputEl().dom.value = (v == '') ? '' :
33677                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33678             
33679             if(!this.allowZero && v === '0') {
33680                 this.hiddenEl().dom.value = '';
33681                 this.inputEl().dom.value = '';
33682             }
33683             
33684             this.validate();
33685         }
33686     },
33687
33688     decimalPrecisionFcn : function(v)
33689     {
33690         return Math.floor(v);
33691     },
33692
33693     beforeBlur : function()
33694     {
33695         var v = this.parseValue(this.getRawValue());
33696         
33697         if(v || v === 0 || v === ''){
33698             this.setValue(v);
33699         }
33700     },
33701     
33702     hiddenEl : function()
33703     {
33704         return this.el.select('input.hidden-number-input',true).first();
33705     }
33706     
33707 });
33708
33709  
33710
33711 /*
33712 * Licence: LGPL
33713 */
33714
33715 /**
33716  * @class Roo.bootstrap.DocumentSlider
33717  * @extends Roo.bootstrap.Component
33718  * Bootstrap DocumentSlider class
33719  * 
33720  * @constructor
33721  * Create a new DocumentViewer
33722  * @param {Object} config The config object
33723  */
33724
33725 Roo.bootstrap.DocumentSlider = function(config){
33726     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33727     
33728     this.files = [];
33729     
33730     this.addEvents({
33731         /**
33732          * @event initial
33733          * Fire after initEvent
33734          * @param {Roo.bootstrap.DocumentSlider} this
33735          */
33736         "initial" : true,
33737         /**
33738          * @event update
33739          * Fire after update
33740          * @param {Roo.bootstrap.DocumentSlider} this
33741          */
33742         "update" : true,
33743         /**
33744          * @event click
33745          * Fire after click
33746          * @param {Roo.bootstrap.DocumentSlider} this
33747          */
33748         "click" : true
33749     });
33750 };
33751
33752 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33753     
33754     files : false,
33755     
33756     indicator : 0,
33757     
33758     getAutoCreate : function()
33759     {
33760         var cfg = {
33761             tag : 'div',
33762             cls : 'roo-document-slider',
33763             cn : [
33764                 {
33765                     tag : 'div',
33766                     cls : 'roo-document-slider-header',
33767                     cn : [
33768                         {
33769                             tag : 'div',
33770                             cls : 'roo-document-slider-header-title'
33771                         }
33772                     ]
33773                 },
33774                 {
33775                     tag : 'div',
33776                     cls : 'roo-document-slider-body',
33777                     cn : [
33778                         {
33779                             tag : 'div',
33780                             cls : 'roo-document-slider-prev',
33781                             cn : [
33782                                 {
33783                                     tag : 'i',
33784                                     cls : 'fa fa-chevron-left'
33785                                 }
33786                             ]
33787                         },
33788                         {
33789                             tag : 'div',
33790                             cls : 'roo-document-slider-thumb',
33791                             cn : [
33792                                 {
33793                                     tag : 'img',
33794                                     cls : 'roo-document-slider-image'
33795                                 }
33796                             ]
33797                         },
33798                         {
33799                             tag : 'div',
33800                             cls : 'roo-document-slider-next',
33801                             cn : [
33802                                 {
33803                                     tag : 'i',
33804                                     cls : 'fa fa-chevron-right'
33805                                 }
33806                             ]
33807                         }
33808                     ]
33809                 }
33810             ]
33811         };
33812         
33813         return cfg;
33814     },
33815     
33816     initEvents : function()
33817     {
33818         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33819         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33820         
33821         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33822         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33823         
33824         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33825         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33826         
33827         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33828         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33829         
33830         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33831         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33832         
33833         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33834         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33835         
33836         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33837         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33838         
33839         this.thumbEl.on('click', this.onClick, this);
33840         
33841         this.prevIndicator.on('click', this.prev, this);
33842         
33843         this.nextIndicator.on('click', this.next, this);
33844         
33845     },
33846     
33847     initial : function()
33848     {
33849         if(this.files.length){
33850             this.indicator = 1;
33851             this.update()
33852         }
33853         
33854         this.fireEvent('initial', this);
33855     },
33856     
33857     update : function()
33858     {
33859         this.imageEl.attr('src', this.files[this.indicator - 1]);
33860         
33861         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33862         
33863         this.prevIndicator.show();
33864         
33865         if(this.indicator == 1){
33866             this.prevIndicator.hide();
33867         }
33868         
33869         this.nextIndicator.show();
33870         
33871         if(this.indicator == this.files.length){
33872             this.nextIndicator.hide();
33873         }
33874         
33875         this.thumbEl.scrollTo('top');
33876         
33877         this.fireEvent('update', this);
33878     },
33879     
33880     onClick : function(e)
33881     {
33882         e.preventDefault();
33883         
33884         this.fireEvent('click', this);
33885     },
33886     
33887     prev : function(e)
33888     {
33889         e.preventDefault();
33890         
33891         this.indicator = Math.max(1, this.indicator - 1);
33892         
33893         this.update();
33894     },
33895     
33896     next : function(e)
33897     {
33898         e.preventDefault();
33899         
33900         this.indicator = Math.min(this.files.length, this.indicator + 1);
33901         
33902         this.update();
33903     }
33904 });
33905 /*
33906  * - LGPL
33907  *
33908  * RadioSet
33909  *
33910  *
33911  */
33912
33913 /**
33914  * @class Roo.bootstrap.RadioSet
33915  * @extends Roo.bootstrap.Input
33916  * Bootstrap RadioSet class
33917  * @cfg {String} indicatorpos (left|right) default left
33918  * @cfg {Boolean} inline (true|false) inline the element (default true)
33919  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33920  * @constructor
33921  * Create a new RadioSet
33922  * @param {Object} config The config object
33923  */
33924
33925 Roo.bootstrap.RadioSet = function(config){
33926     
33927     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33928     
33929     this.radioes = [];
33930     
33931     Roo.bootstrap.RadioSet.register(this);
33932     
33933     this.addEvents({
33934         /**
33935         * @event check
33936         * Fires when the element is checked or unchecked.
33937         * @param {Roo.bootstrap.RadioSet} this This radio
33938         * @param {Roo.bootstrap.Radio} item The checked item
33939         */
33940        check : true,
33941        /**
33942         * @event click
33943         * Fires when the element is click.
33944         * @param {Roo.bootstrap.RadioSet} this This radio set
33945         * @param {Roo.bootstrap.Radio} item The checked item
33946         * @param {Roo.EventObject} e The event object
33947         */
33948        click : true
33949     });
33950     
33951 };
33952
33953 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33954
33955     radioes : false,
33956     
33957     inline : true,
33958     
33959     weight : '',
33960     
33961     indicatorpos : 'left',
33962     
33963     getAutoCreate : function()
33964     {
33965         var label = {
33966             tag : 'label',
33967             cls : 'roo-radio-set-label',
33968             cn : [
33969                 {
33970                     tag : 'span',
33971                     html : this.fieldLabel
33972                 }
33973             ]
33974         };
33975         
33976         if(this.indicatorpos == 'left'){
33977             label.cn.unshift({
33978                 tag : 'i',
33979                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33980                 tooltip : 'This field is required'
33981             });
33982         } else {
33983             label.cn.push({
33984                 tag : 'i',
33985                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33986                 tooltip : 'This field is required'
33987             });
33988         }
33989         
33990         var items = {
33991             tag : 'div',
33992             cls : 'roo-radio-set-items'
33993         };
33994         
33995         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33996         
33997         if (align === 'left' && this.fieldLabel.length) {
33998             
33999             items = {
34000                 cls : "roo-radio-set-right", 
34001                 cn: [
34002                     items
34003                 ]
34004             };
34005             
34006             if(this.labelWidth > 12){
34007                 label.style = "width: " + this.labelWidth + 'px';
34008             }
34009             
34010             if(this.labelWidth < 13 && this.labelmd == 0){
34011                 this.labelmd = this.labelWidth;
34012             }
34013             
34014             if(this.labellg > 0){
34015                 label.cls += ' col-lg-' + this.labellg;
34016                 items.cls += ' col-lg-' + (12 - this.labellg);
34017             }
34018             
34019             if(this.labelmd > 0){
34020                 label.cls += ' col-md-' + this.labelmd;
34021                 items.cls += ' col-md-' + (12 - this.labelmd);
34022             }
34023             
34024             if(this.labelsm > 0){
34025                 label.cls += ' col-sm-' + this.labelsm;
34026                 items.cls += ' col-sm-' + (12 - this.labelsm);
34027             }
34028             
34029             if(this.labelxs > 0){
34030                 label.cls += ' col-xs-' + this.labelxs;
34031                 items.cls += ' col-xs-' + (12 - this.labelxs);
34032             }
34033         }
34034         
34035         var cfg = {
34036             tag : 'div',
34037             cls : 'roo-radio-set',
34038             cn : [
34039                 {
34040                     tag : 'input',
34041                     cls : 'roo-radio-set-input',
34042                     type : 'hidden',
34043                     name : this.name,
34044                     value : this.value ? this.value :  ''
34045                 },
34046                 label,
34047                 items
34048             ]
34049         };
34050         
34051         if(this.weight.length){
34052             cfg.cls += ' roo-radio-' + this.weight;
34053         }
34054         
34055         if(this.inline) {
34056             cfg.cls += ' roo-radio-set-inline';
34057         }
34058         
34059         var settings=this;
34060         ['xs','sm','md','lg'].map(function(size){
34061             if (settings[size]) {
34062                 cfg.cls += ' col-' + size + '-' + settings[size];
34063             }
34064         });
34065         
34066         return cfg;
34067         
34068     },
34069
34070     initEvents : function()
34071     {
34072         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34073         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34074         
34075         if(!this.fieldLabel.length){
34076             this.labelEl.hide();
34077         }
34078         
34079         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34080         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34081         
34082         this.indicator = this.indicatorEl();
34083         
34084         if(this.indicator){
34085             this.indicator.addClass('invisible');
34086         }
34087         
34088         this.originalValue = this.getValue();
34089         
34090     },
34091     
34092     inputEl: function ()
34093     {
34094         return this.el.select('.roo-radio-set-input', true).first();
34095     },
34096     
34097     getChildContainer : function()
34098     {
34099         return this.itemsEl;
34100     },
34101     
34102     register : function(item)
34103     {
34104         this.radioes.push(item);
34105         
34106     },
34107     
34108     validate : function()
34109     {   
34110         if(this.getVisibilityEl().hasClass('hidden')){
34111             return true;
34112         }
34113         
34114         var valid = false;
34115         
34116         Roo.each(this.radioes, function(i){
34117             if(!i.checked){
34118                 return;
34119             }
34120             
34121             valid = true;
34122             return false;
34123         });
34124         
34125         if(this.allowBlank) {
34126             return true;
34127         }
34128         
34129         if(this.disabled || valid){
34130             this.markValid();
34131             return true;
34132         }
34133         
34134         this.markInvalid();
34135         return false;
34136         
34137     },
34138     
34139     markValid : function()
34140     {
34141         if(this.labelEl.isVisible(true)){
34142             this.indicatorEl().removeClass('visible');
34143             this.indicatorEl().addClass('invisible');
34144         }
34145         
34146         this.el.removeClass([this.invalidClass, this.validClass]);
34147         this.el.addClass(this.validClass);
34148         
34149         this.fireEvent('valid', this);
34150     },
34151     
34152     markInvalid : function(msg)
34153     {
34154         if(this.allowBlank || this.disabled){
34155             return;
34156         }
34157         
34158         if(this.labelEl.isVisible(true)){
34159             this.indicatorEl().removeClass('invisible');
34160             this.indicatorEl().addClass('visible');
34161         }
34162         
34163         this.el.removeClass([this.invalidClass, this.validClass]);
34164         this.el.addClass(this.invalidClass);
34165         
34166         this.fireEvent('invalid', this, msg);
34167         
34168     },
34169     
34170     setValue : function(v, suppressEvent)
34171     {   
34172         if(this.value === v){
34173             return;
34174         }
34175         
34176         this.value = v;
34177         
34178         if(this.rendered){
34179             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34180         }
34181         
34182         Roo.each(this.radioes, function(i){
34183             i.checked = false;
34184             i.el.removeClass('checked');
34185         });
34186         
34187         Roo.each(this.radioes, function(i){
34188             
34189             if(i.value === v || i.value.toString() === v.toString()){
34190                 i.checked = true;
34191                 i.el.addClass('checked');
34192                 
34193                 if(suppressEvent !== true){
34194                     this.fireEvent('check', this, i);
34195                 }
34196                 
34197                 return false;
34198             }
34199             
34200         }, this);
34201         
34202         this.validate();
34203     },
34204     
34205     clearInvalid : function(){
34206         
34207         if(!this.el || this.preventMark){
34208             return;
34209         }
34210         
34211         this.el.removeClass([this.invalidClass]);
34212         
34213         this.fireEvent('valid', this);
34214     }
34215     
34216 });
34217
34218 Roo.apply(Roo.bootstrap.RadioSet, {
34219     
34220     groups: {},
34221     
34222     register : function(set)
34223     {
34224         this.groups[set.name] = set;
34225     },
34226     
34227     get: function(name) 
34228     {
34229         if (typeof(this.groups[name]) == 'undefined') {
34230             return false;
34231         }
34232         
34233         return this.groups[name] ;
34234     }
34235     
34236 });
34237 /*
34238  * Based on:
34239  * Ext JS Library 1.1.1
34240  * Copyright(c) 2006-2007, Ext JS, LLC.
34241  *
34242  * Originally Released Under LGPL - original licence link has changed is not relivant.
34243  *
34244  * Fork - LGPL
34245  * <script type="text/javascript">
34246  */
34247
34248
34249 /**
34250  * @class Roo.bootstrap.SplitBar
34251  * @extends Roo.util.Observable
34252  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34253  * <br><br>
34254  * Usage:
34255  * <pre><code>
34256 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34257                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34258 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34259 split.minSize = 100;
34260 split.maxSize = 600;
34261 split.animate = true;
34262 split.on('moved', splitterMoved);
34263 </code></pre>
34264  * @constructor
34265  * Create a new SplitBar
34266  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34267  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34268  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34269  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34270                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34271                         position of the SplitBar).
34272  */
34273 Roo.bootstrap.SplitBar = function(cfg){
34274     
34275     /** @private */
34276     
34277     //{
34278     //  dragElement : elm
34279     //  resizingElement: el,
34280         // optional..
34281     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34282     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34283         // existingProxy ???
34284     //}
34285     
34286     this.el = Roo.get(cfg.dragElement, true);
34287     this.el.dom.unselectable = "on";
34288     /** @private */
34289     this.resizingEl = Roo.get(cfg.resizingElement, true);
34290
34291     /**
34292      * @private
34293      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34294      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34295      * @type Number
34296      */
34297     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34298     
34299     /**
34300      * The minimum size of the resizing element. (Defaults to 0)
34301      * @type Number
34302      */
34303     this.minSize = 0;
34304     
34305     /**
34306      * The maximum size of the resizing element. (Defaults to 2000)
34307      * @type Number
34308      */
34309     this.maxSize = 2000;
34310     
34311     /**
34312      * Whether to animate the transition to the new size
34313      * @type Boolean
34314      */
34315     this.animate = false;
34316     
34317     /**
34318      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34319      * @type Boolean
34320      */
34321     this.useShim = false;
34322     
34323     /** @private */
34324     this.shim = null;
34325     
34326     if(!cfg.existingProxy){
34327         /** @private */
34328         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34329     }else{
34330         this.proxy = Roo.get(cfg.existingProxy).dom;
34331     }
34332     /** @private */
34333     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34334     
34335     /** @private */
34336     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34337     
34338     /** @private */
34339     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34340     
34341     /** @private */
34342     this.dragSpecs = {};
34343     
34344     /**
34345      * @private The adapter to use to positon and resize elements
34346      */
34347     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34348     this.adapter.init(this);
34349     
34350     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34351         /** @private */
34352         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34353         this.el.addClass("roo-splitbar-h");
34354     }else{
34355         /** @private */
34356         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34357         this.el.addClass("roo-splitbar-v");
34358     }
34359     
34360     this.addEvents({
34361         /**
34362          * @event resize
34363          * Fires when the splitter is moved (alias for {@link #event-moved})
34364          * @param {Roo.bootstrap.SplitBar} this
34365          * @param {Number} newSize the new width or height
34366          */
34367         "resize" : true,
34368         /**
34369          * @event moved
34370          * Fires when the splitter is moved
34371          * @param {Roo.bootstrap.SplitBar} this
34372          * @param {Number} newSize the new width or height
34373          */
34374         "moved" : true,
34375         /**
34376          * @event beforeresize
34377          * Fires before the splitter is dragged
34378          * @param {Roo.bootstrap.SplitBar} this
34379          */
34380         "beforeresize" : true,
34381
34382         "beforeapply" : true
34383     });
34384
34385     Roo.util.Observable.call(this);
34386 };
34387
34388 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34389     onStartProxyDrag : function(x, y){
34390         this.fireEvent("beforeresize", this);
34391         if(!this.overlay){
34392             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34393             o.unselectable();
34394             o.enableDisplayMode("block");
34395             // all splitbars share the same overlay
34396             Roo.bootstrap.SplitBar.prototype.overlay = o;
34397         }
34398         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34399         this.overlay.show();
34400         Roo.get(this.proxy).setDisplayed("block");
34401         var size = this.adapter.getElementSize(this);
34402         this.activeMinSize = this.getMinimumSize();;
34403         this.activeMaxSize = this.getMaximumSize();;
34404         var c1 = size - this.activeMinSize;
34405         var c2 = Math.max(this.activeMaxSize - size, 0);
34406         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34407             this.dd.resetConstraints();
34408             this.dd.setXConstraint(
34409                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34410                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34411             );
34412             this.dd.setYConstraint(0, 0);
34413         }else{
34414             this.dd.resetConstraints();
34415             this.dd.setXConstraint(0, 0);
34416             this.dd.setYConstraint(
34417                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34418                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34419             );
34420          }
34421         this.dragSpecs.startSize = size;
34422         this.dragSpecs.startPoint = [x, y];
34423         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34424     },
34425     
34426     /** 
34427      * @private Called after the drag operation by the DDProxy
34428      */
34429     onEndProxyDrag : function(e){
34430         Roo.get(this.proxy).setDisplayed(false);
34431         var endPoint = Roo.lib.Event.getXY(e);
34432         if(this.overlay){
34433             this.overlay.hide();
34434         }
34435         var newSize;
34436         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34437             newSize = this.dragSpecs.startSize + 
34438                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34439                     endPoint[0] - this.dragSpecs.startPoint[0] :
34440                     this.dragSpecs.startPoint[0] - endPoint[0]
34441                 );
34442         }else{
34443             newSize = this.dragSpecs.startSize + 
34444                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34445                     endPoint[1] - this.dragSpecs.startPoint[1] :
34446                     this.dragSpecs.startPoint[1] - endPoint[1]
34447                 );
34448         }
34449         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34450         if(newSize != this.dragSpecs.startSize){
34451             if(this.fireEvent('beforeapply', this, newSize) !== false){
34452                 this.adapter.setElementSize(this, newSize);
34453                 this.fireEvent("moved", this, newSize);
34454                 this.fireEvent("resize", this, newSize);
34455             }
34456         }
34457     },
34458     
34459     /**
34460      * Get the adapter this SplitBar uses
34461      * @return The adapter object
34462      */
34463     getAdapter : function(){
34464         return this.adapter;
34465     },
34466     
34467     /**
34468      * Set the adapter this SplitBar uses
34469      * @param {Object} adapter A SplitBar adapter object
34470      */
34471     setAdapter : function(adapter){
34472         this.adapter = adapter;
34473         this.adapter.init(this);
34474     },
34475     
34476     /**
34477      * Gets the minimum size for the resizing element
34478      * @return {Number} The minimum size
34479      */
34480     getMinimumSize : function(){
34481         return this.minSize;
34482     },
34483     
34484     /**
34485      * Sets the minimum size for the resizing element
34486      * @param {Number} minSize The minimum size
34487      */
34488     setMinimumSize : function(minSize){
34489         this.minSize = minSize;
34490     },
34491     
34492     /**
34493      * Gets the maximum size for the resizing element
34494      * @return {Number} The maximum size
34495      */
34496     getMaximumSize : function(){
34497         return this.maxSize;
34498     },
34499     
34500     /**
34501      * Sets the maximum size for the resizing element
34502      * @param {Number} maxSize The maximum size
34503      */
34504     setMaximumSize : function(maxSize){
34505         this.maxSize = maxSize;
34506     },
34507     
34508     /**
34509      * Sets the initialize size for the resizing element
34510      * @param {Number} size The initial size
34511      */
34512     setCurrentSize : function(size){
34513         var oldAnimate = this.animate;
34514         this.animate = false;
34515         this.adapter.setElementSize(this, size);
34516         this.animate = oldAnimate;
34517     },
34518     
34519     /**
34520      * Destroy this splitbar. 
34521      * @param {Boolean} removeEl True to remove the element
34522      */
34523     destroy : function(removeEl){
34524         if(this.shim){
34525             this.shim.remove();
34526         }
34527         this.dd.unreg();
34528         this.proxy.parentNode.removeChild(this.proxy);
34529         if(removeEl){
34530             this.el.remove();
34531         }
34532     }
34533 });
34534
34535 /**
34536  * @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.
34537  */
34538 Roo.bootstrap.SplitBar.createProxy = function(dir){
34539     var proxy = new Roo.Element(document.createElement("div"));
34540     proxy.unselectable();
34541     var cls = 'roo-splitbar-proxy';
34542     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34543     document.body.appendChild(proxy.dom);
34544     return proxy.dom;
34545 };
34546
34547 /** 
34548  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34549  * Default Adapter. It assumes the splitter and resizing element are not positioned
34550  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34551  */
34552 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34553 };
34554
34555 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34556     // do nothing for now
34557     init : function(s){
34558     
34559     },
34560     /**
34561      * Called before drag operations to get the current size of the resizing element. 
34562      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34563      */
34564      getElementSize : function(s){
34565         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34566             return s.resizingEl.getWidth();
34567         }else{
34568             return s.resizingEl.getHeight();
34569         }
34570     },
34571     
34572     /**
34573      * Called after drag operations to set the size of the resizing element.
34574      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34575      * @param {Number} newSize The new size to set
34576      * @param {Function} onComplete A function to be invoked when resizing is complete
34577      */
34578     setElementSize : function(s, newSize, onComplete){
34579         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34580             if(!s.animate){
34581                 s.resizingEl.setWidth(newSize);
34582                 if(onComplete){
34583                     onComplete(s, newSize);
34584                 }
34585             }else{
34586                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34587             }
34588         }else{
34589             
34590             if(!s.animate){
34591                 s.resizingEl.setHeight(newSize);
34592                 if(onComplete){
34593                     onComplete(s, newSize);
34594                 }
34595             }else{
34596                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34597             }
34598         }
34599     }
34600 };
34601
34602 /** 
34603  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34604  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34605  * Adapter that  moves the splitter element to align with the resized sizing element. 
34606  * Used with an absolute positioned SplitBar.
34607  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34608  * document.body, make sure you assign an id to the body element.
34609  */
34610 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34611     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34612     this.container = Roo.get(container);
34613 };
34614
34615 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34616     init : function(s){
34617         this.basic.init(s);
34618     },
34619     
34620     getElementSize : function(s){
34621         return this.basic.getElementSize(s);
34622     },
34623     
34624     setElementSize : function(s, newSize, onComplete){
34625         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34626     },
34627     
34628     moveSplitter : function(s){
34629         var yes = Roo.bootstrap.SplitBar;
34630         switch(s.placement){
34631             case yes.LEFT:
34632                 s.el.setX(s.resizingEl.getRight());
34633                 break;
34634             case yes.RIGHT:
34635                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34636                 break;
34637             case yes.TOP:
34638                 s.el.setY(s.resizingEl.getBottom());
34639                 break;
34640             case yes.BOTTOM:
34641                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34642                 break;
34643         }
34644     }
34645 };
34646
34647 /**
34648  * Orientation constant - Create a vertical SplitBar
34649  * @static
34650  * @type Number
34651  */
34652 Roo.bootstrap.SplitBar.VERTICAL = 1;
34653
34654 /**
34655  * Orientation constant - Create a horizontal SplitBar
34656  * @static
34657  * @type Number
34658  */
34659 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34660
34661 /**
34662  * Placement constant - The resizing element is to the left of the splitter element
34663  * @static
34664  * @type Number
34665  */
34666 Roo.bootstrap.SplitBar.LEFT = 1;
34667
34668 /**
34669  * Placement constant - The resizing element is to the right of the splitter element
34670  * @static
34671  * @type Number
34672  */
34673 Roo.bootstrap.SplitBar.RIGHT = 2;
34674
34675 /**
34676  * Placement constant - The resizing element is positioned above the splitter element
34677  * @static
34678  * @type Number
34679  */
34680 Roo.bootstrap.SplitBar.TOP = 3;
34681
34682 /**
34683  * Placement constant - The resizing element is positioned under splitter element
34684  * @static
34685  * @type Number
34686  */
34687 Roo.bootstrap.SplitBar.BOTTOM = 4;
34688 Roo.namespace("Roo.bootstrap.layout");/*
34689  * Based on:
34690  * Ext JS Library 1.1.1
34691  * Copyright(c) 2006-2007, Ext JS, LLC.
34692  *
34693  * Originally Released Under LGPL - original licence link has changed is not relivant.
34694  *
34695  * Fork - LGPL
34696  * <script type="text/javascript">
34697  */
34698
34699 /**
34700  * @class Roo.bootstrap.layout.Manager
34701  * @extends Roo.bootstrap.Component
34702  * Base class for layout managers.
34703  */
34704 Roo.bootstrap.layout.Manager = function(config)
34705 {
34706     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34707
34708
34709
34710
34711
34712     /** false to disable window resize monitoring @type Boolean */
34713     this.monitorWindowResize = true;
34714     this.regions = {};
34715     this.addEvents({
34716         /**
34717          * @event layout
34718          * Fires when a layout is performed.
34719          * @param {Roo.LayoutManager} this
34720          */
34721         "layout" : true,
34722         /**
34723          * @event regionresized
34724          * Fires when the user resizes a region.
34725          * @param {Roo.LayoutRegion} region The resized region
34726          * @param {Number} newSize The new size (width for east/west, height for north/south)
34727          */
34728         "regionresized" : true,
34729         /**
34730          * @event regioncollapsed
34731          * Fires when a region is collapsed.
34732          * @param {Roo.LayoutRegion} region The collapsed region
34733          */
34734         "regioncollapsed" : true,
34735         /**
34736          * @event regionexpanded
34737          * Fires when a region is expanded.
34738          * @param {Roo.LayoutRegion} region The expanded region
34739          */
34740         "regionexpanded" : true
34741     });
34742     this.updating = false;
34743
34744     if (config.el) {
34745         this.el = Roo.get(config.el);
34746         this.initEvents();
34747     }
34748
34749 };
34750
34751 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34752
34753
34754     regions : null,
34755
34756     monitorWindowResize : true,
34757
34758
34759     updating : false,
34760
34761
34762     onRender : function(ct, position)
34763     {
34764         if(!this.el){
34765             this.el = Roo.get(ct);
34766             this.initEvents();
34767         }
34768         //this.fireEvent('render',this);
34769     },
34770
34771
34772     initEvents: function()
34773     {
34774
34775
34776         // ie scrollbar fix
34777         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34778             document.body.scroll = "no";
34779         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34780             this.el.position('relative');
34781         }
34782         this.id = this.el.id;
34783         this.el.addClass("roo-layout-container");
34784         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34785         if(this.el.dom != document.body ) {
34786             this.el.on('resize', this.layout,this);
34787             this.el.on('show', this.layout,this);
34788         }
34789
34790     },
34791
34792     /**
34793      * Returns true if this layout is currently being updated
34794      * @return {Boolean}
34795      */
34796     isUpdating : function(){
34797         return this.updating;
34798     },
34799
34800     /**
34801      * Suspend the LayoutManager from doing auto-layouts while
34802      * making multiple add or remove calls
34803      */
34804     beginUpdate : function(){
34805         this.updating = true;
34806     },
34807
34808     /**
34809      * Restore auto-layouts and optionally disable the manager from performing a layout
34810      * @param {Boolean} noLayout true to disable a layout update
34811      */
34812     endUpdate : function(noLayout){
34813         this.updating = false;
34814         if(!noLayout){
34815             this.layout();
34816         }
34817     },
34818
34819     layout: function(){
34820         // abstract...
34821     },
34822
34823     onRegionResized : function(region, newSize){
34824         this.fireEvent("regionresized", region, newSize);
34825         this.layout();
34826     },
34827
34828     onRegionCollapsed : function(region){
34829         this.fireEvent("regioncollapsed", region);
34830     },
34831
34832     onRegionExpanded : function(region){
34833         this.fireEvent("regionexpanded", region);
34834     },
34835
34836     /**
34837      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34838      * performs box-model adjustments.
34839      * @return {Object} The size as an object {width: (the width), height: (the height)}
34840      */
34841     getViewSize : function()
34842     {
34843         var size;
34844         if(this.el.dom != document.body){
34845             size = this.el.getSize();
34846         }else{
34847             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34848         }
34849         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34850         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34851         return size;
34852     },
34853
34854     /**
34855      * Returns the Element this layout is bound to.
34856      * @return {Roo.Element}
34857      */
34858     getEl : function(){
34859         return this.el;
34860     },
34861
34862     /**
34863      * Returns the specified region.
34864      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34865      * @return {Roo.LayoutRegion}
34866      */
34867     getRegion : function(target){
34868         return this.regions[target.toLowerCase()];
34869     },
34870
34871     onWindowResize : function(){
34872         if(this.monitorWindowResize){
34873             this.layout();
34874         }
34875     }
34876 });
34877 /*
34878  * Based on:
34879  * Ext JS Library 1.1.1
34880  * Copyright(c) 2006-2007, Ext JS, LLC.
34881  *
34882  * Originally Released Under LGPL - original licence link has changed is not relivant.
34883  *
34884  * Fork - LGPL
34885  * <script type="text/javascript">
34886  */
34887 /**
34888  * @class Roo.bootstrap.layout.Border
34889  * @extends Roo.bootstrap.layout.Manager
34890  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34891  * please see: examples/bootstrap/nested.html<br><br>
34892  
34893 <b>The container the layout is rendered into can be either the body element or any other element.
34894 If it is not the body element, the container needs to either be an absolute positioned element,
34895 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34896 the container size if it is not the body element.</b>
34897
34898 * @constructor
34899 * Create a new Border
34900 * @param {Object} config Configuration options
34901  */
34902 Roo.bootstrap.layout.Border = function(config){
34903     config = config || {};
34904     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34905     
34906     
34907     
34908     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34909         if(config[region]){
34910             config[region].region = region;
34911             this.addRegion(config[region]);
34912         }
34913     },this);
34914     
34915 };
34916
34917 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34918
34919 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34920     /**
34921      * Creates and adds a new region if it doesn't already exist.
34922      * @param {String} target The target region key (north, south, east, west or center).
34923      * @param {Object} config The regions config object
34924      * @return {BorderLayoutRegion} The new region
34925      */
34926     addRegion : function(config)
34927     {
34928         if(!this.regions[config.region]){
34929             var r = this.factory(config);
34930             this.bindRegion(r);
34931         }
34932         return this.regions[config.region];
34933     },
34934
34935     // private (kinda)
34936     bindRegion : function(r){
34937         this.regions[r.config.region] = r;
34938         
34939         r.on("visibilitychange",    this.layout, this);
34940         r.on("paneladded",          this.layout, this);
34941         r.on("panelremoved",        this.layout, this);
34942         r.on("invalidated",         this.layout, this);
34943         r.on("resized",             this.onRegionResized, this);
34944         r.on("collapsed",           this.onRegionCollapsed, this);
34945         r.on("expanded",            this.onRegionExpanded, this);
34946     },
34947
34948     /**
34949      * Performs a layout update.
34950      */
34951     layout : function()
34952     {
34953         if(this.updating) {
34954             return;
34955         }
34956         
34957         // render all the rebions if they have not been done alreayd?
34958         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34959             if(this.regions[region] && !this.regions[region].bodyEl){
34960                 this.regions[region].onRender(this.el)
34961             }
34962         },this);
34963         
34964         var size = this.getViewSize();
34965         var w = size.width;
34966         var h = size.height;
34967         var centerW = w;
34968         var centerH = h;
34969         var centerY = 0;
34970         var centerX = 0;
34971         //var x = 0, y = 0;
34972
34973         var rs = this.regions;
34974         var north = rs["north"];
34975         var south = rs["south"]; 
34976         var west = rs["west"];
34977         var east = rs["east"];
34978         var center = rs["center"];
34979         //if(this.hideOnLayout){ // not supported anymore
34980             //c.el.setStyle("display", "none");
34981         //}
34982         if(north && north.isVisible()){
34983             var b = north.getBox();
34984             var m = north.getMargins();
34985             b.width = w - (m.left+m.right);
34986             b.x = m.left;
34987             b.y = m.top;
34988             centerY = b.height + b.y + m.bottom;
34989             centerH -= centerY;
34990             north.updateBox(this.safeBox(b));
34991         }
34992         if(south && south.isVisible()){
34993             var b = south.getBox();
34994             var m = south.getMargins();
34995             b.width = w - (m.left+m.right);
34996             b.x = m.left;
34997             var totalHeight = (b.height + m.top + m.bottom);
34998             b.y = h - totalHeight + m.top;
34999             centerH -= totalHeight;
35000             south.updateBox(this.safeBox(b));
35001         }
35002         if(west && west.isVisible()){
35003             var b = west.getBox();
35004             var m = west.getMargins();
35005             b.height = centerH - (m.top+m.bottom);
35006             b.x = m.left;
35007             b.y = centerY + m.top;
35008             var totalWidth = (b.width + m.left + m.right);
35009             centerX += totalWidth;
35010             centerW -= totalWidth;
35011             west.updateBox(this.safeBox(b));
35012         }
35013         if(east && east.isVisible()){
35014             var b = east.getBox();
35015             var m = east.getMargins();
35016             b.height = centerH - (m.top+m.bottom);
35017             var totalWidth = (b.width + m.left + m.right);
35018             b.x = w - totalWidth + m.left;
35019             b.y = centerY + m.top;
35020             centerW -= totalWidth;
35021             east.updateBox(this.safeBox(b));
35022         }
35023         if(center){
35024             var m = center.getMargins();
35025             var centerBox = {
35026                 x: centerX + m.left,
35027                 y: centerY + m.top,
35028                 width: centerW - (m.left+m.right),
35029                 height: centerH - (m.top+m.bottom)
35030             };
35031             //if(this.hideOnLayout){
35032                 //center.el.setStyle("display", "block");
35033             //}
35034             center.updateBox(this.safeBox(centerBox));
35035         }
35036         this.el.repaint();
35037         this.fireEvent("layout", this);
35038     },
35039
35040     // private
35041     safeBox : function(box){
35042         box.width = Math.max(0, box.width);
35043         box.height = Math.max(0, box.height);
35044         return box;
35045     },
35046
35047     /**
35048      * Adds a ContentPanel (or subclass) to this layout.
35049      * @param {String} target The target region key (north, south, east, west or center).
35050      * @param {Roo.ContentPanel} panel The panel to add
35051      * @return {Roo.ContentPanel} The added panel
35052      */
35053     add : function(target, panel){
35054          
35055         target = target.toLowerCase();
35056         return this.regions[target].add(panel);
35057     },
35058
35059     /**
35060      * Remove a ContentPanel (or subclass) to this layout.
35061      * @param {String} target The target region key (north, south, east, west or center).
35062      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35063      * @return {Roo.ContentPanel} The removed panel
35064      */
35065     remove : function(target, panel){
35066         target = target.toLowerCase();
35067         return this.regions[target].remove(panel);
35068     },
35069
35070     /**
35071      * Searches all regions for a panel with the specified id
35072      * @param {String} panelId
35073      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35074      */
35075     findPanel : function(panelId){
35076         var rs = this.regions;
35077         for(var target in rs){
35078             if(typeof rs[target] != "function"){
35079                 var p = rs[target].getPanel(panelId);
35080                 if(p){
35081                     return p;
35082                 }
35083             }
35084         }
35085         return null;
35086     },
35087
35088     /**
35089      * Searches all regions for a panel with the specified id and activates (shows) it.
35090      * @param {String/ContentPanel} panelId The panels id or the panel itself
35091      * @return {Roo.ContentPanel} The shown panel or null
35092      */
35093     showPanel : function(panelId) {
35094       var rs = this.regions;
35095       for(var target in rs){
35096          var r = rs[target];
35097          if(typeof r != "function"){
35098             if(r.hasPanel(panelId)){
35099                return r.showPanel(panelId);
35100             }
35101          }
35102       }
35103       return null;
35104    },
35105
35106    /**
35107      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35108      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35109      */
35110    /*
35111     restoreState : function(provider){
35112         if(!provider){
35113             provider = Roo.state.Manager;
35114         }
35115         var sm = new Roo.LayoutStateManager();
35116         sm.init(this, provider);
35117     },
35118 */
35119  
35120  
35121     /**
35122      * Adds a xtype elements to the layout.
35123      * <pre><code>
35124
35125 layout.addxtype({
35126        xtype : 'ContentPanel',
35127        region: 'west',
35128        items: [ .... ]
35129    }
35130 );
35131
35132 layout.addxtype({
35133         xtype : 'NestedLayoutPanel',
35134         region: 'west',
35135         layout: {
35136            center: { },
35137            west: { }   
35138         },
35139         items : [ ... list of content panels or nested layout panels.. ]
35140    }
35141 );
35142 </code></pre>
35143      * @param {Object} cfg Xtype definition of item to add.
35144      */
35145     addxtype : function(cfg)
35146     {
35147         // basically accepts a pannel...
35148         // can accept a layout region..!?!?
35149         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35150         
35151         
35152         // theory?  children can only be panels??
35153         
35154         //if (!cfg.xtype.match(/Panel$/)) {
35155         //    return false;
35156         //}
35157         var ret = false;
35158         
35159         if (typeof(cfg.region) == 'undefined') {
35160             Roo.log("Failed to add Panel, region was not set");
35161             Roo.log(cfg);
35162             return false;
35163         }
35164         var region = cfg.region;
35165         delete cfg.region;
35166         
35167           
35168         var xitems = [];
35169         if (cfg.items) {
35170             xitems = cfg.items;
35171             delete cfg.items;
35172         }
35173         var nb = false;
35174         
35175         switch(cfg.xtype) 
35176         {
35177             case 'Content':  // ContentPanel (el, cfg)
35178             case 'Scroll':  // ContentPanel (el, cfg)
35179             case 'View': 
35180                 cfg.autoCreate = true;
35181                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35182                 //} else {
35183                 //    var el = this.el.createChild();
35184                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35185                 //}
35186                 
35187                 this.add(region, ret);
35188                 break;
35189             
35190             /*
35191             case 'TreePanel': // our new panel!
35192                 cfg.el = this.el.createChild();
35193                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35194                 this.add(region, ret);
35195                 break;
35196             */
35197             
35198             case 'Nest': 
35199                 // create a new Layout (which is  a Border Layout...
35200                 
35201                 var clayout = cfg.layout;
35202                 clayout.el  = this.el.createChild();
35203                 clayout.items   = clayout.items  || [];
35204                 
35205                 delete cfg.layout;
35206                 
35207                 // replace this exitems with the clayout ones..
35208                 xitems = clayout.items;
35209                  
35210                 // force background off if it's in center...
35211                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35212                     cfg.background = false;
35213                 }
35214                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35215                 
35216                 
35217                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35218                 //console.log('adding nested layout panel '  + cfg.toSource());
35219                 this.add(region, ret);
35220                 nb = {}; /// find first...
35221                 break;
35222             
35223             case 'Grid':
35224                 
35225                 // needs grid and region
35226                 
35227                 //var el = this.getRegion(region).el.createChild();
35228                 /*
35229                  *var el = this.el.createChild();
35230                 // create the grid first...
35231                 cfg.grid.container = el;
35232                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35233                 */
35234                 
35235                 if (region == 'center' && this.active ) {
35236                     cfg.background = false;
35237                 }
35238                 
35239                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35240                 
35241                 this.add(region, ret);
35242                 /*
35243                 if (cfg.background) {
35244                     // render grid on panel activation (if panel background)
35245                     ret.on('activate', function(gp) {
35246                         if (!gp.grid.rendered) {
35247                     //        gp.grid.render(el);
35248                         }
35249                     });
35250                 } else {
35251                   //  cfg.grid.render(el);
35252                 }
35253                 */
35254                 break;
35255            
35256            
35257             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35258                 // it was the old xcomponent building that caused this before.
35259                 // espeically if border is the top element in the tree.
35260                 ret = this;
35261                 break; 
35262                 
35263                     
35264                 
35265                 
35266                 
35267             default:
35268                 /*
35269                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35270                     
35271                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35272                     this.add(region, ret);
35273                 } else {
35274                 */
35275                     Roo.log(cfg);
35276                     throw "Can not add '" + cfg.xtype + "' to Border";
35277                     return null;
35278              
35279                                 
35280              
35281         }
35282         this.beginUpdate();
35283         // add children..
35284         var region = '';
35285         var abn = {};
35286         Roo.each(xitems, function(i)  {
35287             region = nb && i.region ? i.region : false;
35288             
35289             var add = ret.addxtype(i);
35290            
35291             if (region) {
35292                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35293                 if (!i.background) {
35294                     abn[region] = nb[region] ;
35295                 }
35296             }
35297             
35298         });
35299         this.endUpdate();
35300
35301         // make the last non-background panel active..
35302         //if (nb) { Roo.log(abn); }
35303         if (nb) {
35304             
35305             for(var r in abn) {
35306                 region = this.getRegion(r);
35307                 if (region) {
35308                     // tried using nb[r], but it does not work..
35309                      
35310                     region.showPanel(abn[r]);
35311                    
35312                 }
35313             }
35314         }
35315         return ret;
35316         
35317     },
35318     
35319     
35320 // private
35321     factory : function(cfg)
35322     {
35323         
35324         var validRegions = Roo.bootstrap.layout.Border.regions;
35325
35326         var target = cfg.region;
35327         cfg.mgr = this;
35328         
35329         var r = Roo.bootstrap.layout;
35330         Roo.log(target);
35331         switch(target){
35332             case "north":
35333                 return new r.North(cfg);
35334             case "south":
35335                 return new r.South(cfg);
35336             case "east":
35337                 return new r.East(cfg);
35338             case "west":
35339                 return new r.West(cfg);
35340             case "center":
35341                 return new r.Center(cfg);
35342         }
35343         throw 'Layout region "'+target+'" not supported.';
35344     }
35345     
35346     
35347 });
35348  /*
35349  * Based on:
35350  * Ext JS Library 1.1.1
35351  * Copyright(c) 2006-2007, Ext JS, LLC.
35352  *
35353  * Originally Released Under LGPL - original licence link has changed is not relivant.
35354  *
35355  * Fork - LGPL
35356  * <script type="text/javascript">
35357  */
35358  
35359 /**
35360  * @class Roo.bootstrap.layout.Basic
35361  * @extends Roo.util.Observable
35362  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35363  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35364  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35365  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35366  * @cfg {string}   region  the region that it inhabits..
35367  * @cfg {bool}   skipConfig skip config?
35368  * 
35369
35370  */
35371 Roo.bootstrap.layout.Basic = function(config){
35372     
35373     this.mgr = config.mgr;
35374     
35375     this.position = config.region;
35376     
35377     var skipConfig = config.skipConfig;
35378     
35379     this.events = {
35380         /**
35381          * @scope Roo.BasicLayoutRegion
35382          */
35383         
35384         /**
35385          * @event beforeremove
35386          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35387          * @param {Roo.LayoutRegion} this
35388          * @param {Roo.ContentPanel} panel The panel
35389          * @param {Object} e The cancel event object
35390          */
35391         "beforeremove" : true,
35392         /**
35393          * @event invalidated
35394          * Fires when the layout for this region is changed.
35395          * @param {Roo.LayoutRegion} this
35396          */
35397         "invalidated" : true,
35398         /**
35399          * @event visibilitychange
35400          * Fires when this region is shown or hidden 
35401          * @param {Roo.LayoutRegion} this
35402          * @param {Boolean} visibility true or false
35403          */
35404         "visibilitychange" : true,
35405         /**
35406          * @event paneladded
35407          * Fires when a panel is added. 
35408          * @param {Roo.LayoutRegion} this
35409          * @param {Roo.ContentPanel} panel The panel
35410          */
35411         "paneladded" : true,
35412         /**
35413          * @event panelremoved
35414          * Fires when a panel is removed. 
35415          * @param {Roo.LayoutRegion} this
35416          * @param {Roo.ContentPanel} panel The panel
35417          */
35418         "panelremoved" : true,
35419         /**
35420          * @event beforecollapse
35421          * Fires when this region before collapse.
35422          * @param {Roo.LayoutRegion} this
35423          */
35424         "beforecollapse" : true,
35425         /**
35426          * @event collapsed
35427          * Fires when this region is collapsed.
35428          * @param {Roo.LayoutRegion} this
35429          */
35430         "collapsed" : true,
35431         /**
35432          * @event expanded
35433          * Fires when this region is expanded.
35434          * @param {Roo.LayoutRegion} this
35435          */
35436         "expanded" : true,
35437         /**
35438          * @event slideshow
35439          * Fires when this region is slid into view.
35440          * @param {Roo.LayoutRegion} this
35441          */
35442         "slideshow" : true,
35443         /**
35444          * @event slidehide
35445          * Fires when this region slides out of view. 
35446          * @param {Roo.LayoutRegion} this
35447          */
35448         "slidehide" : true,
35449         /**
35450          * @event panelactivated
35451          * Fires when a panel is activated. 
35452          * @param {Roo.LayoutRegion} this
35453          * @param {Roo.ContentPanel} panel The activated panel
35454          */
35455         "panelactivated" : true,
35456         /**
35457          * @event resized
35458          * Fires when the user resizes this region. 
35459          * @param {Roo.LayoutRegion} this
35460          * @param {Number} newSize The new size (width for east/west, height for north/south)
35461          */
35462         "resized" : true
35463     };
35464     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35465     this.panels = new Roo.util.MixedCollection();
35466     this.panels.getKey = this.getPanelId.createDelegate(this);
35467     this.box = null;
35468     this.activePanel = null;
35469     // ensure listeners are added...
35470     
35471     if (config.listeners || config.events) {
35472         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35473             listeners : config.listeners || {},
35474             events : config.events || {}
35475         });
35476     }
35477     
35478     if(skipConfig !== true){
35479         this.applyConfig(config);
35480     }
35481 };
35482
35483 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35484 {
35485     getPanelId : function(p){
35486         return p.getId();
35487     },
35488     
35489     applyConfig : function(config){
35490         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35491         this.config = config;
35492         
35493     },
35494     
35495     /**
35496      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35497      * the width, for horizontal (north, south) the height.
35498      * @param {Number} newSize The new width or height
35499      */
35500     resizeTo : function(newSize){
35501         var el = this.el ? this.el :
35502                  (this.activePanel ? this.activePanel.getEl() : null);
35503         if(el){
35504             switch(this.position){
35505                 case "east":
35506                 case "west":
35507                     el.setWidth(newSize);
35508                     this.fireEvent("resized", this, newSize);
35509                 break;
35510                 case "north":
35511                 case "south":
35512                     el.setHeight(newSize);
35513                     this.fireEvent("resized", this, newSize);
35514                 break;                
35515             }
35516         }
35517     },
35518     
35519     getBox : function(){
35520         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35521     },
35522     
35523     getMargins : function(){
35524         return this.margins;
35525     },
35526     
35527     updateBox : function(box){
35528         this.box = box;
35529         var el = this.activePanel.getEl();
35530         el.dom.style.left = box.x + "px";
35531         el.dom.style.top = box.y + "px";
35532         this.activePanel.setSize(box.width, box.height);
35533     },
35534     
35535     /**
35536      * Returns the container element for this region.
35537      * @return {Roo.Element}
35538      */
35539     getEl : function(){
35540         return this.activePanel;
35541     },
35542     
35543     /**
35544      * Returns true if this region is currently visible.
35545      * @return {Boolean}
35546      */
35547     isVisible : function(){
35548         return this.activePanel ? true : false;
35549     },
35550     
35551     setActivePanel : function(panel){
35552         panel = this.getPanel(panel);
35553         if(this.activePanel && this.activePanel != panel){
35554             this.activePanel.setActiveState(false);
35555             this.activePanel.getEl().setLeftTop(-10000,-10000);
35556         }
35557         this.activePanel = panel;
35558         panel.setActiveState(true);
35559         if(this.box){
35560             panel.setSize(this.box.width, this.box.height);
35561         }
35562         this.fireEvent("panelactivated", this, panel);
35563         this.fireEvent("invalidated");
35564     },
35565     
35566     /**
35567      * Show the specified panel.
35568      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35569      * @return {Roo.ContentPanel} The shown panel or null
35570      */
35571     showPanel : function(panel){
35572         panel = this.getPanel(panel);
35573         if(panel){
35574             this.setActivePanel(panel);
35575         }
35576         return panel;
35577     },
35578     
35579     /**
35580      * Get the active panel for this region.
35581      * @return {Roo.ContentPanel} The active panel or null
35582      */
35583     getActivePanel : function(){
35584         return this.activePanel;
35585     },
35586     
35587     /**
35588      * Add the passed ContentPanel(s)
35589      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35590      * @return {Roo.ContentPanel} The panel added (if only one was added)
35591      */
35592     add : function(panel){
35593         if(arguments.length > 1){
35594             for(var i = 0, len = arguments.length; i < len; i++) {
35595                 this.add(arguments[i]);
35596             }
35597             return null;
35598         }
35599         if(this.hasPanel(panel)){
35600             this.showPanel(panel);
35601             return panel;
35602         }
35603         var el = panel.getEl();
35604         if(el.dom.parentNode != this.mgr.el.dom){
35605             this.mgr.el.dom.appendChild(el.dom);
35606         }
35607         if(panel.setRegion){
35608             panel.setRegion(this);
35609         }
35610         this.panels.add(panel);
35611         el.setStyle("position", "absolute");
35612         if(!panel.background){
35613             this.setActivePanel(panel);
35614             if(this.config.initialSize && this.panels.getCount()==1){
35615                 this.resizeTo(this.config.initialSize);
35616             }
35617         }
35618         this.fireEvent("paneladded", this, panel);
35619         return panel;
35620     },
35621     
35622     /**
35623      * Returns true if the panel is in this region.
35624      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35625      * @return {Boolean}
35626      */
35627     hasPanel : function(panel){
35628         if(typeof panel == "object"){ // must be panel obj
35629             panel = panel.getId();
35630         }
35631         return this.getPanel(panel) ? true : false;
35632     },
35633     
35634     /**
35635      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35636      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35637      * @param {Boolean} preservePanel Overrides the config preservePanel option
35638      * @return {Roo.ContentPanel} The panel that was removed
35639      */
35640     remove : function(panel, preservePanel){
35641         panel = this.getPanel(panel);
35642         if(!panel){
35643             return null;
35644         }
35645         var e = {};
35646         this.fireEvent("beforeremove", this, panel, e);
35647         if(e.cancel === true){
35648             return null;
35649         }
35650         var panelId = panel.getId();
35651         this.panels.removeKey(panelId);
35652         return panel;
35653     },
35654     
35655     /**
35656      * Returns the panel specified or null if it's not in this region.
35657      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35658      * @return {Roo.ContentPanel}
35659      */
35660     getPanel : function(id){
35661         if(typeof id == "object"){ // must be panel obj
35662             return id;
35663         }
35664         return this.panels.get(id);
35665     },
35666     
35667     /**
35668      * Returns this regions position (north/south/east/west/center).
35669      * @return {String} 
35670      */
35671     getPosition: function(){
35672         return this.position;    
35673     }
35674 });/*
35675  * Based on:
35676  * Ext JS Library 1.1.1
35677  * Copyright(c) 2006-2007, Ext JS, LLC.
35678  *
35679  * Originally Released Under LGPL - original licence link has changed is not relivant.
35680  *
35681  * Fork - LGPL
35682  * <script type="text/javascript">
35683  */
35684  
35685 /**
35686  * @class Roo.bootstrap.layout.Region
35687  * @extends Roo.bootstrap.layout.Basic
35688  * This class represents a region in a layout manager.
35689  
35690  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35691  * @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})
35692  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35693  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35694  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35695  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35696  * @cfg {String}    title           The title for the region (overrides panel titles)
35697  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35698  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35699  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35700  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35701  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35702  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35703  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35704  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35705  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35706  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35707
35708  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35709  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35710  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35711  * @cfg {Number}    width           For East/West panels
35712  * @cfg {Number}    height          For North/South panels
35713  * @cfg {Boolean}   split           To show the splitter
35714  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35715  * 
35716  * @cfg {string}   cls             Extra CSS classes to add to region
35717  * 
35718  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35719  * @cfg {string}   region  the region that it inhabits..
35720  *
35721
35722  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35723  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35724
35725  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35726  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35727  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35728  */
35729 Roo.bootstrap.layout.Region = function(config)
35730 {
35731     this.applyConfig(config);
35732
35733     var mgr = config.mgr;
35734     var pos = config.region;
35735     config.skipConfig = true;
35736     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35737     
35738     if (mgr.el) {
35739         this.onRender(mgr.el);   
35740     }
35741      
35742     this.visible = true;
35743     this.collapsed = false;
35744     this.unrendered_panels = [];
35745 };
35746
35747 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35748
35749     position: '', // set by wrapper (eg. north/south etc..)
35750     unrendered_panels : null,  // unrendered panels.
35751     createBody : function(){
35752         /** This region's body element 
35753         * @type Roo.Element */
35754         this.bodyEl = this.el.createChild({
35755                 tag: "div",
35756                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35757         });
35758     },
35759
35760     onRender: function(ctr, pos)
35761     {
35762         var dh = Roo.DomHelper;
35763         /** This region's container element 
35764         * @type Roo.Element */
35765         this.el = dh.append(ctr.dom, {
35766                 tag: "div",
35767                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35768             }, true);
35769         /** This region's title element 
35770         * @type Roo.Element */
35771     
35772         this.titleEl = dh.append(this.el.dom,
35773             {
35774                     tag: "div",
35775                     unselectable: "on",
35776                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35777                     children:[
35778                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35779                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35780                     ]}, true);
35781         
35782         this.titleEl.enableDisplayMode();
35783         /** This region's title text element 
35784         * @type HTMLElement */
35785         this.titleTextEl = this.titleEl.dom.firstChild;
35786         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35787         /*
35788         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35789         this.closeBtn.enableDisplayMode();
35790         this.closeBtn.on("click", this.closeClicked, this);
35791         this.closeBtn.hide();
35792     */
35793         this.createBody(this.config);
35794         if(this.config.hideWhenEmpty){
35795             this.hide();
35796             this.on("paneladded", this.validateVisibility, this);
35797             this.on("panelremoved", this.validateVisibility, this);
35798         }
35799         if(this.autoScroll){
35800             this.bodyEl.setStyle("overflow", "auto");
35801         }else{
35802             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35803         }
35804         //if(c.titlebar !== false){
35805             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35806                 this.titleEl.hide();
35807             }else{
35808                 this.titleEl.show();
35809                 if(this.config.title){
35810                     this.titleTextEl.innerHTML = this.config.title;
35811                 }
35812             }
35813         //}
35814         if(this.config.collapsed){
35815             this.collapse(true);
35816         }
35817         if(this.config.hidden){
35818             this.hide();
35819         }
35820         
35821         if (this.unrendered_panels && this.unrendered_panels.length) {
35822             for (var i =0;i< this.unrendered_panels.length; i++) {
35823                 this.add(this.unrendered_panels[i]);
35824             }
35825             this.unrendered_panels = null;
35826             
35827         }
35828         
35829     },
35830     
35831     applyConfig : function(c)
35832     {
35833         /*
35834          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35835             var dh = Roo.DomHelper;
35836             if(c.titlebar !== false){
35837                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35838                 this.collapseBtn.on("click", this.collapse, this);
35839                 this.collapseBtn.enableDisplayMode();
35840                 /*
35841                 if(c.showPin === true || this.showPin){
35842                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35843                     this.stickBtn.enableDisplayMode();
35844                     this.stickBtn.on("click", this.expand, this);
35845                     this.stickBtn.hide();
35846                 }
35847                 
35848             }
35849             */
35850             /** This region's collapsed element
35851             * @type Roo.Element */
35852             /*
35853              *
35854             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35855                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35856             ]}, true);
35857             
35858             if(c.floatable !== false){
35859                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35860                this.collapsedEl.on("click", this.collapseClick, this);
35861             }
35862
35863             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35864                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35865                    id: "message", unselectable: "on", style:{"float":"left"}});
35866                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35867              }
35868             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35869             this.expandBtn.on("click", this.expand, this);
35870             
35871         }
35872         
35873         if(this.collapseBtn){
35874             this.collapseBtn.setVisible(c.collapsible == true);
35875         }
35876         
35877         this.cmargins = c.cmargins || this.cmargins ||
35878                          (this.position == "west" || this.position == "east" ?
35879                              {top: 0, left: 2, right:2, bottom: 0} :
35880                              {top: 2, left: 0, right:0, bottom: 2});
35881         */
35882         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35883         
35884         
35885         this.bottomTabs = c.tabPosition != "top";
35886         
35887         this.autoScroll = c.autoScroll || false;
35888         
35889         
35890        
35891         
35892         this.duration = c.duration || .30;
35893         this.slideDuration = c.slideDuration || .45;
35894         this.config = c;
35895        
35896     },
35897     /**
35898      * Returns true if this region is currently visible.
35899      * @return {Boolean}
35900      */
35901     isVisible : function(){
35902         return this.visible;
35903     },
35904
35905     /**
35906      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35907      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35908      */
35909     //setCollapsedTitle : function(title){
35910     //    title = title || "&#160;";
35911      //   if(this.collapsedTitleTextEl){
35912       //      this.collapsedTitleTextEl.innerHTML = title;
35913        // }
35914     //},
35915
35916     getBox : function(){
35917         var b;
35918       //  if(!this.collapsed){
35919             b = this.el.getBox(false, true);
35920        // }else{
35921           //  b = this.collapsedEl.getBox(false, true);
35922         //}
35923         return b;
35924     },
35925
35926     getMargins : function(){
35927         return this.margins;
35928         //return this.collapsed ? this.cmargins : this.margins;
35929     },
35930 /*
35931     highlight : function(){
35932         this.el.addClass("x-layout-panel-dragover");
35933     },
35934
35935     unhighlight : function(){
35936         this.el.removeClass("x-layout-panel-dragover");
35937     },
35938 */
35939     updateBox : function(box)
35940     {
35941         if (!this.bodyEl) {
35942             return; // not rendered yet..
35943         }
35944         
35945         this.box = box;
35946         if(!this.collapsed){
35947             this.el.dom.style.left = box.x + "px";
35948             this.el.dom.style.top = box.y + "px";
35949             this.updateBody(box.width, box.height);
35950         }else{
35951             this.collapsedEl.dom.style.left = box.x + "px";
35952             this.collapsedEl.dom.style.top = box.y + "px";
35953             this.collapsedEl.setSize(box.width, box.height);
35954         }
35955         if(this.tabs){
35956             this.tabs.autoSizeTabs();
35957         }
35958     },
35959
35960     updateBody : function(w, h)
35961     {
35962         if(w !== null){
35963             this.el.setWidth(w);
35964             w -= this.el.getBorderWidth("rl");
35965             if(this.config.adjustments){
35966                 w += this.config.adjustments[0];
35967             }
35968         }
35969         if(h !== null && h > 0){
35970             this.el.setHeight(h);
35971             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35972             h -= this.el.getBorderWidth("tb");
35973             if(this.config.adjustments){
35974                 h += this.config.adjustments[1];
35975             }
35976             this.bodyEl.setHeight(h);
35977             if(this.tabs){
35978                 h = this.tabs.syncHeight(h);
35979             }
35980         }
35981         if(this.panelSize){
35982             w = w !== null ? w : this.panelSize.width;
35983             h = h !== null ? h : this.panelSize.height;
35984         }
35985         if(this.activePanel){
35986             var el = this.activePanel.getEl();
35987             w = w !== null ? w : el.getWidth();
35988             h = h !== null ? h : el.getHeight();
35989             this.panelSize = {width: w, height: h};
35990             this.activePanel.setSize(w, h);
35991         }
35992         if(Roo.isIE && this.tabs){
35993             this.tabs.el.repaint();
35994         }
35995     },
35996
35997     /**
35998      * Returns the container element for this region.
35999      * @return {Roo.Element}
36000      */
36001     getEl : function(){
36002         return this.el;
36003     },
36004
36005     /**
36006      * Hides this region.
36007      */
36008     hide : function(){
36009         //if(!this.collapsed){
36010             this.el.dom.style.left = "-2000px";
36011             this.el.hide();
36012         //}else{
36013          //   this.collapsedEl.dom.style.left = "-2000px";
36014          //   this.collapsedEl.hide();
36015        // }
36016         this.visible = false;
36017         this.fireEvent("visibilitychange", this, false);
36018     },
36019
36020     /**
36021      * Shows this region if it was previously hidden.
36022      */
36023     show : function(){
36024         //if(!this.collapsed){
36025             this.el.show();
36026         //}else{
36027         //    this.collapsedEl.show();
36028        // }
36029         this.visible = true;
36030         this.fireEvent("visibilitychange", this, true);
36031     },
36032 /*
36033     closeClicked : function(){
36034         if(this.activePanel){
36035             this.remove(this.activePanel);
36036         }
36037     },
36038
36039     collapseClick : function(e){
36040         if(this.isSlid){
36041            e.stopPropagation();
36042            this.slideIn();
36043         }else{
36044            e.stopPropagation();
36045            this.slideOut();
36046         }
36047     },
36048 */
36049     /**
36050      * Collapses this region.
36051      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36052      */
36053     /*
36054     collapse : function(skipAnim, skipCheck = false){
36055         if(this.collapsed) {
36056             return;
36057         }
36058         
36059         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36060             
36061             this.collapsed = true;
36062             if(this.split){
36063                 this.split.el.hide();
36064             }
36065             if(this.config.animate && skipAnim !== true){
36066                 this.fireEvent("invalidated", this);
36067                 this.animateCollapse();
36068             }else{
36069                 this.el.setLocation(-20000,-20000);
36070                 this.el.hide();
36071                 this.collapsedEl.show();
36072                 this.fireEvent("collapsed", this);
36073                 this.fireEvent("invalidated", this);
36074             }
36075         }
36076         
36077     },
36078 */
36079     animateCollapse : function(){
36080         // overridden
36081     },
36082
36083     /**
36084      * Expands this region if it was previously collapsed.
36085      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36086      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36087      */
36088     /*
36089     expand : function(e, skipAnim){
36090         if(e) {
36091             e.stopPropagation();
36092         }
36093         if(!this.collapsed || this.el.hasActiveFx()) {
36094             return;
36095         }
36096         if(this.isSlid){
36097             this.afterSlideIn();
36098             skipAnim = true;
36099         }
36100         this.collapsed = false;
36101         if(this.config.animate && skipAnim !== true){
36102             this.animateExpand();
36103         }else{
36104             this.el.show();
36105             if(this.split){
36106                 this.split.el.show();
36107             }
36108             this.collapsedEl.setLocation(-2000,-2000);
36109             this.collapsedEl.hide();
36110             this.fireEvent("invalidated", this);
36111             this.fireEvent("expanded", this);
36112         }
36113     },
36114 */
36115     animateExpand : function(){
36116         // overridden
36117     },
36118
36119     initTabs : function()
36120     {
36121         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36122         
36123         var ts = new Roo.bootstrap.panel.Tabs({
36124                 el: this.bodyEl.dom,
36125                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36126                 disableTooltips: this.config.disableTabTips,
36127                 toolbar : this.config.toolbar
36128             });
36129         
36130         if(this.config.hideTabs){
36131             ts.stripWrap.setDisplayed(false);
36132         }
36133         this.tabs = ts;
36134         ts.resizeTabs = this.config.resizeTabs === true;
36135         ts.minTabWidth = this.config.minTabWidth || 40;
36136         ts.maxTabWidth = this.config.maxTabWidth || 250;
36137         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36138         ts.monitorResize = false;
36139         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36140         ts.bodyEl.addClass('roo-layout-tabs-body');
36141         this.panels.each(this.initPanelAsTab, this);
36142     },
36143
36144     initPanelAsTab : function(panel){
36145         var ti = this.tabs.addTab(
36146             panel.getEl().id,
36147             panel.getTitle(),
36148             null,
36149             this.config.closeOnTab && panel.isClosable(),
36150             panel.tpl
36151         );
36152         if(panel.tabTip !== undefined){
36153             ti.setTooltip(panel.tabTip);
36154         }
36155         ti.on("activate", function(){
36156               this.setActivePanel(panel);
36157         }, this);
36158         
36159         if(this.config.closeOnTab){
36160             ti.on("beforeclose", function(t, e){
36161                 e.cancel = true;
36162                 this.remove(panel);
36163             }, this);
36164         }
36165         
36166         panel.tabItem = ti;
36167         
36168         return ti;
36169     },
36170
36171     updatePanelTitle : function(panel, title)
36172     {
36173         if(this.activePanel == panel){
36174             this.updateTitle(title);
36175         }
36176         if(this.tabs){
36177             var ti = this.tabs.getTab(panel.getEl().id);
36178             ti.setText(title);
36179             if(panel.tabTip !== undefined){
36180                 ti.setTooltip(panel.tabTip);
36181             }
36182         }
36183     },
36184
36185     updateTitle : function(title){
36186         if(this.titleTextEl && !this.config.title){
36187             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36188         }
36189     },
36190
36191     setActivePanel : function(panel)
36192     {
36193         panel = this.getPanel(panel);
36194         if(this.activePanel && this.activePanel != panel){
36195             if(this.activePanel.setActiveState(false) === false){
36196                 return;
36197             }
36198         }
36199         this.activePanel = panel;
36200         panel.setActiveState(true);
36201         if(this.panelSize){
36202             panel.setSize(this.panelSize.width, this.panelSize.height);
36203         }
36204         if(this.closeBtn){
36205             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36206         }
36207         this.updateTitle(panel.getTitle());
36208         if(this.tabs){
36209             this.fireEvent("invalidated", this);
36210         }
36211         this.fireEvent("panelactivated", this, panel);
36212     },
36213
36214     /**
36215      * Shows the specified panel.
36216      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36217      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36218      */
36219     showPanel : function(panel)
36220     {
36221         panel = this.getPanel(panel);
36222         if(panel){
36223             if(this.tabs){
36224                 var tab = this.tabs.getTab(panel.getEl().id);
36225                 if(tab.isHidden()){
36226                     this.tabs.unhideTab(tab.id);
36227                 }
36228                 tab.activate();
36229             }else{
36230                 this.setActivePanel(panel);
36231             }
36232         }
36233         return panel;
36234     },
36235
36236     /**
36237      * Get the active panel for this region.
36238      * @return {Roo.ContentPanel} The active panel or null
36239      */
36240     getActivePanel : function(){
36241         return this.activePanel;
36242     },
36243
36244     validateVisibility : function(){
36245         if(this.panels.getCount() < 1){
36246             this.updateTitle("&#160;");
36247             this.closeBtn.hide();
36248             this.hide();
36249         }else{
36250             if(!this.isVisible()){
36251                 this.show();
36252             }
36253         }
36254     },
36255
36256     /**
36257      * Adds the passed ContentPanel(s) to this region.
36258      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36259      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36260      */
36261     add : function(panel)
36262     {
36263         if(arguments.length > 1){
36264             for(var i = 0, len = arguments.length; i < len; i++) {
36265                 this.add(arguments[i]);
36266             }
36267             return null;
36268         }
36269         
36270         // if we have not been rendered yet, then we can not really do much of this..
36271         if (!this.bodyEl) {
36272             this.unrendered_panels.push(panel);
36273             return panel;
36274         }
36275         
36276         
36277         
36278         
36279         if(this.hasPanel(panel)){
36280             this.showPanel(panel);
36281             return panel;
36282         }
36283         panel.setRegion(this);
36284         this.panels.add(panel);
36285        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36286             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36287             // and hide them... ???
36288             this.bodyEl.dom.appendChild(panel.getEl().dom);
36289             if(panel.background !== true){
36290                 this.setActivePanel(panel);
36291             }
36292             this.fireEvent("paneladded", this, panel);
36293             return panel;
36294         }
36295         */
36296         if(!this.tabs){
36297             this.initTabs();
36298         }else{
36299             this.initPanelAsTab(panel);
36300         }
36301         
36302         
36303         if(panel.background !== true){
36304             this.tabs.activate(panel.getEl().id);
36305         }
36306         this.fireEvent("paneladded", this, panel);
36307         return panel;
36308     },
36309
36310     /**
36311      * Hides the tab for the specified panel.
36312      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36313      */
36314     hidePanel : function(panel){
36315         if(this.tabs && (panel = this.getPanel(panel))){
36316             this.tabs.hideTab(panel.getEl().id);
36317         }
36318     },
36319
36320     /**
36321      * Unhides the tab for a previously hidden panel.
36322      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36323      */
36324     unhidePanel : function(panel){
36325         if(this.tabs && (panel = this.getPanel(panel))){
36326             this.tabs.unhideTab(panel.getEl().id);
36327         }
36328     },
36329
36330     clearPanels : function(){
36331         while(this.panels.getCount() > 0){
36332              this.remove(this.panels.first());
36333         }
36334     },
36335
36336     /**
36337      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36338      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36339      * @param {Boolean} preservePanel Overrides the config preservePanel option
36340      * @return {Roo.ContentPanel} The panel that was removed
36341      */
36342     remove : function(panel, preservePanel)
36343     {
36344         panel = this.getPanel(panel);
36345         if(!panel){
36346             return null;
36347         }
36348         var e = {};
36349         this.fireEvent("beforeremove", this, panel, e);
36350         if(e.cancel === true){
36351             return null;
36352         }
36353         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36354         var panelId = panel.getId();
36355         this.panels.removeKey(panelId);
36356         if(preservePanel){
36357             document.body.appendChild(panel.getEl().dom);
36358         }
36359         if(this.tabs){
36360             this.tabs.removeTab(panel.getEl().id);
36361         }else if (!preservePanel){
36362             this.bodyEl.dom.removeChild(panel.getEl().dom);
36363         }
36364         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36365             var p = this.panels.first();
36366             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36367             tempEl.appendChild(p.getEl().dom);
36368             this.bodyEl.update("");
36369             this.bodyEl.dom.appendChild(p.getEl().dom);
36370             tempEl = null;
36371             this.updateTitle(p.getTitle());
36372             this.tabs = null;
36373             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36374             this.setActivePanel(p);
36375         }
36376         panel.setRegion(null);
36377         if(this.activePanel == panel){
36378             this.activePanel = null;
36379         }
36380         if(this.config.autoDestroy !== false && preservePanel !== true){
36381             try{panel.destroy();}catch(e){}
36382         }
36383         this.fireEvent("panelremoved", this, panel);
36384         return panel;
36385     },
36386
36387     /**
36388      * Returns the TabPanel component used by this region
36389      * @return {Roo.TabPanel}
36390      */
36391     getTabs : function(){
36392         return this.tabs;
36393     },
36394
36395     createTool : function(parentEl, className){
36396         var btn = Roo.DomHelper.append(parentEl, {
36397             tag: "div",
36398             cls: "x-layout-tools-button",
36399             children: [ {
36400                 tag: "div",
36401                 cls: "roo-layout-tools-button-inner " + className,
36402                 html: "&#160;"
36403             }]
36404         }, true);
36405         btn.addClassOnOver("roo-layout-tools-button-over");
36406         return btn;
36407     }
36408 });/*
36409  * Based on:
36410  * Ext JS Library 1.1.1
36411  * Copyright(c) 2006-2007, Ext JS, LLC.
36412  *
36413  * Originally Released Under LGPL - original licence link has changed is not relivant.
36414  *
36415  * Fork - LGPL
36416  * <script type="text/javascript">
36417  */
36418  
36419
36420
36421 /**
36422  * @class Roo.SplitLayoutRegion
36423  * @extends Roo.LayoutRegion
36424  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36425  */
36426 Roo.bootstrap.layout.Split = function(config){
36427     this.cursor = config.cursor;
36428     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36429 };
36430
36431 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36432 {
36433     splitTip : "Drag to resize.",
36434     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36435     useSplitTips : false,
36436
36437     applyConfig : function(config){
36438         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36439     },
36440     
36441     onRender : function(ctr,pos) {
36442         
36443         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36444         if(!this.config.split){
36445             return;
36446         }
36447         if(!this.split){
36448             
36449             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36450                             tag: "div",
36451                             id: this.el.id + "-split",
36452                             cls: "roo-layout-split roo-layout-split-"+this.position,
36453                             html: "&#160;"
36454             });
36455             /** The SplitBar for this region 
36456             * @type Roo.SplitBar */
36457             // does not exist yet...
36458             Roo.log([this.position, this.orientation]);
36459             
36460             this.split = new Roo.bootstrap.SplitBar({
36461                 dragElement : splitEl,
36462                 resizingElement: this.el,
36463                 orientation : this.orientation
36464             });
36465             
36466             this.split.on("moved", this.onSplitMove, this);
36467             this.split.useShim = this.config.useShim === true;
36468             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36469             if(this.useSplitTips){
36470                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36471             }
36472             //if(config.collapsible){
36473             //    this.split.el.on("dblclick", this.collapse,  this);
36474             //}
36475         }
36476         if(typeof this.config.minSize != "undefined"){
36477             this.split.minSize = this.config.minSize;
36478         }
36479         if(typeof this.config.maxSize != "undefined"){
36480             this.split.maxSize = this.config.maxSize;
36481         }
36482         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36483             this.hideSplitter();
36484         }
36485         
36486     },
36487
36488     getHMaxSize : function(){
36489          var cmax = this.config.maxSize || 10000;
36490          var center = this.mgr.getRegion("center");
36491          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36492     },
36493
36494     getVMaxSize : function(){
36495          var cmax = this.config.maxSize || 10000;
36496          var center = this.mgr.getRegion("center");
36497          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36498     },
36499
36500     onSplitMove : function(split, newSize){
36501         this.fireEvent("resized", this, newSize);
36502     },
36503     
36504     /** 
36505      * Returns the {@link Roo.SplitBar} for this region.
36506      * @return {Roo.SplitBar}
36507      */
36508     getSplitBar : function(){
36509         return this.split;
36510     },
36511     
36512     hide : function(){
36513         this.hideSplitter();
36514         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36515     },
36516
36517     hideSplitter : function(){
36518         if(this.split){
36519             this.split.el.setLocation(-2000,-2000);
36520             this.split.el.hide();
36521         }
36522     },
36523
36524     show : function(){
36525         if(this.split){
36526             this.split.el.show();
36527         }
36528         Roo.bootstrap.layout.Split.superclass.show.call(this);
36529     },
36530     
36531     beforeSlide: function(){
36532         if(Roo.isGecko){// firefox overflow auto bug workaround
36533             this.bodyEl.clip();
36534             if(this.tabs) {
36535                 this.tabs.bodyEl.clip();
36536             }
36537             if(this.activePanel){
36538                 this.activePanel.getEl().clip();
36539                 
36540                 if(this.activePanel.beforeSlide){
36541                     this.activePanel.beforeSlide();
36542                 }
36543             }
36544         }
36545     },
36546     
36547     afterSlide : function(){
36548         if(Roo.isGecko){// firefox overflow auto bug workaround
36549             this.bodyEl.unclip();
36550             if(this.tabs) {
36551                 this.tabs.bodyEl.unclip();
36552             }
36553             if(this.activePanel){
36554                 this.activePanel.getEl().unclip();
36555                 if(this.activePanel.afterSlide){
36556                     this.activePanel.afterSlide();
36557                 }
36558             }
36559         }
36560     },
36561
36562     initAutoHide : function(){
36563         if(this.autoHide !== false){
36564             if(!this.autoHideHd){
36565                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36566                 this.autoHideHd = {
36567                     "mouseout": function(e){
36568                         if(!e.within(this.el, true)){
36569                             st.delay(500);
36570                         }
36571                     },
36572                     "mouseover" : function(e){
36573                         st.cancel();
36574                     },
36575                     scope : this
36576                 };
36577             }
36578             this.el.on(this.autoHideHd);
36579         }
36580     },
36581
36582     clearAutoHide : function(){
36583         if(this.autoHide !== false){
36584             this.el.un("mouseout", this.autoHideHd.mouseout);
36585             this.el.un("mouseover", this.autoHideHd.mouseover);
36586         }
36587     },
36588
36589     clearMonitor : function(){
36590         Roo.get(document).un("click", this.slideInIf, this);
36591     },
36592
36593     // these names are backwards but not changed for compat
36594     slideOut : function(){
36595         if(this.isSlid || this.el.hasActiveFx()){
36596             return;
36597         }
36598         this.isSlid = true;
36599         if(this.collapseBtn){
36600             this.collapseBtn.hide();
36601         }
36602         this.closeBtnState = this.closeBtn.getStyle('display');
36603         this.closeBtn.hide();
36604         if(this.stickBtn){
36605             this.stickBtn.show();
36606         }
36607         this.el.show();
36608         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36609         this.beforeSlide();
36610         this.el.setStyle("z-index", 10001);
36611         this.el.slideIn(this.getSlideAnchor(), {
36612             callback: function(){
36613                 this.afterSlide();
36614                 this.initAutoHide();
36615                 Roo.get(document).on("click", this.slideInIf, this);
36616                 this.fireEvent("slideshow", this);
36617             },
36618             scope: this,
36619             block: true
36620         });
36621     },
36622
36623     afterSlideIn : function(){
36624         this.clearAutoHide();
36625         this.isSlid = false;
36626         this.clearMonitor();
36627         this.el.setStyle("z-index", "");
36628         if(this.collapseBtn){
36629             this.collapseBtn.show();
36630         }
36631         this.closeBtn.setStyle('display', this.closeBtnState);
36632         if(this.stickBtn){
36633             this.stickBtn.hide();
36634         }
36635         this.fireEvent("slidehide", this);
36636     },
36637
36638     slideIn : function(cb){
36639         if(!this.isSlid || this.el.hasActiveFx()){
36640             Roo.callback(cb);
36641             return;
36642         }
36643         this.isSlid = false;
36644         this.beforeSlide();
36645         this.el.slideOut(this.getSlideAnchor(), {
36646             callback: function(){
36647                 this.el.setLeftTop(-10000, -10000);
36648                 this.afterSlide();
36649                 this.afterSlideIn();
36650                 Roo.callback(cb);
36651             },
36652             scope: this,
36653             block: true
36654         });
36655     },
36656     
36657     slideInIf : function(e){
36658         if(!e.within(this.el)){
36659             this.slideIn();
36660         }
36661     },
36662
36663     animateCollapse : function(){
36664         this.beforeSlide();
36665         this.el.setStyle("z-index", 20000);
36666         var anchor = this.getSlideAnchor();
36667         this.el.slideOut(anchor, {
36668             callback : function(){
36669                 this.el.setStyle("z-index", "");
36670                 this.collapsedEl.slideIn(anchor, {duration:.3});
36671                 this.afterSlide();
36672                 this.el.setLocation(-10000,-10000);
36673                 this.el.hide();
36674                 this.fireEvent("collapsed", this);
36675             },
36676             scope: this,
36677             block: true
36678         });
36679     },
36680
36681     animateExpand : function(){
36682         this.beforeSlide();
36683         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36684         this.el.setStyle("z-index", 20000);
36685         this.collapsedEl.hide({
36686             duration:.1
36687         });
36688         this.el.slideIn(this.getSlideAnchor(), {
36689             callback : function(){
36690                 this.el.setStyle("z-index", "");
36691                 this.afterSlide();
36692                 if(this.split){
36693                     this.split.el.show();
36694                 }
36695                 this.fireEvent("invalidated", this);
36696                 this.fireEvent("expanded", this);
36697             },
36698             scope: this,
36699             block: true
36700         });
36701     },
36702
36703     anchors : {
36704         "west" : "left",
36705         "east" : "right",
36706         "north" : "top",
36707         "south" : "bottom"
36708     },
36709
36710     sanchors : {
36711         "west" : "l",
36712         "east" : "r",
36713         "north" : "t",
36714         "south" : "b"
36715     },
36716
36717     canchors : {
36718         "west" : "tl-tr",
36719         "east" : "tr-tl",
36720         "north" : "tl-bl",
36721         "south" : "bl-tl"
36722     },
36723
36724     getAnchor : function(){
36725         return this.anchors[this.position];
36726     },
36727
36728     getCollapseAnchor : function(){
36729         return this.canchors[this.position];
36730     },
36731
36732     getSlideAnchor : function(){
36733         return this.sanchors[this.position];
36734     },
36735
36736     getAlignAdj : function(){
36737         var cm = this.cmargins;
36738         switch(this.position){
36739             case "west":
36740                 return [0, 0];
36741             break;
36742             case "east":
36743                 return [0, 0];
36744             break;
36745             case "north":
36746                 return [0, 0];
36747             break;
36748             case "south":
36749                 return [0, 0];
36750             break;
36751         }
36752     },
36753
36754     getExpandAdj : function(){
36755         var c = this.collapsedEl, cm = this.cmargins;
36756         switch(this.position){
36757             case "west":
36758                 return [-(cm.right+c.getWidth()+cm.left), 0];
36759             break;
36760             case "east":
36761                 return [cm.right+c.getWidth()+cm.left, 0];
36762             break;
36763             case "north":
36764                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36765             break;
36766             case "south":
36767                 return [0, cm.top+cm.bottom+c.getHeight()];
36768             break;
36769         }
36770     }
36771 });/*
36772  * Based on:
36773  * Ext JS Library 1.1.1
36774  * Copyright(c) 2006-2007, Ext JS, LLC.
36775  *
36776  * Originally Released Under LGPL - original licence link has changed is not relivant.
36777  *
36778  * Fork - LGPL
36779  * <script type="text/javascript">
36780  */
36781 /*
36782  * These classes are private internal classes
36783  */
36784 Roo.bootstrap.layout.Center = function(config){
36785     config.region = "center";
36786     Roo.bootstrap.layout.Region.call(this, config);
36787     this.visible = true;
36788     this.minWidth = config.minWidth || 20;
36789     this.minHeight = config.minHeight || 20;
36790 };
36791
36792 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36793     hide : function(){
36794         // center panel can't be hidden
36795     },
36796     
36797     show : function(){
36798         // center panel can't be hidden
36799     },
36800     
36801     getMinWidth: function(){
36802         return this.minWidth;
36803     },
36804     
36805     getMinHeight: function(){
36806         return this.minHeight;
36807     }
36808 });
36809
36810
36811
36812
36813  
36814
36815
36816
36817
36818
36819 Roo.bootstrap.layout.North = function(config)
36820 {
36821     config.region = 'north';
36822     config.cursor = 'n-resize';
36823     
36824     Roo.bootstrap.layout.Split.call(this, config);
36825     
36826     
36827     if(this.split){
36828         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36829         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36830         this.split.el.addClass("roo-layout-split-v");
36831     }
36832     var size = config.initialSize || config.height;
36833     if(typeof size != "undefined"){
36834         this.el.setHeight(size);
36835     }
36836 };
36837 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36838 {
36839     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36840     
36841     
36842     
36843     getBox : function(){
36844         if(this.collapsed){
36845             return this.collapsedEl.getBox();
36846         }
36847         var box = this.el.getBox();
36848         if(this.split){
36849             box.height += this.split.el.getHeight();
36850         }
36851         return box;
36852     },
36853     
36854     updateBox : function(box){
36855         if(this.split && !this.collapsed){
36856             box.height -= this.split.el.getHeight();
36857             this.split.el.setLeft(box.x);
36858             this.split.el.setTop(box.y+box.height);
36859             this.split.el.setWidth(box.width);
36860         }
36861         if(this.collapsed){
36862             this.updateBody(box.width, null);
36863         }
36864         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36865     }
36866 });
36867
36868
36869
36870
36871
36872 Roo.bootstrap.layout.South = function(config){
36873     config.region = 'south';
36874     config.cursor = 's-resize';
36875     Roo.bootstrap.layout.Split.call(this, config);
36876     if(this.split){
36877         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36878         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36879         this.split.el.addClass("roo-layout-split-v");
36880     }
36881     var size = config.initialSize || config.height;
36882     if(typeof size != "undefined"){
36883         this.el.setHeight(size);
36884     }
36885 };
36886
36887 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36888     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36889     getBox : function(){
36890         if(this.collapsed){
36891             return this.collapsedEl.getBox();
36892         }
36893         var box = this.el.getBox();
36894         if(this.split){
36895             var sh = this.split.el.getHeight();
36896             box.height += sh;
36897             box.y -= sh;
36898         }
36899         return box;
36900     },
36901     
36902     updateBox : function(box){
36903         if(this.split && !this.collapsed){
36904             var sh = this.split.el.getHeight();
36905             box.height -= sh;
36906             box.y += sh;
36907             this.split.el.setLeft(box.x);
36908             this.split.el.setTop(box.y-sh);
36909             this.split.el.setWidth(box.width);
36910         }
36911         if(this.collapsed){
36912             this.updateBody(box.width, null);
36913         }
36914         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36915     }
36916 });
36917
36918 Roo.bootstrap.layout.East = function(config){
36919     config.region = "east";
36920     config.cursor = "e-resize";
36921     Roo.bootstrap.layout.Split.call(this, config);
36922     if(this.split){
36923         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36924         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36925         this.split.el.addClass("roo-layout-split-h");
36926     }
36927     var size = config.initialSize || config.width;
36928     if(typeof size != "undefined"){
36929         this.el.setWidth(size);
36930     }
36931 };
36932 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36933     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36934     getBox : function(){
36935         if(this.collapsed){
36936             return this.collapsedEl.getBox();
36937         }
36938         var box = this.el.getBox();
36939         if(this.split){
36940             var sw = this.split.el.getWidth();
36941             box.width += sw;
36942             box.x -= sw;
36943         }
36944         return box;
36945     },
36946
36947     updateBox : function(box){
36948         if(this.split && !this.collapsed){
36949             var sw = this.split.el.getWidth();
36950             box.width -= sw;
36951             this.split.el.setLeft(box.x);
36952             this.split.el.setTop(box.y);
36953             this.split.el.setHeight(box.height);
36954             box.x += sw;
36955         }
36956         if(this.collapsed){
36957             this.updateBody(null, box.height);
36958         }
36959         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36960     }
36961 });
36962
36963 Roo.bootstrap.layout.West = function(config){
36964     config.region = "west";
36965     config.cursor = "w-resize";
36966     
36967     Roo.bootstrap.layout.Split.call(this, config);
36968     if(this.split){
36969         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36970         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36971         this.split.el.addClass("roo-layout-split-h");
36972     }
36973     
36974 };
36975 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36976     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36977     
36978     onRender: function(ctr, pos)
36979     {
36980         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36981         var size = this.config.initialSize || this.config.width;
36982         if(typeof size != "undefined"){
36983             this.el.setWidth(size);
36984         }
36985     },
36986     
36987     getBox : function(){
36988         if(this.collapsed){
36989             return this.collapsedEl.getBox();
36990         }
36991         var box = this.el.getBox();
36992         if(this.split){
36993             box.width += this.split.el.getWidth();
36994         }
36995         return box;
36996     },
36997     
36998     updateBox : function(box){
36999         if(this.split && !this.collapsed){
37000             var sw = this.split.el.getWidth();
37001             box.width -= sw;
37002             this.split.el.setLeft(box.x+box.width);
37003             this.split.el.setTop(box.y);
37004             this.split.el.setHeight(box.height);
37005         }
37006         if(this.collapsed){
37007             this.updateBody(null, box.height);
37008         }
37009         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37010     }
37011 });
37012 Roo.namespace("Roo.bootstrap.panel");/*
37013  * Based on:
37014  * Ext JS Library 1.1.1
37015  * Copyright(c) 2006-2007, Ext JS, LLC.
37016  *
37017  * Originally Released Under LGPL - original licence link has changed is not relivant.
37018  *
37019  * Fork - LGPL
37020  * <script type="text/javascript">
37021  */
37022 /**
37023  * @class Roo.ContentPanel
37024  * @extends Roo.util.Observable
37025  * A basic ContentPanel element.
37026  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37027  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37028  * @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
37029  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37030  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37031  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37032  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37033  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37034  * @cfg {String} title          The title for this panel
37035  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37036  * @cfg {String} url            Calls {@link #setUrl} with this value
37037  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37038  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37039  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37040  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37041  * @cfg {Boolean} badges render the badges
37042
37043  * @constructor
37044  * Create a new ContentPanel.
37045  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37046  * @param {String/Object} config A string to set only the title or a config object
37047  * @param {String} content (optional) Set the HTML content for this panel
37048  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37049  */
37050 Roo.bootstrap.panel.Content = function( config){
37051     
37052     this.tpl = config.tpl || false;
37053     
37054     var el = config.el;
37055     var content = config.content;
37056
37057     if(config.autoCreate){ // xtype is available if this is called from factory
37058         el = Roo.id();
37059     }
37060     this.el = Roo.get(el);
37061     if(!this.el && config && config.autoCreate){
37062         if(typeof config.autoCreate == "object"){
37063             if(!config.autoCreate.id){
37064                 config.autoCreate.id = config.id||el;
37065             }
37066             this.el = Roo.DomHelper.append(document.body,
37067                         config.autoCreate, true);
37068         }else{
37069             var elcfg =  {   tag: "div",
37070                             cls: "roo-layout-inactive-content",
37071                             id: config.id||el
37072                             };
37073             if (config.html) {
37074                 elcfg.html = config.html;
37075                 
37076             }
37077                         
37078             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37079         }
37080     } 
37081     this.closable = false;
37082     this.loaded = false;
37083     this.active = false;
37084    
37085       
37086     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37087         
37088         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37089         
37090         this.wrapEl = this.el; //this.el.wrap();
37091         var ti = [];
37092         if (config.toolbar.items) {
37093             ti = config.toolbar.items ;
37094             delete config.toolbar.items ;
37095         }
37096         
37097         var nitems = [];
37098         this.toolbar.render(this.wrapEl, 'before');
37099         for(var i =0;i < ti.length;i++) {
37100           //  Roo.log(['add child', items[i]]);
37101             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37102         }
37103         this.toolbar.items = nitems;
37104         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37105         delete config.toolbar;
37106         
37107     }
37108     /*
37109     // xtype created footer. - not sure if will work as we normally have to render first..
37110     if (this.footer && !this.footer.el && this.footer.xtype) {
37111         if (!this.wrapEl) {
37112             this.wrapEl = this.el.wrap();
37113         }
37114     
37115         this.footer.container = this.wrapEl.createChild();
37116          
37117         this.footer = Roo.factory(this.footer, Roo);
37118         
37119     }
37120     */
37121     
37122      if(typeof config == "string"){
37123         this.title = config;
37124     }else{
37125         Roo.apply(this, config);
37126     }
37127     
37128     if(this.resizeEl){
37129         this.resizeEl = Roo.get(this.resizeEl, true);
37130     }else{
37131         this.resizeEl = this.el;
37132     }
37133     // handle view.xtype
37134     
37135  
37136     
37137     
37138     this.addEvents({
37139         /**
37140          * @event activate
37141          * Fires when this panel is activated. 
37142          * @param {Roo.ContentPanel} this
37143          */
37144         "activate" : true,
37145         /**
37146          * @event deactivate
37147          * Fires when this panel is activated. 
37148          * @param {Roo.ContentPanel} this
37149          */
37150         "deactivate" : true,
37151
37152         /**
37153          * @event resize
37154          * Fires when this panel is resized if fitToFrame is true.
37155          * @param {Roo.ContentPanel} this
37156          * @param {Number} width The width after any component adjustments
37157          * @param {Number} height The height after any component adjustments
37158          */
37159         "resize" : true,
37160         
37161          /**
37162          * @event render
37163          * Fires when this tab is created
37164          * @param {Roo.ContentPanel} this
37165          */
37166         "render" : true
37167         
37168         
37169         
37170     });
37171     
37172
37173     
37174     
37175     if(this.autoScroll){
37176         this.resizeEl.setStyle("overflow", "auto");
37177     } else {
37178         // fix randome scrolling
37179         //this.el.on('scroll', function() {
37180         //    Roo.log('fix random scolling');
37181         //    this.scrollTo('top',0); 
37182         //});
37183     }
37184     content = content || this.content;
37185     if(content){
37186         this.setContent(content);
37187     }
37188     if(config && config.url){
37189         this.setUrl(this.url, this.params, this.loadOnce);
37190     }
37191     
37192     
37193     
37194     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37195     
37196     if (this.view && typeof(this.view.xtype) != 'undefined') {
37197         this.view.el = this.el.appendChild(document.createElement("div"));
37198         this.view = Roo.factory(this.view); 
37199         this.view.render  &&  this.view.render(false, '');  
37200     }
37201     
37202     
37203     this.fireEvent('render', this);
37204 };
37205
37206 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37207     
37208     tabTip : '',
37209     
37210     setRegion : function(region){
37211         this.region = region;
37212         this.setActiveClass(region && !this.background);
37213     },
37214     
37215     
37216     setActiveClass: function(state)
37217     {
37218         if(state){
37219            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37220            this.el.setStyle('position','relative');
37221         }else{
37222            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37223            this.el.setStyle('position', 'absolute');
37224         } 
37225     },
37226     
37227     /**
37228      * Returns the toolbar for this Panel if one was configured. 
37229      * @return {Roo.Toolbar} 
37230      */
37231     getToolbar : function(){
37232         return this.toolbar;
37233     },
37234     
37235     setActiveState : function(active)
37236     {
37237         this.active = active;
37238         this.setActiveClass(active);
37239         if(!active){
37240             if(this.fireEvent("deactivate", this) === false){
37241                 return false;
37242             }
37243             return true;
37244         }
37245         this.fireEvent("activate", this);
37246         return true;
37247     },
37248     /**
37249      * Updates this panel's element
37250      * @param {String} content The new content
37251      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37252     */
37253     setContent : function(content, loadScripts){
37254         this.el.update(content, loadScripts);
37255     },
37256
37257     ignoreResize : function(w, h){
37258         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37259             return true;
37260         }else{
37261             this.lastSize = {width: w, height: h};
37262             return false;
37263         }
37264     },
37265     /**
37266      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37267      * @return {Roo.UpdateManager} The UpdateManager
37268      */
37269     getUpdateManager : function(){
37270         return this.el.getUpdateManager();
37271     },
37272      /**
37273      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37274      * @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:
37275 <pre><code>
37276 panel.load({
37277     url: "your-url.php",
37278     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37279     callback: yourFunction,
37280     scope: yourObject, //(optional scope)
37281     discardUrl: false,
37282     nocache: false,
37283     text: "Loading...",
37284     timeout: 30,
37285     scripts: false
37286 });
37287 </code></pre>
37288      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37289      * 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.
37290      * @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}
37291      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37292      * @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.
37293      * @return {Roo.ContentPanel} this
37294      */
37295     load : function(){
37296         var um = this.el.getUpdateManager();
37297         um.update.apply(um, arguments);
37298         return this;
37299     },
37300
37301
37302     /**
37303      * 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.
37304      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37305      * @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)
37306      * @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)
37307      * @return {Roo.UpdateManager} The UpdateManager
37308      */
37309     setUrl : function(url, params, loadOnce){
37310         if(this.refreshDelegate){
37311             this.removeListener("activate", this.refreshDelegate);
37312         }
37313         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37314         this.on("activate", this.refreshDelegate);
37315         return this.el.getUpdateManager();
37316     },
37317     
37318     _handleRefresh : function(url, params, loadOnce){
37319         if(!loadOnce || !this.loaded){
37320             var updater = this.el.getUpdateManager();
37321             updater.update(url, params, this._setLoaded.createDelegate(this));
37322         }
37323     },
37324     
37325     _setLoaded : function(){
37326         this.loaded = true;
37327     }, 
37328     
37329     /**
37330      * Returns this panel's id
37331      * @return {String} 
37332      */
37333     getId : function(){
37334         return this.el.id;
37335     },
37336     
37337     /** 
37338      * Returns this panel's element - used by regiosn to add.
37339      * @return {Roo.Element} 
37340      */
37341     getEl : function(){
37342         return this.wrapEl || this.el;
37343     },
37344     
37345    
37346     
37347     adjustForComponents : function(width, height)
37348     {
37349         //Roo.log('adjustForComponents ');
37350         if(this.resizeEl != this.el){
37351             width -= this.el.getFrameWidth('lr');
37352             height -= this.el.getFrameWidth('tb');
37353         }
37354         if(this.toolbar){
37355             var te = this.toolbar.getEl();
37356             te.setWidth(width);
37357             height -= te.getHeight();
37358         }
37359         if(this.footer){
37360             var te = this.footer.getEl();
37361             te.setWidth(width);
37362             height -= te.getHeight();
37363         }
37364         
37365         
37366         if(this.adjustments){
37367             width += this.adjustments[0];
37368             height += this.adjustments[1];
37369         }
37370         return {"width": width, "height": height};
37371     },
37372     
37373     setSize : function(width, height){
37374         if(this.fitToFrame && !this.ignoreResize(width, height)){
37375             if(this.fitContainer && this.resizeEl != this.el){
37376                 this.el.setSize(width, height);
37377             }
37378             var size = this.adjustForComponents(width, height);
37379             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37380             this.fireEvent('resize', this, size.width, size.height);
37381         }
37382     },
37383     
37384     /**
37385      * Returns this panel's title
37386      * @return {String} 
37387      */
37388     getTitle : function(){
37389         
37390         if (typeof(this.title) != 'object') {
37391             return this.title;
37392         }
37393         
37394         var t = '';
37395         for (var k in this.title) {
37396             if (!this.title.hasOwnProperty(k)) {
37397                 continue;
37398             }
37399             
37400             if (k.indexOf('-') >= 0) {
37401                 var s = k.split('-');
37402                 for (var i = 0; i<s.length; i++) {
37403                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37404                 }
37405             } else {
37406                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37407             }
37408         }
37409         return t;
37410     },
37411     
37412     /**
37413      * Set this panel's title
37414      * @param {String} title
37415      */
37416     setTitle : function(title){
37417         this.title = title;
37418         if(this.region){
37419             this.region.updatePanelTitle(this, title);
37420         }
37421     },
37422     
37423     /**
37424      * Returns true is this panel was configured to be closable
37425      * @return {Boolean} 
37426      */
37427     isClosable : function(){
37428         return this.closable;
37429     },
37430     
37431     beforeSlide : function(){
37432         this.el.clip();
37433         this.resizeEl.clip();
37434     },
37435     
37436     afterSlide : function(){
37437         this.el.unclip();
37438         this.resizeEl.unclip();
37439     },
37440     
37441     /**
37442      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37443      *   Will fail silently if the {@link #setUrl} method has not been called.
37444      *   This does not activate the panel, just updates its content.
37445      */
37446     refresh : function(){
37447         if(this.refreshDelegate){
37448            this.loaded = false;
37449            this.refreshDelegate();
37450         }
37451     },
37452     
37453     /**
37454      * Destroys this panel
37455      */
37456     destroy : function(){
37457         this.el.removeAllListeners();
37458         var tempEl = document.createElement("span");
37459         tempEl.appendChild(this.el.dom);
37460         tempEl.innerHTML = "";
37461         this.el.remove();
37462         this.el = null;
37463     },
37464     
37465     /**
37466      * form - if the content panel contains a form - this is a reference to it.
37467      * @type {Roo.form.Form}
37468      */
37469     form : false,
37470     /**
37471      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37472      *    This contains a reference to it.
37473      * @type {Roo.View}
37474      */
37475     view : false,
37476     
37477       /**
37478      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37479      * <pre><code>
37480
37481 layout.addxtype({
37482        xtype : 'Form',
37483        items: [ .... ]
37484    }
37485 );
37486
37487 </code></pre>
37488      * @param {Object} cfg Xtype definition of item to add.
37489      */
37490     
37491     
37492     getChildContainer: function () {
37493         return this.getEl();
37494     }
37495     
37496     
37497     /*
37498         var  ret = new Roo.factory(cfg);
37499         return ret;
37500         
37501         
37502         // add form..
37503         if (cfg.xtype.match(/^Form$/)) {
37504             
37505             var el;
37506             //if (this.footer) {
37507             //    el = this.footer.container.insertSibling(false, 'before');
37508             //} else {
37509                 el = this.el.createChild();
37510             //}
37511
37512             this.form = new  Roo.form.Form(cfg);
37513             
37514             
37515             if ( this.form.allItems.length) {
37516                 this.form.render(el.dom);
37517             }
37518             return this.form;
37519         }
37520         // should only have one of theses..
37521         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37522             // views.. should not be just added - used named prop 'view''
37523             
37524             cfg.el = this.el.appendChild(document.createElement("div"));
37525             // factory?
37526             
37527             var ret = new Roo.factory(cfg);
37528              
37529              ret.render && ret.render(false, ''); // render blank..
37530             this.view = ret;
37531             return ret;
37532         }
37533         return false;
37534     }
37535     \*/
37536 });
37537  
37538 /**
37539  * @class Roo.bootstrap.panel.Grid
37540  * @extends Roo.bootstrap.panel.Content
37541  * @constructor
37542  * Create a new GridPanel.
37543  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37544  * @param {Object} config A the config object
37545   
37546  */
37547
37548
37549
37550 Roo.bootstrap.panel.Grid = function(config)
37551 {
37552     
37553       
37554     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37555         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37556
37557     config.el = this.wrapper;
37558     //this.el = this.wrapper;
37559     
37560       if (config.container) {
37561         // ctor'ed from a Border/panel.grid
37562         
37563         
37564         this.wrapper.setStyle("overflow", "hidden");
37565         this.wrapper.addClass('roo-grid-container');
37566
37567     }
37568     
37569     
37570     if(config.toolbar){
37571         var tool_el = this.wrapper.createChild();    
37572         this.toolbar = Roo.factory(config.toolbar);
37573         var ti = [];
37574         if (config.toolbar.items) {
37575             ti = config.toolbar.items ;
37576             delete config.toolbar.items ;
37577         }
37578         
37579         var nitems = [];
37580         this.toolbar.render(tool_el);
37581         for(var i =0;i < ti.length;i++) {
37582           //  Roo.log(['add child', items[i]]);
37583             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37584         }
37585         this.toolbar.items = nitems;
37586         
37587         delete config.toolbar;
37588     }
37589     
37590     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37591     config.grid.scrollBody = true;;
37592     config.grid.monitorWindowResize = false; // turn off autosizing
37593     config.grid.autoHeight = false;
37594     config.grid.autoWidth = false;
37595     
37596     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37597     
37598     if (config.background) {
37599         // render grid on panel activation (if panel background)
37600         this.on('activate', function(gp) {
37601             if (!gp.grid.rendered) {
37602                 gp.grid.render(this.wrapper);
37603                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37604             }
37605         });
37606             
37607     } else {
37608         this.grid.render(this.wrapper);
37609         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37610
37611     }
37612     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37613     // ??? needed ??? config.el = this.wrapper;
37614     
37615     
37616     
37617   
37618     // xtype created footer. - not sure if will work as we normally have to render first..
37619     if (this.footer && !this.footer.el && this.footer.xtype) {
37620         
37621         var ctr = this.grid.getView().getFooterPanel(true);
37622         this.footer.dataSource = this.grid.dataSource;
37623         this.footer = Roo.factory(this.footer, Roo);
37624         this.footer.render(ctr);
37625         
37626     }
37627     
37628     
37629     
37630     
37631      
37632 };
37633
37634 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37635     getId : function(){
37636         return this.grid.id;
37637     },
37638     
37639     /**
37640      * Returns the grid for this panel
37641      * @return {Roo.bootstrap.Table} 
37642      */
37643     getGrid : function(){
37644         return this.grid;    
37645     },
37646     
37647     setSize : function(width, height){
37648         if(!this.ignoreResize(width, height)){
37649             var grid = this.grid;
37650             var size = this.adjustForComponents(width, height);
37651             var gridel = grid.getGridEl();
37652             gridel.setSize(size.width, size.height);
37653             /*
37654             var thd = grid.getGridEl().select('thead',true).first();
37655             var tbd = grid.getGridEl().select('tbody', true).first();
37656             if (tbd) {
37657                 tbd.setSize(width, height - thd.getHeight());
37658             }
37659             */
37660             grid.autoSize();
37661         }
37662     },
37663      
37664     
37665     
37666     beforeSlide : function(){
37667         this.grid.getView().scroller.clip();
37668     },
37669     
37670     afterSlide : function(){
37671         this.grid.getView().scroller.unclip();
37672     },
37673     
37674     destroy : function(){
37675         this.grid.destroy();
37676         delete this.grid;
37677         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37678     }
37679 });
37680
37681 /**
37682  * @class Roo.bootstrap.panel.Nest
37683  * @extends Roo.bootstrap.panel.Content
37684  * @constructor
37685  * Create a new Panel, that can contain a layout.Border.
37686  * 
37687  * 
37688  * @param {Roo.BorderLayout} layout The layout for this panel
37689  * @param {String/Object} config A string to set only the title or a config object
37690  */
37691 Roo.bootstrap.panel.Nest = function(config)
37692 {
37693     // construct with only one argument..
37694     /* FIXME - implement nicer consturctors
37695     if (layout.layout) {
37696         config = layout;
37697         layout = config.layout;
37698         delete config.layout;
37699     }
37700     if (layout.xtype && !layout.getEl) {
37701         // then layout needs constructing..
37702         layout = Roo.factory(layout, Roo);
37703     }
37704     */
37705     
37706     config.el =  config.layout.getEl();
37707     
37708     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37709     
37710     config.layout.monitorWindowResize = false; // turn off autosizing
37711     this.layout = config.layout;
37712     this.layout.getEl().addClass("roo-layout-nested-layout");
37713     
37714     
37715     
37716     
37717 };
37718
37719 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37720
37721     setSize : function(width, height){
37722         if(!this.ignoreResize(width, height)){
37723             var size = this.adjustForComponents(width, height);
37724             var el = this.layout.getEl();
37725             if (size.height < 1) {
37726                 el.setWidth(size.width);   
37727             } else {
37728                 el.setSize(size.width, size.height);
37729             }
37730             var touch = el.dom.offsetWidth;
37731             this.layout.layout();
37732             // ie requires a double layout on the first pass
37733             if(Roo.isIE && !this.initialized){
37734                 this.initialized = true;
37735                 this.layout.layout();
37736             }
37737         }
37738     },
37739     
37740     // activate all subpanels if not currently active..
37741     
37742     setActiveState : function(active){
37743         this.active = active;
37744         this.setActiveClass(active);
37745         
37746         if(!active){
37747             this.fireEvent("deactivate", this);
37748             return;
37749         }
37750         
37751         this.fireEvent("activate", this);
37752         // not sure if this should happen before or after..
37753         if (!this.layout) {
37754             return; // should not happen..
37755         }
37756         var reg = false;
37757         for (var r in this.layout.regions) {
37758             reg = this.layout.getRegion(r);
37759             if (reg.getActivePanel()) {
37760                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37761                 reg.setActivePanel(reg.getActivePanel());
37762                 continue;
37763             }
37764             if (!reg.panels.length) {
37765                 continue;
37766             }
37767             reg.showPanel(reg.getPanel(0));
37768         }
37769         
37770         
37771         
37772         
37773     },
37774     
37775     /**
37776      * Returns the nested BorderLayout for this panel
37777      * @return {Roo.BorderLayout} 
37778      */
37779     getLayout : function(){
37780         return this.layout;
37781     },
37782     
37783      /**
37784      * Adds a xtype elements to the layout of the nested panel
37785      * <pre><code>
37786
37787 panel.addxtype({
37788        xtype : 'ContentPanel',
37789        region: 'west',
37790        items: [ .... ]
37791    }
37792 );
37793
37794 panel.addxtype({
37795         xtype : 'NestedLayoutPanel',
37796         region: 'west',
37797         layout: {
37798            center: { },
37799            west: { }   
37800         },
37801         items : [ ... list of content panels or nested layout panels.. ]
37802    }
37803 );
37804 </code></pre>
37805      * @param {Object} cfg Xtype definition of item to add.
37806      */
37807     addxtype : function(cfg) {
37808         return this.layout.addxtype(cfg);
37809     
37810     }
37811 });        /*
37812  * Based on:
37813  * Ext JS Library 1.1.1
37814  * Copyright(c) 2006-2007, Ext JS, LLC.
37815  *
37816  * Originally Released Under LGPL - original licence link has changed is not relivant.
37817  *
37818  * Fork - LGPL
37819  * <script type="text/javascript">
37820  */
37821 /**
37822  * @class Roo.TabPanel
37823  * @extends Roo.util.Observable
37824  * A lightweight tab container.
37825  * <br><br>
37826  * Usage:
37827  * <pre><code>
37828 // basic tabs 1, built from existing content
37829 var tabs = new Roo.TabPanel("tabs1");
37830 tabs.addTab("script", "View Script");
37831 tabs.addTab("markup", "View Markup");
37832 tabs.activate("script");
37833
37834 // more advanced tabs, built from javascript
37835 var jtabs = new Roo.TabPanel("jtabs");
37836 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37837
37838 // set up the UpdateManager
37839 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37840 var updater = tab2.getUpdateManager();
37841 updater.setDefaultUrl("ajax1.htm");
37842 tab2.on('activate', updater.refresh, updater, true);
37843
37844 // Use setUrl for Ajax loading
37845 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37846 tab3.setUrl("ajax2.htm", null, true);
37847
37848 // Disabled tab
37849 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37850 tab4.disable();
37851
37852 jtabs.activate("jtabs-1");
37853  * </code></pre>
37854  * @constructor
37855  * Create a new TabPanel.
37856  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37857  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37858  */
37859 Roo.bootstrap.panel.Tabs = function(config){
37860     /**
37861     * The container element for this TabPanel.
37862     * @type Roo.Element
37863     */
37864     this.el = Roo.get(config.el);
37865     delete config.el;
37866     if(config){
37867         if(typeof config == "boolean"){
37868             this.tabPosition = config ? "bottom" : "top";
37869         }else{
37870             Roo.apply(this, config);
37871         }
37872     }
37873     
37874     if(this.tabPosition == "bottom"){
37875         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37876         this.el.addClass("roo-tabs-bottom");
37877     }
37878     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37879     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37880     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37881     if(Roo.isIE){
37882         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37883     }
37884     if(this.tabPosition != "bottom"){
37885         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37886          * @type Roo.Element
37887          */
37888         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37889         this.el.addClass("roo-tabs-top");
37890     }
37891     this.items = [];
37892
37893     this.bodyEl.setStyle("position", "relative");
37894
37895     this.active = null;
37896     this.activateDelegate = this.activate.createDelegate(this);
37897
37898     this.addEvents({
37899         /**
37900          * @event tabchange
37901          * Fires when the active tab changes
37902          * @param {Roo.TabPanel} this
37903          * @param {Roo.TabPanelItem} activePanel The new active tab
37904          */
37905         "tabchange": true,
37906         /**
37907          * @event beforetabchange
37908          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37909          * @param {Roo.TabPanel} this
37910          * @param {Object} e Set cancel to true on this object to cancel the tab change
37911          * @param {Roo.TabPanelItem} tab The tab being changed to
37912          */
37913         "beforetabchange" : true
37914     });
37915
37916     Roo.EventManager.onWindowResize(this.onResize, this);
37917     this.cpad = this.el.getPadding("lr");
37918     this.hiddenCount = 0;
37919
37920
37921     // toolbar on the tabbar support...
37922     if (this.toolbar) {
37923         alert("no toolbar support yet");
37924         this.toolbar  = false;
37925         /*
37926         var tcfg = this.toolbar;
37927         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37928         this.toolbar = new Roo.Toolbar(tcfg);
37929         if (Roo.isSafari) {
37930             var tbl = tcfg.container.child('table', true);
37931             tbl.setAttribute('width', '100%');
37932         }
37933         */
37934         
37935     }
37936    
37937
37938
37939     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37940 };
37941
37942 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37943     /*
37944      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37945      */
37946     tabPosition : "top",
37947     /*
37948      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37949      */
37950     currentTabWidth : 0,
37951     /*
37952      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37953      */
37954     minTabWidth : 40,
37955     /*
37956      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37957      */
37958     maxTabWidth : 250,
37959     /*
37960      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37961      */
37962     preferredTabWidth : 175,
37963     /*
37964      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37965      */
37966     resizeTabs : false,
37967     /*
37968      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37969      */
37970     monitorResize : true,
37971     /*
37972      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37973      */
37974     toolbar : false,
37975
37976     /**
37977      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37978      * @param {String} id The id of the div to use <b>or create</b>
37979      * @param {String} text The text for the tab
37980      * @param {String} content (optional) Content to put in the TabPanelItem body
37981      * @param {Boolean} closable (optional) True to create a close icon on the tab
37982      * @return {Roo.TabPanelItem} The created TabPanelItem
37983      */
37984     addTab : function(id, text, content, closable, tpl)
37985     {
37986         var item = new Roo.bootstrap.panel.TabItem({
37987             panel: this,
37988             id : id,
37989             text : text,
37990             closable : closable,
37991             tpl : tpl
37992         });
37993         this.addTabItem(item);
37994         if(content){
37995             item.setContent(content);
37996         }
37997         return item;
37998     },
37999
38000     /**
38001      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38002      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38003      * @return {Roo.TabPanelItem}
38004      */
38005     getTab : function(id){
38006         return this.items[id];
38007     },
38008
38009     /**
38010      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38011      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38012      */
38013     hideTab : function(id){
38014         var t = this.items[id];
38015         if(!t.isHidden()){
38016            t.setHidden(true);
38017            this.hiddenCount++;
38018            this.autoSizeTabs();
38019         }
38020     },
38021
38022     /**
38023      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38024      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38025      */
38026     unhideTab : function(id){
38027         var t = this.items[id];
38028         if(t.isHidden()){
38029            t.setHidden(false);
38030            this.hiddenCount--;
38031            this.autoSizeTabs();
38032         }
38033     },
38034
38035     /**
38036      * Adds an existing {@link Roo.TabPanelItem}.
38037      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38038      */
38039     addTabItem : function(item){
38040         this.items[item.id] = item;
38041         this.items.push(item);
38042       //  if(this.resizeTabs){
38043     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38044   //         this.autoSizeTabs();
38045 //        }else{
38046 //            item.autoSize();
38047        // }
38048     },
38049
38050     /**
38051      * Removes a {@link Roo.TabPanelItem}.
38052      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38053      */
38054     removeTab : function(id){
38055         var items = this.items;
38056         var tab = items[id];
38057         if(!tab) { return; }
38058         var index = items.indexOf(tab);
38059         if(this.active == tab && items.length > 1){
38060             var newTab = this.getNextAvailable(index);
38061             if(newTab) {
38062                 newTab.activate();
38063             }
38064         }
38065         this.stripEl.dom.removeChild(tab.pnode.dom);
38066         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38067             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38068         }
38069         items.splice(index, 1);
38070         delete this.items[tab.id];
38071         tab.fireEvent("close", tab);
38072         tab.purgeListeners();
38073         this.autoSizeTabs();
38074     },
38075
38076     getNextAvailable : function(start){
38077         var items = this.items;
38078         var index = start;
38079         // look for a next tab that will slide over to
38080         // replace the one being removed
38081         while(index < items.length){
38082             var item = items[++index];
38083             if(item && !item.isHidden()){
38084                 return item;
38085             }
38086         }
38087         // if one isn't found select the previous tab (on the left)
38088         index = start;
38089         while(index >= 0){
38090             var item = items[--index];
38091             if(item && !item.isHidden()){
38092                 return item;
38093             }
38094         }
38095         return null;
38096     },
38097
38098     /**
38099      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38100      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38101      */
38102     disableTab : function(id){
38103         var tab = this.items[id];
38104         if(tab && this.active != tab){
38105             tab.disable();
38106         }
38107     },
38108
38109     /**
38110      * Enables a {@link Roo.TabPanelItem} that is disabled.
38111      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38112      */
38113     enableTab : function(id){
38114         var tab = this.items[id];
38115         tab.enable();
38116     },
38117
38118     /**
38119      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38120      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38121      * @return {Roo.TabPanelItem} The TabPanelItem.
38122      */
38123     activate : function(id){
38124         var tab = this.items[id];
38125         if(!tab){
38126             return null;
38127         }
38128         if(tab == this.active || tab.disabled){
38129             return tab;
38130         }
38131         var e = {};
38132         this.fireEvent("beforetabchange", this, e, tab);
38133         if(e.cancel !== true && !tab.disabled){
38134             if(this.active){
38135                 this.active.hide();
38136             }
38137             this.active = this.items[id];
38138             this.active.show();
38139             this.fireEvent("tabchange", this, this.active);
38140         }
38141         return tab;
38142     },
38143
38144     /**
38145      * Gets the active {@link Roo.TabPanelItem}.
38146      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38147      */
38148     getActiveTab : function(){
38149         return this.active;
38150     },
38151
38152     /**
38153      * Updates the tab body element to fit the height of the container element
38154      * for overflow scrolling
38155      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38156      */
38157     syncHeight : function(targetHeight){
38158         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38159         var bm = this.bodyEl.getMargins();
38160         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38161         this.bodyEl.setHeight(newHeight);
38162         return newHeight;
38163     },
38164
38165     onResize : function(){
38166         if(this.monitorResize){
38167             this.autoSizeTabs();
38168         }
38169     },
38170
38171     /**
38172      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38173      */
38174     beginUpdate : function(){
38175         this.updating = true;
38176     },
38177
38178     /**
38179      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38180      */
38181     endUpdate : function(){
38182         this.updating = false;
38183         this.autoSizeTabs();
38184     },
38185
38186     /**
38187      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38188      */
38189     autoSizeTabs : function(){
38190         var count = this.items.length;
38191         var vcount = count - this.hiddenCount;
38192         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38193             return;
38194         }
38195         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38196         var availWidth = Math.floor(w / vcount);
38197         var b = this.stripBody;
38198         if(b.getWidth() > w){
38199             var tabs = this.items;
38200             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38201             if(availWidth < this.minTabWidth){
38202                 /*if(!this.sleft){    // incomplete scrolling code
38203                     this.createScrollButtons();
38204                 }
38205                 this.showScroll();
38206                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38207             }
38208         }else{
38209             if(this.currentTabWidth < this.preferredTabWidth){
38210                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38211             }
38212         }
38213     },
38214
38215     /**
38216      * Returns the number of tabs in this TabPanel.
38217      * @return {Number}
38218      */
38219      getCount : function(){
38220          return this.items.length;
38221      },
38222
38223     /**
38224      * Resizes all the tabs to the passed width
38225      * @param {Number} The new width
38226      */
38227     setTabWidth : function(width){
38228         this.currentTabWidth = width;
38229         for(var i = 0, len = this.items.length; i < len; i++) {
38230                 if(!this.items[i].isHidden()) {
38231                 this.items[i].setWidth(width);
38232             }
38233         }
38234     },
38235
38236     /**
38237      * Destroys this TabPanel
38238      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38239      */
38240     destroy : function(removeEl){
38241         Roo.EventManager.removeResizeListener(this.onResize, this);
38242         for(var i = 0, len = this.items.length; i < len; i++){
38243             this.items[i].purgeListeners();
38244         }
38245         if(removeEl === true){
38246             this.el.update("");
38247             this.el.remove();
38248         }
38249     },
38250     
38251     createStrip : function(container)
38252     {
38253         var strip = document.createElement("nav");
38254         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38255         container.appendChild(strip);
38256         return strip;
38257     },
38258     
38259     createStripList : function(strip)
38260     {
38261         // div wrapper for retard IE
38262         // returns the "tr" element.
38263         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38264         //'<div class="x-tabs-strip-wrap">'+
38265           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38266           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38267         return strip.firstChild; //.firstChild.firstChild.firstChild;
38268     },
38269     createBody : function(container)
38270     {
38271         var body = document.createElement("div");
38272         Roo.id(body, "tab-body");
38273         //Roo.fly(body).addClass("x-tabs-body");
38274         Roo.fly(body).addClass("tab-content");
38275         container.appendChild(body);
38276         return body;
38277     },
38278     createItemBody :function(bodyEl, id){
38279         var body = Roo.getDom(id);
38280         if(!body){
38281             body = document.createElement("div");
38282             body.id = id;
38283         }
38284         //Roo.fly(body).addClass("x-tabs-item-body");
38285         Roo.fly(body).addClass("tab-pane");
38286          bodyEl.insertBefore(body, bodyEl.firstChild);
38287         return body;
38288     },
38289     /** @private */
38290     createStripElements :  function(stripEl, text, closable, tpl)
38291     {
38292         var td = document.createElement("li"); // was td..
38293         
38294         
38295         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38296         
38297         
38298         stripEl.appendChild(td);
38299         /*if(closable){
38300             td.className = "x-tabs-closable";
38301             if(!this.closeTpl){
38302                 this.closeTpl = new Roo.Template(
38303                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38304                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38305                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38306                 );
38307             }
38308             var el = this.closeTpl.overwrite(td, {"text": text});
38309             var close = el.getElementsByTagName("div")[0];
38310             var inner = el.getElementsByTagName("em")[0];
38311             return {"el": el, "close": close, "inner": inner};
38312         } else {
38313         */
38314         // not sure what this is..
38315 //            if(!this.tabTpl){
38316                 //this.tabTpl = new Roo.Template(
38317                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38318                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38319                 //);
38320 //                this.tabTpl = new Roo.Template(
38321 //                   '<a href="#">' +
38322 //                   '<span unselectable="on"' +
38323 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38324 //                            ' >{text}</span></a>'
38325 //                );
38326 //                
38327 //            }
38328
38329
38330             var template = tpl || this.tabTpl || false;
38331             
38332             if(!template){
38333                 
38334                 template = new Roo.Template(
38335                    '<a href="#">' +
38336                    '<span unselectable="on"' +
38337                             (this.disableTooltips ? '' : ' title="{text}"') +
38338                             ' >{text}</span></a>'
38339                 );
38340             }
38341             
38342             switch (typeof(template)) {
38343                 case 'object' :
38344                     break;
38345                 case 'string' :
38346                     template = new Roo.Template(template);
38347                     break;
38348                 default :
38349                     break;
38350             }
38351             
38352             var el = template.overwrite(td, {"text": text});
38353             
38354             var inner = el.getElementsByTagName("span")[0];
38355             
38356             return {"el": el, "inner": inner};
38357             
38358     }
38359         
38360     
38361 });
38362
38363 /**
38364  * @class Roo.TabPanelItem
38365  * @extends Roo.util.Observable
38366  * Represents an individual item (tab plus body) in a TabPanel.
38367  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38368  * @param {String} id The id of this TabPanelItem
38369  * @param {String} text The text for the tab of this TabPanelItem
38370  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38371  */
38372 Roo.bootstrap.panel.TabItem = function(config){
38373     /**
38374      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38375      * @type Roo.TabPanel
38376      */
38377     this.tabPanel = config.panel;
38378     /**
38379      * The id for this TabPanelItem
38380      * @type String
38381      */
38382     this.id = config.id;
38383     /** @private */
38384     this.disabled = false;
38385     /** @private */
38386     this.text = config.text;
38387     /** @private */
38388     this.loaded = false;
38389     this.closable = config.closable;
38390
38391     /**
38392      * The body element for this TabPanelItem.
38393      * @type Roo.Element
38394      */
38395     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38396     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38397     this.bodyEl.setStyle("display", "block");
38398     this.bodyEl.setStyle("zoom", "1");
38399     //this.hideAction();
38400
38401     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38402     /** @private */
38403     this.el = Roo.get(els.el);
38404     this.inner = Roo.get(els.inner, true);
38405     this.textEl = Roo.get(this.el.dom.firstChild, true);
38406     this.pnode = Roo.get(els.el.parentNode, true);
38407 //    this.el.on("mousedown", this.onTabMouseDown, this);
38408     this.el.on("click", this.onTabClick, this);
38409     /** @private */
38410     if(config.closable){
38411         var c = Roo.get(els.close, true);
38412         c.dom.title = this.closeText;
38413         c.addClassOnOver("close-over");
38414         c.on("click", this.closeClick, this);
38415      }
38416
38417     this.addEvents({
38418          /**
38419          * @event activate
38420          * Fires when this tab becomes the active tab.
38421          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38422          * @param {Roo.TabPanelItem} this
38423          */
38424         "activate": true,
38425         /**
38426          * @event beforeclose
38427          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38428          * @param {Roo.TabPanelItem} this
38429          * @param {Object} e Set cancel to true on this object to cancel the close.
38430          */
38431         "beforeclose": true,
38432         /**
38433          * @event close
38434          * Fires when this tab is closed.
38435          * @param {Roo.TabPanelItem} this
38436          */
38437          "close": true,
38438         /**
38439          * @event deactivate
38440          * Fires when this tab is no longer the active tab.
38441          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38442          * @param {Roo.TabPanelItem} this
38443          */
38444          "deactivate" : true
38445     });
38446     this.hidden = false;
38447
38448     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38449 };
38450
38451 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38452            {
38453     purgeListeners : function(){
38454        Roo.util.Observable.prototype.purgeListeners.call(this);
38455        this.el.removeAllListeners();
38456     },
38457     /**
38458      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38459      */
38460     show : function(){
38461         this.pnode.addClass("active");
38462         this.showAction();
38463         if(Roo.isOpera){
38464             this.tabPanel.stripWrap.repaint();
38465         }
38466         this.fireEvent("activate", this.tabPanel, this);
38467     },
38468
38469     /**
38470      * Returns true if this tab is the active tab.
38471      * @return {Boolean}
38472      */
38473     isActive : function(){
38474         return this.tabPanel.getActiveTab() == this;
38475     },
38476
38477     /**
38478      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38479      */
38480     hide : function(){
38481         this.pnode.removeClass("active");
38482         this.hideAction();
38483         this.fireEvent("deactivate", this.tabPanel, this);
38484     },
38485
38486     hideAction : function(){
38487         this.bodyEl.hide();
38488         this.bodyEl.setStyle("position", "absolute");
38489         this.bodyEl.setLeft("-20000px");
38490         this.bodyEl.setTop("-20000px");
38491     },
38492
38493     showAction : function(){
38494         this.bodyEl.setStyle("position", "relative");
38495         this.bodyEl.setTop("");
38496         this.bodyEl.setLeft("");
38497         this.bodyEl.show();
38498     },
38499
38500     /**
38501      * Set the tooltip for the tab.
38502      * @param {String} tooltip The tab's tooltip
38503      */
38504     setTooltip : function(text){
38505         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38506             this.textEl.dom.qtip = text;
38507             this.textEl.dom.removeAttribute('title');
38508         }else{
38509             this.textEl.dom.title = text;
38510         }
38511     },
38512
38513     onTabClick : function(e){
38514         e.preventDefault();
38515         this.tabPanel.activate(this.id);
38516     },
38517
38518     onTabMouseDown : function(e){
38519         e.preventDefault();
38520         this.tabPanel.activate(this.id);
38521     },
38522 /*
38523     getWidth : function(){
38524         return this.inner.getWidth();
38525     },
38526
38527     setWidth : function(width){
38528         var iwidth = width - this.pnode.getPadding("lr");
38529         this.inner.setWidth(iwidth);
38530         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38531         this.pnode.setWidth(width);
38532     },
38533 */
38534     /**
38535      * Show or hide the tab
38536      * @param {Boolean} hidden True to hide or false to show.
38537      */
38538     setHidden : function(hidden){
38539         this.hidden = hidden;
38540         this.pnode.setStyle("display", hidden ? "none" : "");
38541     },
38542
38543     /**
38544      * Returns true if this tab is "hidden"
38545      * @return {Boolean}
38546      */
38547     isHidden : function(){
38548         return this.hidden;
38549     },
38550
38551     /**
38552      * Returns the text for this tab
38553      * @return {String}
38554      */
38555     getText : function(){
38556         return this.text;
38557     },
38558     /*
38559     autoSize : function(){
38560         //this.el.beginMeasure();
38561         this.textEl.setWidth(1);
38562         /*
38563          *  #2804 [new] Tabs in Roojs
38564          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38565          */
38566         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38567         //this.el.endMeasure();
38568     //},
38569
38570     /**
38571      * Sets the text for the tab (Note: this also sets the tooltip text)
38572      * @param {String} text The tab's text and tooltip
38573      */
38574     setText : function(text){
38575         this.text = text;
38576         this.textEl.update(text);
38577         this.setTooltip(text);
38578         //if(!this.tabPanel.resizeTabs){
38579         //    this.autoSize();
38580         //}
38581     },
38582     /**
38583      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38584      */
38585     activate : function(){
38586         this.tabPanel.activate(this.id);
38587     },
38588
38589     /**
38590      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38591      */
38592     disable : function(){
38593         if(this.tabPanel.active != this){
38594             this.disabled = true;
38595             this.pnode.addClass("disabled");
38596         }
38597     },
38598
38599     /**
38600      * Enables this TabPanelItem if it was previously disabled.
38601      */
38602     enable : function(){
38603         this.disabled = false;
38604         this.pnode.removeClass("disabled");
38605     },
38606
38607     /**
38608      * Sets the content for this TabPanelItem.
38609      * @param {String} content The content
38610      * @param {Boolean} loadScripts true to look for and load scripts
38611      */
38612     setContent : function(content, loadScripts){
38613         this.bodyEl.update(content, loadScripts);
38614     },
38615
38616     /**
38617      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38618      * @return {Roo.UpdateManager} The UpdateManager
38619      */
38620     getUpdateManager : function(){
38621         return this.bodyEl.getUpdateManager();
38622     },
38623
38624     /**
38625      * Set a URL to be used to load the content for this TabPanelItem.
38626      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38627      * @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)
38628      * @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)
38629      * @return {Roo.UpdateManager} The UpdateManager
38630      */
38631     setUrl : function(url, params, loadOnce){
38632         if(this.refreshDelegate){
38633             this.un('activate', this.refreshDelegate);
38634         }
38635         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38636         this.on("activate", this.refreshDelegate);
38637         return this.bodyEl.getUpdateManager();
38638     },
38639
38640     /** @private */
38641     _handleRefresh : function(url, params, loadOnce){
38642         if(!loadOnce || !this.loaded){
38643             var updater = this.bodyEl.getUpdateManager();
38644             updater.update(url, params, this._setLoaded.createDelegate(this));
38645         }
38646     },
38647
38648     /**
38649      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38650      *   Will fail silently if the setUrl method has not been called.
38651      *   This does not activate the panel, just updates its content.
38652      */
38653     refresh : function(){
38654         if(this.refreshDelegate){
38655            this.loaded = false;
38656            this.refreshDelegate();
38657         }
38658     },
38659
38660     /** @private */
38661     _setLoaded : function(){
38662         this.loaded = true;
38663     },
38664
38665     /** @private */
38666     closeClick : function(e){
38667         var o = {};
38668         e.stopEvent();
38669         this.fireEvent("beforeclose", this, o);
38670         if(o.cancel !== true){
38671             this.tabPanel.removeTab(this.id);
38672         }
38673     },
38674     /**
38675      * The text displayed in the tooltip for the close icon.
38676      * @type String
38677      */
38678     closeText : "Close this tab"
38679 });
38680 /**
38681 *    This script refer to:
38682 *    Title: International Telephone Input
38683 *    Author: Jack O'Connor
38684 *    Code version:  v12.1.12
38685 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38686 **/
38687
38688 Roo.bootstrap.PhoneInputData = function() {
38689     var d = [
38690       [
38691         "Afghanistan (‫افغانستان‬‎)",
38692         "af",
38693         "93"
38694       ],
38695       [
38696         "Albania (Shqipëri)",
38697         "al",
38698         "355"
38699       ],
38700       [
38701         "Algeria (‫الجزائر‬‎)",
38702         "dz",
38703         "213"
38704       ],
38705       [
38706         "American Samoa",
38707         "as",
38708         "1684"
38709       ],
38710       [
38711         "Andorra",
38712         "ad",
38713         "376"
38714       ],
38715       [
38716         "Angola",
38717         "ao",
38718         "244"
38719       ],
38720       [
38721         "Anguilla",
38722         "ai",
38723         "1264"
38724       ],
38725       [
38726         "Antigua and Barbuda",
38727         "ag",
38728         "1268"
38729       ],
38730       [
38731         "Argentina",
38732         "ar",
38733         "54"
38734       ],
38735       [
38736         "Armenia (Հայաստան)",
38737         "am",
38738         "374"
38739       ],
38740       [
38741         "Aruba",
38742         "aw",
38743         "297"
38744       ],
38745       [
38746         "Australia",
38747         "au",
38748         "61",
38749         0
38750       ],
38751       [
38752         "Austria (Österreich)",
38753         "at",
38754         "43"
38755       ],
38756       [
38757         "Azerbaijan (Azərbaycan)",
38758         "az",
38759         "994"
38760       ],
38761       [
38762         "Bahamas",
38763         "bs",
38764         "1242"
38765       ],
38766       [
38767         "Bahrain (‫البحرين‬‎)",
38768         "bh",
38769         "973"
38770       ],
38771       [
38772         "Bangladesh (বাংলাদেশ)",
38773         "bd",
38774         "880"
38775       ],
38776       [
38777         "Barbados",
38778         "bb",
38779         "1246"
38780       ],
38781       [
38782         "Belarus (Беларусь)",
38783         "by",
38784         "375"
38785       ],
38786       [
38787         "Belgium (België)",
38788         "be",
38789         "32"
38790       ],
38791       [
38792         "Belize",
38793         "bz",
38794         "501"
38795       ],
38796       [
38797         "Benin (Bénin)",
38798         "bj",
38799         "229"
38800       ],
38801       [
38802         "Bermuda",
38803         "bm",
38804         "1441"
38805       ],
38806       [
38807         "Bhutan (འབྲུག)",
38808         "bt",
38809         "975"
38810       ],
38811       [
38812         "Bolivia",
38813         "bo",
38814         "591"
38815       ],
38816       [
38817         "Bosnia and Herzegovina (Босна и Херцеговина)",
38818         "ba",
38819         "387"
38820       ],
38821       [
38822         "Botswana",
38823         "bw",
38824         "267"
38825       ],
38826       [
38827         "Brazil (Brasil)",
38828         "br",
38829         "55"
38830       ],
38831       [
38832         "British Indian Ocean Territory",
38833         "io",
38834         "246"
38835       ],
38836       [
38837         "British Virgin Islands",
38838         "vg",
38839         "1284"
38840       ],
38841       [
38842         "Brunei",
38843         "bn",
38844         "673"
38845       ],
38846       [
38847         "Bulgaria (България)",
38848         "bg",
38849         "359"
38850       ],
38851       [
38852         "Burkina Faso",
38853         "bf",
38854         "226"
38855       ],
38856       [
38857         "Burundi (Uburundi)",
38858         "bi",
38859         "257"
38860       ],
38861       [
38862         "Cambodia (កម្ពុជា)",
38863         "kh",
38864         "855"
38865       ],
38866       [
38867         "Cameroon (Cameroun)",
38868         "cm",
38869         "237"
38870       ],
38871       [
38872         "Canada",
38873         "ca",
38874         "1",
38875         1,
38876         ["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"]
38877       ],
38878       [
38879         "Cape Verde (Kabu Verdi)",
38880         "cv",
38881         "238"
38882       ],
38883       [
38884         "Caribbean Netherlands",
38885         "bq",
38886         "599",
38887         1
38888       ],
38889       [
38890         "Cayman Islands",
38891         "ky",
38892         "1345"
38893       ],
38894       [
38895         "Central African Republic (République centrafricaine)",
38896         "cf",
38897         "236"
38898       ],
38899       [
38900         "Chad (Tchad)",
38901         "td",
38902         "235"
38903       ],
38904       [
38905         "Chile",
38906         "cl",
38907         "56"
38908       ],
38909       [
38910         "China (中国)",
38911         "cn",
38912         "86"
38913       ],
38914       [
38915         "Christmas Island",
38916         "cx",
38917         "61",
38918         2
38919       ],
38920       [
38921         "Cocos (Keeling) Islands",
38922         "cc",
38923         "61",
38924         1
38925       ],
38926       [
38927         "Colombia",
38928         "co",
38929         "57"
38930       ],
38931       [
38932         "Comoros (‫جزر القمر‬‎)",
38933         "km",
38934         "269"
38935       ],
38936       [
38937         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38938         "cd",
38939         "243"
38940       ],
38941       [
38942         "Congo (Republic) (Congo-Brazzaville)",
38943         "cg",
38944         "242"
38945       ],
38946       [
38947         "Cook Islands",
38948         "ck",
38949         "682"
38950       ],
38951       [
38952         "Costa Rica",
38953         "cr",
38954         "506"
38955       ],
38956       [
38957         "Côte d’Ivoire",
38958         "ci",
38959         "225"
38960       ],
38961       [
38962         "Croatia (Hrvatska)",
38963         "hr",
38964         "385"
38965       ],
38966       [
38967         "Cuba",
38968         "cu",
38969         "53"
38970       ],
38971       [
38972         "Curaçao",
38973         "cw",
38974         "599",
38975         0
38976       ],
38977       [
38978         "Cyprus (Κύπρος)",
38979         "cy",
38980         "357"
38981       ],
38982       [
38983         "Czech Republic (Česká republika)",
38984         "cz",
38985         "420"
38986       ],
38987       [
38988         "Denmark (Danmark)",
38989         "dk",
38990         "45"
38991       ],
38992       [
38993         "Djibouti",
38994         "dj",
38995         "253"
38996       ],
38997       [
38998         "Dominica",
38999         "dm",
39000         "1767"
39001       ],
39002       [
39003         "Dominican Republic (República Dominicana)",
39004         "do",
39005         "1",
39006         2,
39007         ["809", "829", "849"]
39008       ],
39009       [
39010         "Ecuador",
39011         "ec",
39012         "593"
39013       ],
39014       [
39015         "Egypt (‫مصر‬‎)",
39016         "eg",
39017         "20"
39018       ],
39019       [
39020         "El Salvador",
39021         "sv",
39022         "503"
39023       ],
39024       [
39025         "Equatorial Guinea (Guinea Ecuatorial)",
39026         "gq",
39027         "240"
39028       ],
39029       [
39030         "Eritrea",
39031         "er",
39032         "291"
39033       ],
39034       [
39035         "Estonia (Eesti)",
39036         "ee",
39037         "372"
39038       ],
39039       [
39040         "Ethiopia",
39041         "et",
39042         "251"
39043       ],
39044       [
39045         "Falkland Islands (Islas Malvinas)",
39046         "fk",
39047         "500"
39048       ],
39049       [
39050         "Faroe Islands (Føroyar)",
39051         "fo",
39052         "298"
39053       ],
39054       [
39055         "Fiji",
39056         "fj",
39057         "679"
39058       ],
39059       [
39060         "Finland (Suomi)",
39061         "fi",
39062         "358",
39063         0
39064       ],
39065       [
39066         "France",
39067         "fr",
39068         "33"
39069       ],
39070       [
39071         "French Guiana (Guyane française)",
39072         "gf",
39073         "594"
39074       ],
39075       [
39076         "French Polynesia (Polynésie française)",
39077         "pf",
39078         "689"
39079       ],
39080       [
39081         "Gabon",
39082         "ga",
39083         "241"
39084       ],
39085       [
39086         "Gambia",
39087         "gm",
39088         "220"
39089       ],
39090       [
39091         "Georgia (საქართველო)",
39092         "ge",
39093         "995"
39094       ],
39095       [
39096         "Germany (Deutschland)",
39097         "de",
39098         "49"
39099       ],
39100       [
39101         "Ghana (Gaana)",
39102         "gh",
39103         "233"
39104       ],
39105       [
39106         "Gibraltar",
39107         "gi",
39108         "350"
39109       ],
39110       [
39111         "Greece (Ελλάδα)",
39112         "gr",
39113         "30"
39114       ],
39115       [
39116         "Greenland (Kalaallit Nunaat)",
39117         "gl",
39118         "299"
39119       ],
39120       [
39121         "Grenada",
39122         "gd",
39123         "1473"
39124       ],
39125       [
39126         "Guadeloupe",
39127         "gp",
39128         "590",
39129         0
39130       ],
39131       [
39132         "Guam",
39133         "gu",
39134         "1671"
39135       ],
39136       [
39137         "Guatemala",
39138         "gt",
39139         "502"
39140       ],
39141       [
39142         "Guernsey",
39143         "gg",
39144         "44",
39145         1
39146       ],
39147       [
39148         "Guinea (Guinée)",
39149         "gn",
39150         "224"
39151       ],
39152       [
39153         "Guinea-Bissau (Guiné Bissau)",
39154         "gw",
39155         "245"
39156       ],
39157       [
39158         "Guyana",
39159         "gy",
39160         "592"
39161       ],
39162       [
39163         "Haiti",
39164         "ht",
39165         "509"
39166       ],
39167       [
39168         "Honduras",
39169         "hn",
39170         "504"
39171       ],
39172       [
39173         "Hong Kong (香港)",
39174         "hk",
39175         "852"
39176       ],
39177       [
39178         "Hungary (Magyarország)",
39179         "hu",
39180         "36"
39181       ],
39182       [
39183         "Iceland (Ísland)",
39184         "is",
39185         "354"
39186       ],
39187       [
39188         "India (भारत)",
39189         "in",
39190         "91"
39191       ],
39192       [
39193         "Indonesia",
39194         "id",
39195         "62"
39196       ],
39197       [
39198         "Iran (‫ایران‬‎)",
39199         "ir",
39200         "98"
39201       ],
39202       [
39203         "Iraq (‫العراق‬‎)",
39204         "iq",
39205         "964"
39206       ],
39207       [
39208         "Ireland",
39209         "ie",
39210         "353"
39211       ],
39212       [
39213         "Isle of Man",
39214         "im",
39215         "44",
39216         2
39217       ],
39218       [
39219         "Israel (‫ישראל‬‎)",
39220         "il",
39221         "972"
39222       ],
39223       [
39224         "Italy (Italia)",
39225         "it",
39226         "39",
39227         0
39228       ],
39229       [
39230         "Jamaica",
39231         "jm",
39232         "1876"
39233       ],
39234       [
39235         "Japan (日本)",
39236         "jp",
39237         "81"
39238       ],
39239       [
39240         "Jersey",
39241         "je",
39242         "44",
39243         3
39244       ],
39245       [
39246         "Jordan (‫الأردن‬‎)",
39247         "jo",
39248         "962"
39249       ],
39250       [
39251         "Kazakhstan (Казахстан)",
39252         "kz",
39253         "7",
39254         1
39255       ],
39256       [
39257         "Kenya",
39258         "ke",
39259         "254"
39260       ],
39261       [
39262         "Kiribati",
39263         "ki",
39264         "686"
39265       ],
39266       [
39267         "Kosovo",
39268         "xk",
39269         "383"
39270       ],
39271       [
39272         "Kuwait (‫الكويت‬‎)",
39273         "kw",
39274         "965"
39275       ],
39276       [
39277         "Kyrgyzstan (Кыргызстан)",
39278         "kg",
39279         "996"
39280       ],
39281       [
39282         "Laos (ລາວ)",
39283         "la",
39284         "856"
39285       ],
39286       [
39287         "Latvia (Latvija)",
39288         "lv",
39289         "371"
39290       ],
39291       [
39292         "Lebanon (‫لبنان‬‎)",
39293         "lb",
39294         "961"
39295       ],
39296       [
39297         "Lesotho",
39298         "ls",
39299         "266"
39300       ],
39301       [
39302         "Liberia",
39303         "lr",
39304         "231"
39305       ],
39306       [
39307         "Libya (‫ليبيا‬‎)",
39308         "ly",
39309         "218"
39310       ],
39311       [
39312         "Liechtenstein",
39313         "li",
39314         "423"
39315       ],
39316       [
39317         "Lithuania (Lietuva)",
39318         "lt",
39319         "370"
39320       ],
39321       [
39322         "Luxembourg",
39323         "lu",
39324         "352"
39325       ],
39326       [
39327         "Macau (澳門)",
39328         "mo",
39329         "853"
39330       ],
39331       [
39332         "Macedonia (FYROM) (Македонија)",
39333         "mk",
39334         "389"
39335       ],
39336       [
39337         "Madagascar (Madagasikara)",
39338         "mg",
39339         "261"
39340       ],
39341       [
39342         "Malawi",
39343         "mw",
39344         "265"
39345       ],
39346       [
39347         "Malaysia",
39348         "my",
39349         "60"
39350       ],
39351       [
39352         "Maldives",
39353         "mv",
39354         "960"
39355       ],
39356       [
39357         "Mali",
39358         "ml",
39359         "223"
39360       ],
39361       [
39362         "Malta",
39363         "mt",
39364         "356"
39365       ],
39366       [
39367         "Marshall Islands",
39368         "mh",
39369         "692"
39370       ],
39371       [
39372         "Martinique",
39373         "mq",
39374         "596"
39375       ],
39376       [
39377         "Mauritania (‫موريتانيا‬‎)",
39378         "mr",
39379         "222"
39380       ],
39381       [
39382         "Mauritius (Moris)",
39383         "mu",
39384         "230"
39385       ],
39386       [
39387         "Mayotte",
39388         "yt",
39389         "262",
39390         1
39391       ],
39392       [
39393         "Mexico (México)",
39394         "mx",
39395         "52"
39396       ],
39397       [
39398         "Micronesia",
39399         "fm",
39400         "691"
39401       ],
39402       [
39403         "Moldova (Republica Moldova)",
39404         "md",
39405         "373"
39406       ],
39407       [
39408         "Monaco",
39409         "mc",
39410         "377"
39411       ],
39412       [
39413         "Mongolia (Монгол)",
39414         "mn",
39415         "976"
39416       ],
39417       [
39418         "Montenegro (Crna Gora)",
39419         "me",
39420         "382"
39421       ],
39422       [
39423         "Montserrat",
39424         "ms",
39425         "1664"
39426       ],
39427       [
39428         "Morocco (‫المغرب‬‎)",
39429         "ma",
39430         "212",
39431         0
39432       ],
39433       [
39434         "Mozambique (Moçambique)",
39435         "mz",
39436         "258"
39437       ],
39438       [
39439         "Myanmar (Burma) (မြန်မာ)",
39440         "mm",
39441         "95"
39442       ],
39443       [
39444         "Namibia (Namibië)",
39445         "na",
39446         "264"
39447       ],
39448       [
39449         "Nauru",
39450         "nr",
39451         "674"
39452       ],
39453       [
39454         "Nepal (नेपाल)",
39455         "np",
39456         "977"
39457       ],
39458       [
39459         "Netherlands (Nederland)",
39460         "nl",
39461         "31"
39462       ],
39463       [
39464         "New Caledonia (Nouvelle-Calédonie)",
39465         "nc",
39466         "687"
39467       ],
39468       [
39469         "New Zealand",
39470         "nz",
39471         "64"
39472       ],
39473       [
39474         "Nicaragua",
39475         "ni",
39476         "505"
39477       ],
39478       [
39479         "Niger (Nijar)",
39480         "ne",
39481         "227"
39482       ],
39483       [
39484         "Nigeria",
39485         "ng",
39486         "234"
39487       ],
39488       [
39489         "Niue",
39490         "nu",
39491         "683"
39492       ],
39493       [
39494         "Norfolk Island",
39495         "nf",
39496         "672"
39497       ],
39498       [
39499         "North Korea (조선 민주주의 인민 공화국)",
39500         "kp",
39501         "850"
39502       ],
39503       [
39504         "Northern Mariana Islands",
39505         "mp",
39506         "1670"
39507       ],
39508       [
39509         "Norway (Norge)",
39510         "no",
39511         "47",
39512         0
39513       ],
39514       [
39515         "Oman (‫عُمان‬‎)",
39516         "om",
39517         "968"
39518       ],
39519       [
39520         "Pakistan (‫پاکستان‬‎)",
39521         "pk",
39522         "92"
39523       ],
39524       [
39525         "Palau",
39526         "pw",
39527         "680"
39528       ],
39529       [
39530         "Palestine (‫فلسطين‬‎)",
39531         "ps",
39532         "970"
39533       ],
39534       [
39535         "Panama (Panamá)",
39536         "pa",
39537         "507"
39538       ],
39539       [
39540         "Papua New Guinea",
39541         "pg",
39542         "675"
39543       ],
39544       [
39545         "Paraguay",
39546         "py",
39547         "595"
39548       ],
39549       [
39550         "Peru (Perú)",
39551         "pe",
39552         "51"
39553       ],
39554       [
39555         "Philippines",
39556         "ph",
39557         "63"
39558       ],
39559       [
39560         "Poland (Polska)",
39561         "pl",
39562         "48"
39563       ],
39564       [
39565         "Portugal",
39566         "pt",
39567         "351"
39568       ],
39569       [
39570         "Puerto Rico",
39571         "pr",
39572         "1",
39573         3,
39574         ["787", "939"]
39575       ],
39576       [
39577         "Qatar (‫قطر‬‎)",
39578         "qa",
39579         "974"
39580       ],
39581       [
39582         "Réunion (La Réunion)",
39583         "re",
39584         "262",
39585         0
39586       ],
39587       [
39588         "Romania (România)",
39589         "ro",
39590         "40"
39591       ],
39592       [
39593         "Russia (Россия)",
39594         "ru",
39595         "7",
39596         0
39597       ],
39598       [
39599         "Rwanda",
39600         "rw",
39601         "250"
39602       ],
39603       [
39604         "Saint Barthélemy",
39605         "bl",
39606         "590",
39607         1
39608       ],
39609       [
39610         "Saint Helena",
39611         "sh",
39612         "290"
39613       ],
39614       [
39615         "Saint Kitts and Nevis",
39616         "kn",
39617         "1869"
39618       ],
39619       [
39620         "Saint Lucia",
39621         "lc",
39622         "1758"
39623       ],
39624       [
39625         "Saint Martin (Saint-Martin (partie française))",
39626         "mf",
39627         "590",
39628         2
39629       ],
39630       [
39631         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39632         "pm",
39633         "508"
39634       ],
39635       [
39636         "Saint Vincent and the Grenadines",
39637         "vc",
39638         "1784"
39639       ],
39640       [
39641         "Samoa",
39642         "ws",
39643         "685"
39644       ],
39645       [
39646         "San Marino",
39647         "sm",
39648         "378"
39649       ],
39650       [
39651         "São Tomé and Príncipe (São Tomé e Príncipe)",
39652         "st",
39653         "239"
39654       ],
39655       [
39656         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39657         "sa",
39658         "966"
39659       ],
39660       [
39661         "Senegal (Sénégal)",
39662         "sn",
39663         "221"
39664       ],
39665       [
39666         "Serbia (Србија)",
39667         "rs",
39668         "381"
39669       ],
39670       [
39671         "Seychelles",
39672         "sc",
39673         "248"
39674       ],
39675       [
39676         "Sierra Leone",
39677         "sl",
39678         "232"
39679       ],
39680       [
39681         "Singapore",
39682         "sg",
39683         "65"
39684       ],
39685       [
39686         "Sint Maarten",
39687         "sx",
39688         "1721"
39689       ],
39690       [
39691         "Slovakia (Slovensko)",
39692         "sk",
39693         "421"
39694       ],
39695       [
39696         "Slovenia (Slovenija)",
39697         "si",
39698         "386"
39699       ],
39700       [
39701         "Solomon Islands",
39702         "sb",
39703         "677"
39704       ],
39705       [
39706         "Somalia (Soomaaliya)",
39707         "so",
39708         "252"
39709       ],
39710       [
39711         "South Africa",
39712         "za",
39713         "27"
39714       ],
39715       [
39716         "South Korea (대한민국)",
39717         "kr",
39718         "82"
39719       ],
39720       [
39721         "South Sudan (‫جنوب السودان‬‎)",
39722         "ss",
39723         "211"
39724       ],
39725       [
39726         "Spain (España)",
39727         "es",
39728         "34"
39729       ],
39730       [
39731         "Sri Lanka (ශ්‍රී ලංකාව)",
39732         "lk",
39733         "94"
39734       ],
39735       [
39736         "Sudan (‫السودان‬‎)",
39737         "sd",
39738         "249"
39739       ],
39740       [
39741         "Suriname",
39742         "sr",
39743         "597"
39744       ],
39745       [
39746         "Svalbard and Jan Mayen",
39747         "sj",
39748         "47",
39749         1
39750       ],
39751       [
39752         "Swaziland",
39753         "sz",
39754         "268"
39755       ],
39756       [
39757         "Sweden (Sverige)",
39758         "se",
39759         "46"
39760       ],
39761       [
39762         "Switzerland (Schweiz)",
39763         "ch",
39764         "41"
39765       ],
39766       [
39767         "Syria (‫سوريا‬‎)",
39768         "sy",
39769         "963"
39770       ],
39771       [
39772         "Taiwan (台灣)",
39773         "tw",
39774         "886"
39775       ],
39776       [
39777         "Tajikistan",
39778         "tj",
39779         "992"
39780       ],
39781       [
39782         "Tanzania",
39783         "tz",
39784         "255"
39785       ],
39786       [
39787         "Thailand (ไทย)",
39788         "th",
39789         "66"
39790       ],
39791       [
39792         "Timor-Leste",
39793         "tl",
39794         "670"
39795       ],
39796       [
39797         "Togo",
39798         "tg",
39799         "228"
39800       ],
39801       [
39802         "Tokelau",
39803         "tk",
39804         "690"
39805       ],
39806       [
39807         "Tonga",
39808         "to",
39809         "676"
39810       ],
39811       [
39812         "Trinidad and Tobago",
39813         "tt",
39814         "1868"
39815       ],
39816       [
39817         "Tunisia (‫تونس‬‎)",
39818         "tn",
39819         "216"
39820       ],
39821       [
39822         "Turkey (Türkiye)",
39823         "tr",
39824         "90"
39825       ],
39826       [
39827         "Turkmenistan",
39828         "tm",
39829         "993"
39830       ],
39831       [
39832         "Turks and Caicos Islands",
39833         "tc",
39834         "1649"
39835       ],
39836       [
39837         "Tuvalu",
39838         "tv",
39839         "688"
39840       ],
39841       [
39842         "U.S. Virgin Islands",
39843         "vi",
39844         "1340"
39845       ],
39846       [
39847         "Uganda",
39848         "ug",
39849         "256"
39850       ],
39851       [
39852         "Ukraine (Україна)",
39853         "ua",
39854         "380"
39855       ],
39856       [
39857         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39858         "ae",
39859         "971"
39860       ],
39861       [
39862         "United Kingdom",
39863         "gb",
39864         "44",
39865         0
39866       ],
39867       [
39868         "United States",
39869         "us",
39870         "1",
39871         0
39872       ],
39873       [
39874         "Uruguay",
39875         "uy",
39876         "598"
39877       ],
39878       [
39879         "Uzbekistan (Oʻzbekiston)",
39880         "uz",
39881         "998"
39882       ],
39883       [
39884         "Vanuatu",
39885         "vu",
39886         "678"
39887       ],
39888       [
39889         "Vatican City (Città del Vaticano)",
39890         "va",
39891         "39",
39892         1
39893       ],
39894       [
39895         "Venezuela",
39896         "ve",
39897         "58"
39898       ],
39899       [
39900         "Vietnam (Việt Nam)",
39901         "vn",
39902         "84"
39903       ],
39904       [
39905         "Wallis and Futuna (Wallis-et-Futuna)",
39906         "wf",
39907         "681"
39908       ],
39909       [
39910         "Western Sahara (‫الصحراء الغربية‬‎)",
39911         "eh",
39912         "212",
39913         1
39914       ],
39915       [
39916         "Yemen (‫اليمن‬‎)",
39917         "ye",
39918         "967"
39919       ],
39920       [
39921         "Zambia",
39922         "zm",
39923         "260"
39924       ],
39925       [
39926         "Zimbabwe",
39927         "zw",
39928         "263"
39929       ],
39930       [
39931         "Åland Islands",
39932         "ax",
39933         "358",
39934         1
39935       ]
39936   ];
39937   
39938   return d;
39939 }/**
39940 *    This script refer to:
39941 *    Title: International Telephone Input
39942 *    Author: Jack O'Connor
39943 *    Code version:  v12.1.12
39944 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39945 **/
39946
39947 /**
39948  * @class Roo.bootstrap.PhoneInput
39949  * @extends Roo.bootstrap.TriggerField
39950  * An input with International dial-code selection
39951  
39952  * @cfg {String} defaultDialCode default '+852'
39953  * @cfg {Array} preferedCountries default []
39954   
39955  * @constructor
39956  * Create a new PhoneInput.
39957  * @param {Object} config Configuration options
39958  */
39959
39960 Roo.bootstrap.PhoneInput = function(config) {
39961     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39962 };
39963
39964 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39965         
39966         listWidth: undefined,
39967         
39968         selectedClass: 'active',
39969         
39970         invalidClass : "has-warning",
39971         
39972         validClass: 'has-success',
39973         
39974         allowed: '0123456789',
39975         
39976         max_length: 15,
39977         
39978         /**
39979          * @cfg {String} defaultDialCode The default dial code when initializing the input
39980          */
39981         defaultDialCode: '+852',
39982         
39983         /**
39984          * @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
39985          */
39986         preferedCountries: false,
39987         
39988         getAutoCreate : function()
39989         {
39990             var data = Roo.bootstrap.PhoneInputData();
39991             var align = this.labelAlign || this.parentLabelAlign();
39992             var id = Roo.id();
39993             
39994             this.allCountries = [];
39995             this.dialCodeMapping = [];
39996             
39997             for (var i = 0; i < data.length; i++) {
39998               var c = data[i];
39999               this.allCountries[i] = {
40000                 name: c[0],
40001                 iso2: c[1],
40002                 dialCode: c[2],
40003                 priority: c[3] || 0,
40004                 areaCodes: c[4] || null
40005               };
40006               this.dialCodeMapping[c[2]] = {
40007                   name: c[0],
40008                   iso2: c[1],
40009                   priority: c[3] || 0,
40010                   areaCodes: c[4] || null
40011               };
40012             }
40013             
40014             var cfg = {
40015                 cls: 'form-group',
40016                 cn: []
40017             };
40018             
40019             var input =  {
40020                 tag: 'input',
40021                 id : id,
40022                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40023                 maxlength: this.max_length,
40024                 cls : 'form-control tel-input',
40025                 autocomplete: 'new-password'
40026             };
40027             
40028             var hiddenInput = {
40029                 tag: 'input',
40030                 type: 'hidden',
40031                 cls: 'hidden-tel-input'
40032             };
40033             
40034             if (this.name) {
40035                 hiddenInput.name = this.name;
40036             }
40037             
40038             if (this.disabled) {
40039                 input.disabled = true;
40040             }
40041             
40042             var flag_container = {
40043                 tag: 'div',
40044                 cls: 'flag-box',
40045                 cn: [
40046                     {
40047                         tag: 'div',
40048                         cls: 'flag'
40049                     },
40050                     {
40051                         tag: 'div',
40052                         cls: 'caret'
40053                     }
40054                 ]
40055             };
40056             
40057             var box = {
40058                 tag: 'div',
40059                 cls: this.hasFeedback ? 'has-feedback' : '',
40060                 cn: [
40061                     hiddenInput,
40062                     input,
40063                     {
40064                         tag: 'input',
40065                         cls: 'dial-code-holder',
40066                         disabled: true
40067                     }
40068                 ]
40069             };
40070             
40071             var container = {
40072                 cls: 'roo-select2-container input-group',
40073                 cn: [
40074                     flag_container,
40075                     box
40076                 ]
40077             };
40078             
40079             if (this.fieldLabel.length) {
40080                 var indicator = {
40081                     tag: 'i',
40082                     tooltip: 'This field is required'
40083                 };
40084                 
40085                 var label = {
40086                     tag: 'label',
40087                     'for':  id,
40088                     cls: 'control-label',
40089                     cn: []
40090                 };
40091                 
40092                 var label_text = {
40093                     tag: 'span',
40094                     html: this.fieldLabel
40095                 };
40096                 
40097                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40098                 label.cn = [
40099                     indicator,
40100                     label_text
40101                 ];
40102                 
40103                 if(this.indicatorpos == 'right') {
40104                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40105                     label.cn = [
40106                         label_text,
40107                         indicator
40108                     ];
40109                 }
40110                 
40111                 if(align == 'left') {
40112                     container = {
40113                         tag: 'div',
40114                         cn: [
40115                             container
40116                         ]
40117                     };
40118                     
40119                     if(this.labelWidth > 12){
40120                         label.style = "width: " + this.labelWidth + 'px';
40121                     }
40122                     if(this.labelWidth < 13 && this.labelmd == 0){
40123                         this.labelmd = this.labelWidth;
40124                     }
40125                     if(this.labellg > 0){
40126                         label.cls += ' col-lg-' + this.labellg;
40127                         input.cls += ' col-lg-' + (12 - this.labellg);
40128                     }
40129                     if(this.labelmd > 0){
40130                         label.cls += ' col-md-' + this.labelmd;
40131                         container.cls += ' col-md-' + (12 - this.labelmd);
40132                     }
40133                     if(this.labelsm > 0){
40134                         label.cls += ' col-sm-' + this.labelsm;
40135                         container.cls += ' col-sm-' + (12 - this.labelsm);
40136                     }
40137                     if(this.labelxs > 0){
40138                         label.cls += ' col-xs-' + this.labelxs;
40139                         container.cls += ' col-xs-' + (12 - this.labelxs);
40140                     }
40141                 }
40142             }
40143             
40144             cfg.cn = [
40145                 label,
40146                 container
40147             ];
40148             
40149             var settings = this;
40150             
40151             ['xs','sm','md','lg'].map(function(size){
40152                 if (settings[size]) {
40153                     cfg.cls += ' col-' + size + '-' + settings[size];
40154                 }
40155             });
40156             
40157             this.store = new Roo.data.Store({
40158                 proxy : new Roo.data.MemoryProxy({}),
40159                 reader : new Roo.data.JsonReader({
40160                     fields : [
40161                         {
40162                             'name' : 'name',
40163                             'type' : 'string'
40164                         },
40165                         {
40166                             'name' : 'iso2',
40167                             'type' : 'string'
40168                         },
40169                         {
40170                             'name' : 'dialCode',
40171                             'type' : 'string'
40172                         },
40173                         {
40174                             'name' : 'priority',
40175                             'type' : 'string'
40176                         },
40177                         {
40178                             'name' : 'areaCodes',
40179                             'type' : 'string'
40180                         }
40181                     ]
40182                 })
40183             });
40184             
40185             if(!this.preferedCountries) {
40186                 this.preferedCountries = [
40187                     'hk',
40188                     'gb',
40189                     'us'
40190                 ];
40191             }
40192             
40193             var p = this.preferedCountries.reverse();
40194             
40195             if(p) {
40196                 for (var i = 0; i < p.length; i++) {
40197                     for (var j = 0; j < this.allCountries.length; j++) {
40198                         if(this.allCountries[j].iso2 == p[i]) {
40199                             var t = this.allCountries[j];
40200                             this.allCountries.splice(j,1);
40201                             this.allCountries.unshift(t);
40202                         }
40203                     } 
40204                 }
40205             }
40206             
40207             this.store.proxy.data = {
40208                 success: true,
40209                 data: this.allCountries
40210             };
40211             
40212             return cfg;
40213         },
40214         
40215         initEvents : function()
40216         {
40217             this.createList();
40218             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40219             
40220             this.indicator = this.indicatorEl();
40221             this.flag = this.flagEl();
40222             this.dialCodeHolder = this.dialCodeHolderEl();
40223             
40224             this.trigger = this.el.select('div.flag-box',true).first();
40225             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40226             
40227             var _this = this;
40228             
40229             (function(){
40230                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40231                 _this.list.setWidth(lw);
40232             }).defer(100);
40233             
40234             this.list.on('mouseover', this.onViewOver, this);
40235             this.list.on('mousemove', this.onViewMove, this);
40236             this.inputEl().on("keyup", this.onKeyUp, this);
40237             this.inputEl().on("keypress", this.onKeyPress, this);
40238             
40239             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40240
40241             this.view = new Roo.View(this.list, this.tpl, {
40242                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40243             });
40244             
40245             this.view.on('click', this.onViewClick, this);
40246             this.setValue(this.defaultDialCode);
40247         },
40248         
40249         onTriggerClick : function(e)
40250         {
40251             Roo.log('trigger click');
40252             if(this.disabled){
40253                 return;
40254             }
40255             
40256             if(this.isExpanded()){
40257                 this.collapse();
40258                 this.hasFocus = false;
40259             }else {
40260                 this.store.load({});
40261                 this.hasFocus = true;
40262                 this.expand();
40263             }
40264         },
40265         
40266         isExpanded : function()
40267         {
40268             return this.list.isVisible();
40269         },
40270         
40271         collapse : function()
40272         {
40273             if(!this.isExpanded()){
40274                 return;
40275             }
40276             this.list.hide();
40277             Roo.get(document).un('mousedown', this.collapseIf, this);
40278             Roo.get(document).un('mousewheel', this.collapseIf, this);
40279             this.fireEvent('collapse', this);
40280             this.validate();
40281         },
40282         
40283         expand : function()
40284         {
40285             Roo.log('expand');
40286
40287             if(this.isExpanded() || !this.hasFocus){
40288                 return;
40289             }
40290             
40291             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40292             this.list.setWidth(lw);
40293             
40294             this.list.show();
40295             this.restrictHeight();
40296             
40297             Roo.get(document).on('mousedown', this.collapseIf, this);
40298             Roo.get(document).on('mousewheel', this.collapseIf, this);
40299             
40300             this.fireEvent('expand', this);
40301         },
40302         
40303         restrictHeight : function()
40304         {
40305             this.list.alignTo(this.inputEl(), this.listAlign);
40306             this.list.alignTo(this.inputEl(), this.listAlign);
40307         },
40308         
40309         onViewOver : function(e, t)
40310         {
40311             if(this.inKeyMode){
40312                 return;
40313             }
40314             var item = this.view.findItemFromChild(t);
40315             
40316             if(item){
40317                 var index = this.view.indexOf(item);
40318                 this.select(index, false);
40319             }
40320         },
40321
40322         // private
40323         onViewClick : function(view, doFocus, el, e)
40324         {
40325             var index = this.view.getSelectedIndexes()[0];
40326             
40327             var r = this.store.getAt(index);
40328             
40329             if(r){
40330                 this.onSelect(r, index);
40331             }
40332             if(doFocus !== false && !this.blockFocus){
40333                 this.inputEl().focus();
40334             }
40335         },
40336         
40337         onViewMove : function(e, t)
40338         {
40339             this.inKeyMode = false;
40340         },
40341         
40342         select : function(index, scrollIntoView)
40343         {
40344             this.selectedIndex = index;
40345             this.view.select(index);
40346             if(scrollIntoView !== false){
40347                 var el = this.view.getNode(index);
40348                 if(el){
40349                     this.list.scrollChildIntoView(el, false);
40350                 }
40351             }
40352         },
40353         
40354         createList : function()
40355         {
40356             this.list = Roo.get(document.body).createChild({
40357                 tag: 'ul',
40358                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40359                 style: 'display:none'
40360             });
40361             
40362             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40363         },
40364         
40365         collapseIf : function(e)
40366         {
40367             var in_combo  = e.within(this.el);
40368             var in_list =  e.within(this.list);
40369             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40370             
40371             if (in_combo || in_list || is_list) {
40372                 return;
40373             }
40374             this.collapse();
40375         },
40376         
40377         onSelect : function(record, index)
40378         {
40379             if(this.fireEvent('beforeselect', this, record, index) !== false){
40380                 
40381                 this.setFlagClass(record.data.iso2);
40382                 this.setDialCode(record.data.dialCode);
40383                 this.hasFocus = false;
40384                 this.collapse();
40385                 this.fireEvent('select', this, record, index);
40386             }
40387         },
40388         
40389         flagEl : function()
40390         {
40391             var flag = this.el.select('div.flag',true).first();
40392             if(!flag){
40393                 return false;
40394             }
40395             return flag;
40396         },
40397         
40398         dialCodeHolderEl : function()
40399         {
40400             var d = this.el.select('input.dial-code-holder',true).first();
40401             if(!d){
40402                 return false;
40403             }
40404             return d;
40405         },
40406         
40407         setDialCode : function(v)
40408         {
40409             this.dialCodeHolder.dom.value = '+'+v;
40410         },
40411         
40412         setFlagClass : function(n)
40413         {
40414             this.flag.dom.className = 'flag '+n;
40415         },
40416         
40417         getValue : function()
40418         {
40419             var v = this.inputEl().getValue();
40420             if(this.dialCodeHolder) {
40421                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40422             }
40423             return v;
40424         },
40425         
40426         setValue : function(v)
40427         {
40428             var d = this.getDialCode(v);
40429             
40430             //invalid dial code
40431             if(v.length == 0 || !d || d.length == 0) {
40432                 if(this.rendered){
40433                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40434                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40435                 }
40436                 return;
40437             }
40438             
40439             //valid dial code
40440             this.setFlagClass(this.dialCodeMapping[d].iso2);
40441             this.setDialCode(d);
40442             this.inputEl().dom.value = v.replace('+'+d,'');
40443             this.hiddenEl().dom.value = this.getValue();
40444             
40445             this.validate();
40446         },
40447         
40448         getDialCode : function(v)
40449         {
40450             v = v ||  '';
40451             
40452             if (v.length == 0) {
40453                 return this.dialCodeHolder.dom.value;
40454             }
40455             
40456             var dialCode = "";
40457             if (v.charAt(0) != "+") {
40458                 return false;
40459             }
40460             var numericChars = "";
40461             for (var i = 1; i < v.length; i++) {
40462               var c = v.charAt(i);
40463               if (!isNaN(c)) {
40464                 numericChars += c;
40465                 if (this.dialCodeMapping[numericChars]) {
40466                   dialCode = v.substr(1, i);
40467                 }
40468                 if (numericChars.length == 4) {
40469                   break;
40470                 }
40471               }
40472             }
40473             return dialCode;
40474         },
40475         
40476         reset : function()
40477         {
40478             this.setValue(this.defaultDialCode);
40479             this.validate();
40480         },
40481         
40482         hiddenEl : function()
40483         {
40484             return this.el.select('input.hidden-tel-input',true).first();
40485         },
40486         
40487         // after setting val
40488         onKeyUp : function(e){
40489             this.setValue(this.getValue());
40490         },
40491         
40492         onKeyPress : function(e){
40493             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40494                 e.stopEvent();
40495             }
40496         }
40497         
40498 });
40499 /**
40500  * @class Roo.bootstrap.MoneyField
40501  * @extends Roo.bootstrap.ComboBox
40502  * Bootstrap MoneyField class
40503  * 
40504  * @constructor
40505  * Create a new MoneyField.
40506  * @param {Object} config Configuration options
40507  */
40508
40509 Roo.bootstrap.MoneyField = function(config) {
40510     
40511     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40512     
40513 };
40514
40515 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40516     
40517     /**
40518      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40519      */
40520     allowDecimals : true,
40521     /**
40522      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40523      */
40524     decimalSeparator : ".",
40525     /**
40526      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40527      */
40528     decimalPrecision : 0,
40529     /**
40530      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40531      */
40532     allowNegative : true,
40533     /**
40534      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40535      */
40536     allowZero: true,
40537     /**
40538      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40539      */
40540     minValue : Number.NEGATIVE_INFINITY,
40541     /**
40542      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40543      */
40544     maxValue : Number.MAX_VALUE,
40545     /**
40546      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40547      */
40548     minText : "The minimum value for this field is {0}",
40549     /**
40550      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40551      */
40552     maxText : "The maximum value for this field is {0}",
40553     /**
40554      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40555      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40556      */
40557     nanText : "{0} is not a valid number",
40558     /**
40559      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40560      */
40561     castInt : true,
40562     /**
40563      * @cfg {String} defaults currency of the MoneyField
40564      * value should be in lkey
40565      */
40566     defaultCurrency : false,
40567     /**
40568      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40569      */
40570     thousandsDelimiter : false,
40571     /**
40572      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40573      */
40574     max_length: false,
40575     
40576     inputlg : 9,
40577     inputmd : 9,
40578     inputsm : 9,
40579     inputxs : 6,
40580     
40581     store : false,
40582     
40583     getAutoCreate : function()
40584     {
40585         var align = this.labelAlign || this.parentLabelAlign();
40586         
40587         var id = Roo.id();
40588
40589         var cfg = {
40590             cls: 'form-group',
40591             cn: []
40592         };
40593
40594         var input =  {
40595             tag: 'input',
40596             id : id,
40597             cls : 'form-control roo-money-amount-input',
40598             autocomplete: 'new-password'
40599         };
40600         
40601         var hiddenInput = {
40602             tag: 'input',
40603             type: 'hidden',
40604             id: Roo.id(),
40605             cls: 'hidden-number-input'
40606         };
40607         
40608         if(this.max_length) {
40609             input.maxlength = this.max_length; 
40610         }
40611         
40612         if (this.name) {
40613             hiddenInput.name = this.name;
40614         }
40615
40616         if (this.disabled) {
40617             input.disabled = true;
40618         }
40619
40620         var clg = 12 - this.inputlg;
40621         var cmd = 12 - this.inputmd;
40622         var csm = 12 - this.inputsm;
40623         var cxs = 12 - this.inputxs;
40624         
40625         var container = {
40626             tag : 'div',
40627             cls : 'row roo-money-field',
40628             cn : [
40629                 {
40630                     tag : 'div',
40631                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40632                     cn : [
40633                         {
40634                             tag : 'div',
40635                             cls: 'roo-select2-container input-group',
40636                             cn: [
40637                                 {
40638                                     tag : 'input',
40639                                     cls : 'form-control roo-money-currency-input',
40640                                     autocomplete: 'new-password',
40641                                     readOnly : 1,
40642                                     name : this.currencyName
40643                                 },
40644                                 {
40645                                     tag :'span',
40646                                     cls : 'input-group-addon',
40647                                     cn : [
40648                                         {
40649                                             tag: 'span',
40650                                             cls: 'caret'
40651                                         }
40652                                     ]
40653                                 }
40654                             ]
40655                         }
40656                     ]
40657                 },
40658                 {
40659                     tag : 'div',
40660                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40661                     cn : [
40662                         {
40663                             tag: 'div',
40664                             cls: this.hasFeedback ? 'has-feedback' : '',
40665                             cn: [
40666                                 input
40667                             ]
40668                         }
40669                     ]
40670                 }
40671             ]
40672             
40673         };
40674         
40675         if (this.fieldLabel.length) {
40676             var indicator = {
40677                 tag: 'i',
40678                 tooltip: 'This field is required'
40679             };
40680
40681             var label = {
40682                 tag: 'label',
40683                 'for':  id,
40684                 cls: 'control-label',
40685                 cn: []
40686             };
40687
40688             var label_text = {
40689                 tag: 'span',
40690                 html: this.fieldLabel
40691             };
40692
40693             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40694             label.cn = [
40695                 indicator,
40696                 label_text
40697             ];
40698
40699             if(this.indicatorpos == 'right') {
40700                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40701                 label.cn = [
40702                     label_text,
40703                     indicator
40704                 ];
40705             }
40706
40707             if(align == 'left') {
40708                 container = {
40709                     tag: 'div',
40710                     cn: [
40711                         container
40712                     ]
40713                 };
40714
40715                 if(this.labelWidth > 12){
40716                     label.style = "width: " + this.labelWidth + 'px';
40717                 }
40718                 if(this.labelWidth < 13 && this.labelmd == 0){
40719                     this.labelmd = this.labelWidth;
40720                 }
40721                 if(this.labellg > 0){
40722                     label.cls += ' col-lg-' + this.labellg;
40723                     input.cls += ' col-lg-' + (12 - this.labellg);
40724                 }
40725                 if(this.labelmd > 0){
40726                     label.cls += ' col-md-' + this.labelmd;
40727                     container.cls += ' col-md-' + (12 - this.labelmd);
40728                 }
40729                 if(this.labelsm > 0){
40730                     label.cls += ' col-sm-' + this.labelsm;
40731                     container.cls += ' col-sm-' + (12 - this.labelsm);
40732                 }
40733                 if(this.labelxs > 0){
40734                     label.cls += ' col-xs-' + this.labelxs;
40735                     container.cls += ' col-xs-' + (12 - this.labelxs);
40736                 }
40737             }
40738         }
40739
40740         cfg.cn = [
40741             label,
40742             container,
40743             hiddenInput
40744         ];
40745         
40746         var settings = this;
40747
40748         ['xs','sm','md','lg'].map(function(size){
40749             if (settings[size]) {
40750                 cfg.cls += ' col-' + size + '-' + settings[size];
40751             }
40752         });
40753         
40754         return cfg;
40755     },
40756     
40757     initEvents : function()
40758     {
40759         this.indicator = this.indicatorEl();
40760         
40761         this.initCurrencyEvent();
40762         
40763         this.initNumberEvent();
40764     },
40765     
40766     initCurrencyEvent : function()
40767     {
40768         if (!this.store) {
40769             throw "can not find store for combo";
40770         }
40771         
40772         this.store = Roo.factory(this.store, Roo.data);
40773         this.store.parent = this;
40774         
40775         this.createList();
40776         
40777         this.triggerEl = this.el.select('.input-group-addon', true).first();
40778         
40779         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40780         
40781         var _this = this;
40782         
40783         (function(){
40784             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40785             _this.list.setWidth(lw);
40786         }).defer(100);
40787         
40788         this.list.on('mouseover', this.onViewOver, this);
40789         this.list.on('mousemove', this.onViewMove, this);
40790         this.list.on('scroll', this.onViewScroll, this);
40791         
40792         if(!this.tpl){
40793             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40794         }
40795         
40796         this.view = new Roo.View(this.list, this.tpl, {
40797             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40798         });
40799         
40800         this.view.on('click', this.onViewClick, this);
40801         
40802         this.store.on('beforeload', this.onBeforeLoad, this);
40803         this.store.on('load', this.onLoad, this);
40804         this.store.on('loadexception', this.onLoadException, this);
40805         
40806         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40807             "up" : function(e){
40808                 this.inKeyMode = true;
40809                 this.selectPrev();
40810             },
40811
40812             "down" : function(e){
40813                 if(!this.isExpanded()){
40814                     this.onTriggerClick();
40815                 }else{
40816                     this.inKeyMode = true;
40817                     this.selectNext();
40818                 }
40819             },
40820
40821             "enter" : function(e){
40822                 this.collapse();
40823                 
40824                 if(this.fireEvent("specialkey", this, e)){
40825                     this.onViewClick(false);
40826                 }
40827                 
40828                 return true;
40829             },
40830
40831             "esc" : function(e){
40832                 this.collapse();
40833             },
40834
40835             "tab" : function(e){
40836                 this.collapse();
40837                 
40838                 if(this.fireEvent("specialkey", this, e)){
40839                     this.onViewClick(false);
40840                 }
40841                 
40842                 return true;
40843             },
40844
40845             scope : this,
40846
40847             doRelay : function(foo, bar, hname){
40848                 if(hname == 'down' || this.scope.isExpanded()){
40849                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40850                 }
40851                 return true;
40852             },
40853
40854             forceKeyDown: true
40855         });
40856         
40857         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40858         
40859     },
40860     
40861     initNumberEvent : function(e)
40862     {
40863         this.inputEl().on("keydown" , this.fireKey,  this);
40864         this.inputEl().on("focus", this.onFocus,  this);
40865         this.inputEl().on("blur", this.onBlur,  this);
40866         
40867         this.inputEl().relayEvent('keyup', this);
40868         
40869         if(this.indicator){
40870             this.indicator.addClass('invisible');
40871         }
40872  
40873         this.originalValue = this.getValue();
40874         
40875         if(this.validationEvent == 'keyup'){
40876             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40877             this.inputEl().on('keyup', this.filterValidation, this);
40878         }
40879         else if(this.validationEvent !== false){
40880             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40881         }
40882         
40883         if(this.selectOnFocus){
40884             this.on("focus", this.preFocus, this);
40885             
40886         }
40887         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40888             this.inputEl().on("keypress", this.filterKeys, this);
40889         } else {
40890             this.inputEl().relayEvent('keypress', this);
40891         }
40892         
40893         var allowed = "0123456789";
40894         
40895         if(this.allowDecimals){
40896             allowed += this.decimalSeparator;
40897         }
40898         
40899         if(this.allowNegative){
40900             allowed += "-";
40901         }
40902         
40903         if(this.thousandsDelimiter) {
40904             allowed += ",";
40905         }
40906         
40907         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40908         
40909         var keyPress = function(e){
40910             
40911             var k = e.getKey();
40912             
40913             var c = e.getCharCode();
40914             
40915             if(
40916                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40917                     allowed.indexOf(String.fromCharCode(c)) === -1
40918             ){
40919                 e.stopEvent();
40920                 return;
40921             }
40922             
40923             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40924                 return;
40925             }
40926             
40927             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40928                 e.stopEvent();
40929             }
40930         };
40931         
40932         this.inputEl().on("keypress", keyPress, this);
40933         
40934     },
40935     
40936     onTriggerClick : function(e)
40937     {   
40938         if(this.disabled){
40939             return;
40940         }
40941         
40942         this.page = 0;
40943         this.loadNext = false;
40944         
40945         if(this.isExpanded()){
40946             this.collapse();
40947             return;
40948         }
40949         
40950         this.hasFocus = true;
40951         
40952         if(this.triggerAction == 'all') {
40953             this.doQuery(this.allQuery, true);
40954             return;
40955         }
40956         
40957         this.doQuery(this.getRawValue());
40958     },
40959     
40960     getCurrency : function()
40961     {   
40962         var v = this.currencyEl().getValue();
40963         
40964         return v;
40965     },
40966     
40967     restrictHeight : function()
40968     {
40969         this.list.alignTo(this.currencyEl(), this.listAlign);
40970         this.list.alignTo(this.currencyEl(), this.listAlign);
40971     },
40972     
40973     onViewClick : function(view, doFocus, el, e)
40974     {
40975         var index = this.view.getSelectedIndexes()[0];
40976         
40977         var r = this.store.getAt(index);
40978         
40979         if(r){
40980             this.onSelect(r, index);
40981         }
40982     },
40983     
40984     onSelect : function(record, index){
40985         
40986         if(this.fireEvent('beforeselect', this, record, index) !== false){
40987         
40988             this.setFromCurrencyData(index > -1 ? record.data : false);
40989             
40990             this.collapse();
40991             
40992             this.fireEvent('select', this, record, index);
40993         }
40994     },
40995     
40996     setFromCurrencyData : function(o)
40997     {
40998         var currency = '';
40999         
41000         this.lastCurrency = o;
41001         
41002         if (this.currencyField) {
41003             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41004         } else {
41005             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41006         }
41007         
41008         this.lastSelectionText = currency;
41009         
41010         //setting default currency
41011         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41012             this.setCurrency(this.defaultCurrency);
41013             return;
41014         }
41015         
41016         this.setCurrency(currency);
41017     },
41018     
41019     setFromData : function(o)
41020     {
41021         var c = {};
41022         
41023         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41024         
41025         this.setFromCurrencyData(c);
41026         
41027         var value = '';
41028         
41029         if (this.name) {
41030             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41031         } else {
41032             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41033         }
41034         
41035         this.setValue(value);
41036         
41037     },
41038     
41039     setCurrency : function(v)
41040     {   
41041         this.currencyValue = v;
41042         
41043         if(this.rendered){
41044             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41045             this.validate();
41046         }
41047     },
41048     
41049     setValue : function(v)
41050     {
41051         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41052         
41053         this.value = v;
41054         
41055         if(this.rendered){
41056             
41057             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41058             
41059             this.inputEl().dom.value = (v == '') ? '' :
41060                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41061             
41062             if(!this.allowZero && v === '0') {
41063                 this.hiddenEl().dom.value = '';
41064                 this.inputEl().dom.value = '';
41065             }
41066             
41067             this.validate();
41068         }
41069     },
41070     
41071     getRawValue : function()
41072     {
41073         var v = this.inputEl().getValue();
41074         
41075         return v;
41076     },
41077     
41078     getValue : function()
41079     {
41080         return this.fixPrecision(this.parseValue(this.getRawValue()));
41081     },
41082     
41083     parseValue : function(value)
41084     {
41085         if(this.thousandsDelimiter) {
41086             value += "";
41087             r = new RegExp(",", "g");
41088             value = value.replace(r, "");
41089         }
41090         
41091         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41092         return isNaN(value) ? '' : value;
41093         
41094     },
41095     
41096     fixPrecision : function(value)
41097     {
41098         if(this.thousandsDelimiter) {
41099             value += "";
41100             r = new RegExp(",", "g");
41101             value = value.replace(r, "");
41102         }
41103         
41104         var nan = isNaN(value);
41105         
41106         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41107             return nan ? '' : value;
41108         }
41109         return parseFloat(value).toFixed(this.decimalPrecision);
41110     },
41111     
41112     decimalPrecisionFcn : function(v)
41113     {
41114         return Math.floor(v);
41115     },
41116     
41117     validateValue : function(value)
41118     {
41119         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41120             return false;
41121         }
41122         
41123         var num = this.parseValue(value);
41124         
41125         if(isNaN(num)){
41126             this.markInvalid(String.format(this.nanText, value));
41127             return false;
41128         }
41129         
41130         if(num < this.minValue){
41131             this.markInvalid(String.format(this.minText, this.minValue));
41132             return false;
41133         }
41134         
41135         if(num > this.maxValue){
41136             this.markInvalid(String.format(this.maxText, this.maxValue));
41137             return false;
41138         }
41139         
41140         return true;
41141     },
41142     
41143     validate : function()
41144     {
41145         if(this.disabled || this.allowBlank){
41146             this.markValid();
41147             return true;
41148         }
41149         
41150         var currency = this.getCurrency();
41151         
41152         if(this.validateValue(this.getRawValue()) && currency.length){
41153             this.markValid();
41154             return true;
41155         }
41156         
41157         this.markInvalid();
41158         return false;
41159     },
41160     
41161     getName: function()
41162     {
41163         return this.name;
41164     },
41165     
41166     beforeBlur : function()
41167     {
41168         if(!this.castInt){
41169             return;
41170         }
41171         
41172         var v = this.parseValue(this.getRawValue());
41173         
41174         if(v || v == 0){
41175             this.setValue(v);
41176         }
41177     },
41178     
41179     onBlur : function()
41180     {
41181         this.beforeBlur();
41182         
41183         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41184             //this.el.removeClass(this.focusClass);
41185         }
41186         
41187         this.hasFocus = false;
41188         
41189         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41190             this.validate();
41191         }
41192         
41193         var v = this.getValue();
41194         
41195         if(String(v) !== String(this.startValue)){
41196             this.fireEvent('change', this, v, this.startValue);
41197         }
41198         
41199         this.fireEvent("blur", this);
41200     },
41201     
41202     inputEl : function()
41203     {
41204         return this.el.select('.roo-money-amount-input', true).first();
41205     },
41206     
41207     currencyEl : function()
41208     {
41209         return this.el.select('.roo-money-currency-input', true).first();
41210     },
41211     
41212     hiddenEl : function()
41213     {
41214         return this.el.select('input.hidden-number-input',true).first();
41215     }
41216     
41217 });