roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @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)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     hide: function() {
921        
922      
923         this.el.hide();   
924     },
925     show: function() {
926        
927         this.el.show();   
928     },
929     setWeight : function(str)
930     {
931         this.el.removeClass(this.weightClass);
932         this.el.addClass('btn-' + str);        
933     }
934     
935     
936 });
937
938  /*
939  * - LGPL
940  *
941  * column
942  * 
943  */
944
945 /**
946  * @class Roo.bootstrap.Column
947  * @extends Roo.bootstrap.Component
948  * Bootstrap Column class
949  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
957  *
958  * 
959  * @cfg {Boolean} hidden (true|false) hide the element
960  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961  * @cfg {String} fa (ban|check|...) font awesome icon
962  * @cfg {Number} fasize (1|2|....) font awsome size
963
964  * @cfg {String} icon (info-sign|check|...) glyphicon name
965
966  * @cfg {String} html content of column.
967  * 
968  * @constructor
969  * Create a new Column
970  * @param {Object} config The config object
971  */
972
973 Roo.bootstrap.Column = function(config){
974     Roo.bootstrap.Column.superclass.constructor.call(this, config);
975 };
976
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
978     
979     xs: false,
980     sm: false,
981     md: false,
982     lg: false,
983     xsoff: false,
984     smoff: false,
985     mdoff: false,
986     lgoff: false,
987     html: '',
988     offset: 0,
989     alert: false,
990     fa: false,
991     icon : false,
992     hidden : false,
993     fasize : 1,
994     
995     getAutoCreate : function(){
996         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
997         
998         cfg = {
999             tag: 'div',
1000             cls: 'column'
1001         };
1002         
1003         var settings=this;
1004         ['xs','sm','md','lg'].map(function(size){
1005             //Roo.log( size + ':' + settings[size]);
1006             
1007             if (settings[size+'off'] !== false) {
1008                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1009             }
1010             
1011             if (settings[size] === false) {
1012                 return;
1013             }
1014             
1015             if (!settings[size]) { // 0 = hidden
1016                 cfg.cls += ' hidden-' + size;
1017                 return;
1018             }
1019             cfg.cls += ' col-' + size + '-' + settings[size];
1020             
1021         });
1022         
1023         if (this.hidden) {
1024             cfg.cls += ' hidden';
1025         }
1026         
1027         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028             cfg.cls +=' alert alert-' + this.alert;
1029         }
1030         
1031         
1032         if (this.html.length) {
1033             cfg.html = this.html;
1034         }
1035         if (this.fa) {
1036             var fasize = '';
1037             if (this.fasize > 1) {
1038                 fasize = ' fa-' + this.fasize + 'x';
1039             }
1040             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041             
1042             
1043         }
1044         if (this.icon) {
1045             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1046         }
1047         
1048         return cfg;
1049     }
1050    
1051 });
1052
1053  
1054
1055  /*
1056  * - LGPL
1057  *
1058  * page container.
1059  * 
1060  */
1061
1062
1063 /**
1064  * @class Roo.bootstrap.Container
1065  * @extends Roo.bootstrap.Component
1066  * Bootstrap Container class
1067  * @cfg {Boolean} jumbotron is it a jumbotron element
1068  * @cfg {String} html content of element
1069  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1071  * @cfg {String} header content of header (for panel)
1072  * @cfg {String} footer content of footer (for panel)
1073  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074  * @cfg {String} tag (header|aside|section) type of HTML tag.
1075  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076  * @cfg {String} fa font awesome icon
1077  * @cfg {String} icon (info-sign|check|...) glyphicon name
1078  * @cfg {Boolean} hidden (true|false) hide the element
1079  * @cfg {Boolean} expandable (true|false) default false
1080  * @cfg {Boolean} expanded (true|false) default true
1081  * @cfg {String} rheader contet on the right of header
1082  * @cfg {Boolean} clickable (true|false) default false
1083
1084  *     
1085  * @constructor
1086  * Create a new Container
1087  * @param {Object} config The config object
1088  */
1089
1090 Roo.bootstrap.Container = function(config){
1091     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1092     
1093     this.addEvents({
1094         // raw events
1095          /**
1096          * @event expand
1097          * After the panel has been expand
1098          * 
1099          * @param {Roo.bootstrap.Container} this
1100          */
1101         "expand" : true,
1102         /**
1103          * @event collapse
1104          * After the panel has been collapsed
1105          * 
1106          * @param {Roo.bootstrap.Container} this
1107          */
1108         "collapse" : true,
1109         /**
1110          * @event click
1111          * When a element is chick
1112          * @param {Roo.bootstrap.Container} this
1113          * @param {Roo.EventObject} e
1114          */
1115         "click" : true
1116     });
1117 };
1118
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1120     
1121     jumbotron : false,
1122     well: '',
1123     panel : '',
1124     header: '',
1125     footer : '',
1126     sticky: '',
1127     tag : false,
1128     alert : false,
1129     fa: false,
1130     icon : false,
1131     expandable : false,
1132     rheader : '',
1133     expanded : true,
1134     clickable: false,
1135   
1136      
1137     getChildContainer : function() {
1138         
1139         if(!this.el){
1140             return false;
1141         }
1142         
1143         if (this.panel.length) {
1144             return this.el.select('.panel-body',true).first();
1145         }
1146         
1147         return this.el;
1148     },
1149     
1150     
1151     getAutoCreate : function(){
1152         
1153         var cfg = {
1154             tag : this.tag || 'div',
1155             html : '',
1156             cls : ''
1157         };
1158         if (this.jumbotron) {
1159             cfg.cls = 'jumbotron';
1160         }
1161         
1162         
1163         
1164         // - this is applied by the parent..
1165         //if (this.cls) {
1166         //    cfg.cls = this.cls + '';
1167         //}
1168         
1169         if (this.sticky.length) {
1170             
1171             var bd = Roo.get(document.body);
1172             if (!bd.hasClass('bootstrap-sticky')) {
1173                 bd.addClass('bootstrap-sticky');
1174                 Roo.select('html',true).setStyle('height', '100%');
1175             }
1176              
1177             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1178         }
1179         
1180         
1181         if (this.well.length) {
1182             switch (this.well) {
1183                 case 'lg':
1184                 case 'sm':
1185                     cfg.cls +=' well well-' +this.well;
1186                     break;
1187                 default:
1188                     cfg.cls +=' well';
1189                     break;
1190             }
1191         }
1192         
1193         if (this.hidden) {
1194             cfg.cls += ' hidden';
1195         }
1196         
1197         
1198         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199             cfg.cls +=' alert alert-' + this.alert;
1200         }
1201         
1202         var body = cfg;
1203         
1204         if (this.panel.length) {
1205             cfg.cls += ' panel panel-' + this.panel;
1206             cfg.cn = [];
1207             if (this.header.length) {
1208                 
1209                 var h = [];
1210                 
1211                 if(this.expandable){
1212                     
1213                     cfg.cls = cfg.cls + ' expandable';
1214                     
1215                     h.push({
1216                         tag: 'i',
1217                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1218                     });
1219                     
1220                 }
1221                 
1222                 h.push(
1223                     {
1224                         tag: 'span',
1225                         cls : 'panel-title',
1226                         html : (this.expandable ? '&nbsp;' : '') + this.header
1227                     },
1228                     {
1229                         tag: 'span',
1230                         cls: 'panel-header-right',
1231                         html: this.rheader
1232                     }
1233                 );
1234                 
1235                 cfg.cn.push({
1236                     cls : 'panel-heading',
1237                     style : this.expandable ? 'cursor: pointer' : '',
1238                     cn : h
1239                 });
1240                 
1241             }
1242             
1243             body = false;
1244             cfg.cn.push({
1245                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246                 html : this.html
1247             });
1248             
1249             
1250             if (this.footer.length) {
1251                 cfg.cn.push({
1252                     cls : 'panel-footer',
1253                     html : this.footer
1254                     
1255                 });
1256             }
1257             
1258         }
1259         
1260         if (body) {
1261             body.html = this.html || cfg.html;
1262             // prefix with the icons..
1263             if (this.fa) {
1264                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1265             }
1266             if (this.icon) {
1267                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268             }
1269             
1270             
1271         }
1272         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273             cfg.cls =  'container';
1274         }
1275         
1276         return cfg;
1277     },
1278     
1279     initEvents: function() 
1280     {
1281         if(this.expandable){
1282             var headerEl = this.headerEl();
1283         
1284             if(headerEl){
1285                 headerEl.on('click', this.onToggleClick, this);
1286             }
1287         }
1288         
1289         if(this.clickable){
1290             this.el.on('click', this.onClick, this);
1291         }
1292         
1293     },
1294     
1295     onToggleClick : function()
1296     {
1297         var headerEl = this.headerEl();
1298         
1299         if(!headerEl){
1300             return;
1301         }
1302         
1303         if(this.expanded){
1304             this.collapse();
1305             return;
1306         }
1307         
1308         this.expand();
1309     },
1310     
1311     expand : function()
1312     {
1313         if(this.fireEvent('expand', this)) {
1314             
1315             this.expanded = true;
1316             
1317             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1318             
1319             this.el.select('.panel-body',true).first().removeClass('hide');
1320             
1321             var toggleEl = this.toggleEl();
1322
1323             if(!toggleEl){
1324                 return;
1325             }
1326
1327             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328         }
1329         
1330     },
1331     
1332     collapse : function()
1333     {
1334         if(this.fireEvent('collapse', this)) {
1335             
1336             this.expanded = false;
1337             
1338             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339             this.el.select('.panel-body',true).first().addClass('hide');
1340         
1341             var toggleEl = this.toggleEl();
1342
1343             if(!toggleEl){
1344                 return;
1345             }
1346
1347             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1348         }
1349     },
1350     
1351     toggleEl : function()
1352     {
1353         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1354             return;
1355         }
1356         
1357         return this.el.select('.panel-heading .fa',true).first();
1358     },
1359     
1360     headerEl : function()
1361     {
1362         if(!this.el || !this.panel.length || !this.header.length){
1363             return;
1364         }
1365         
1366         return this.el.select('.panel-heading',true).first()
1367     },
1368     
1369     bodyEl : function()
1370     {
1371         if(!this.el || !this.panel.length){
1372             return;
1373         }
1374         
1375         return this.el.select('.panel-body',true).first()
1376     },
1377     
1378     titleEl : function()
1379     {
1380         if(!this.el || !this.panel.length || !this.header.length){
1381             return;
1382         }
1383         
1384         return this.el.select('.panel-title',true).first();
1385     },
1386     
1387     setTitle : function(v)
1388     {
1389         var titleEl = this.titleEl();
1390         
1391         if(!titleEl){
1392             return;
1393         }
1394         
1395         titleEl.dom.innerHTML = v;
1396     },
1397     
1398     getTitle : function()
1399     {
1400         
1401         var titleEl = this.titleEl();
1402         
1403         if(!titleEl){
1404             return '';
1405         }
1406         
1407         return titleEl.dom.innerHTML;
1408     },
1409     
1410     setRightTitle : function(v)
1411     {
1412         var t = this.el.select('.panel-header-right',true).first();
1413         
1414         if(!t){
1415             return;
1416         }
1417         
1418         t.dom.innerHTML = v;
1419     },
1420     
1421     onClick : function(e)
1422     {
1423         e.preventDefault();
1424         
1425         this.fireEvent('click', this, e);
1426     }
1427 });
1428
1429  /*
1430  * - LGPL
1431  *
1432  * image
1433  * 
1434  */
1435
1436
1437 /**
1438  * @class Roo.bootstrap.Img
1439  * @extends Roo.bootstrap.Component
1440  * Bootstrap Img class
1441  * @cfg {Boolean} imgResponsive false | true
1442  * @cfg {String} border rounded | circle | thumbnail
1443  * @cfg {String} src image source
1444  * @cfg {String} alt image alternative text
1445  * @cfg {String} href a tag href
1446  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447  * @cfg {String} xsUrl xs image source
1448  * @cfg {String} smUrl sm image source
1449  * @cfg {String} mdUrl md image source
1450  * @cfg {String} lgUrl lg image source
1451  * 
1452  * @constructor
1453  * Create a new Input
1454  * @param {Object} config The config object
1455  */
1456
1457 Roo.bootstrap.Img = function(config){
1458     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1459     
1460     this.addEvents({
1461         // img events
1462         /**
1463          * @event click
1464          * The img click event for the img.
1465          * @param {Roo.EventObject} e
1466          */
1467         "click" : true
1468     });
1469 };
1470
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1472     
1473     imgResponsive: true,
1474     border: '',
1475     src: 'about:blank',
1476     href: false,
1477     target: false,
1478     xsUrl: '',
1479     smUrl: '',
1480     mdUrl: '',
1481     lgUrl: '',
1482
1483     getAutoCreate : function()
1484     {   
1485         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486             return this.createSingleImg();
1487         }
1488         
1489         var cfg = {
1490             tag: 'div',
1491             cls: 'roo-image-responsive-group',
1492             cn: []
1493         };
1494         var _this = this;
1495         
1496         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1497             
1498             if(!_this[size + 'Url']){
1499                 return;
1500             }
1501             
1502             var img = {
1503                 tag: 'img',
1504                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505                 html: _this.html || cfg.html,
1506                 src: _this[size + 'Url']
1507             };
1508             
1509             img.cls += ' roo-image-responsive-' + size;
1510             
1511             var s = ['xs', 'sm', 'md', 'lg'];
1512             
1513             s.splice(s.indexOf(size), 1);
1514             
1515             Roo.each(s, function(ss){
1516                 img.cls += ' hidden-' + ss;
1517             });
1518             
1519             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520                 cfg.cls += ' img-' + _this.border;
1521             }
1522             
1523             if(_this.alt){
1524                 cfg.alt = _this.alt;
1525             }
1526             
1527             if(_this.href){
1528                 var a = {
1529                     tag: 'a',
1530                     href: _this.href,
1531                     cn: [
1532                         img
1533                     ]
1534                 };
1535
1536                 if(this.target){
1537                     a.target = _this.target;
1538                 }
1539             }
1540             
1541             cfg.cn.push((_this.href) ? a : img);
1542             
1543         });
1544         
1545         return cfg;
1546     },
1547     
1548     createSingleImg : function()
1549     {
1550         var cfg = {
1551             tag: 'img',
1552             cls: (this.imgResponsive) ? 'img-responsive' : '',
1553             html : null,
1554             src : 'about:blank'  // just incase src get's set to undefined?!?
1555         };
1556         
1557         cfg.html = this.html || cfg.html;
1558         
1559         cfg.src = this.src || cfg.src;
1560         
1561         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562             cfg.cls += ' img-' + this.border;
1563         }
1564         
1565         if(this.alt){
1566             cfg.alt = this.alt;
1567         }
1568         
1569         if(this.href){
1570             var a = {
1571                 tag: 'a',
1572                 href: this.href,
1573                 cn: [
1574                     cfg
1575                 ]
1576             };
1577             
1578             if(this.target){
1579                 a.target = this.target;
1580             }
1581             
1582         }
1583         
1584         return (this.href) ? a : cfg;
1585     },
1586     
1587     initEvents: function() 
1588     {
1589         if(!this.href){
1590             this.el.on('click', this.onClick, this);
1591         }
1592         
1593     },
1594     
1595     onClick : function(e)
1596     {
1597         Roo.log('img onclick');
1598         this.fireEvent('click', this, e);
1599     },
1600     /**
1601      * Sets the url of the image - used to update it
1602      * @param {String} url the url of the image
1603      */
1604     
1605     setSrc : function(url)
1606     {
1607         this.src =  url;
1608         
1609         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610             this.el.dom.src =  url;
1611             return;
1612         }
1613         
1614         this.el.select('img', true).first().dom.src =  url;
1615     }
1616     
1617     
1618    
1619 });
1620
1621  /*
1622  * - LGPL
1623  *
1624  * image
1625  * 
1626  */
1627
1628
1629 /**
1630  * @class Roo.bootstrap.Link
1631  * @extends Roo.bootstrap.Component
1632  * Bootstrap Link Class
1633  * @cfg {String} alt image alternative text
1634  * @cfg {String} href a tag href
1635  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636  * @cfg {String} html the content of the link.
1637  * @cfg {String} anchor name for the anchor link
1638  * @cfg {String} fa - favicon
1639
1640  * @cfg {Boolean} preventDefault (true | false) default false
1641
1642  * 
1643  * @constructor
1644  * Create a new Input
1645  * @param {Object} config The config object
1646  */
1647
1648 Roo.bootstrap.Link = function(config){
1649     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1650     
1651     this.addEvents({
1652         // img events
1653         /**
1654          * @event click
1655          * The img click event for the img.
1656          * @param {Roo.EventObject} e
1657          */
1658         "click" : true
1659     });
1660 };
1661
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1663     
1664     href: false,
1665     target: false,
1666     preventDefault: false,
1667     anchor : false,
1668     alt : false,
1669     fa: false,
1670
1671
1672     getAutoCreate : function()
1673     {
1674         var html = this.html || '';
1675         
1676         if (this.fa !== false) {
1677             html = '<i class="fa fa-' + this.fa + '"></i>';
1678         }
1679         var cfg = {
1680             tag: 'a'
1681         };
1682         // anchor's do not require html/href...
1683         if (this.anchor === false) {
1684             cfg.html = html;
1685             cfg.href = this.href || '#';
1686         } else {
1687             cfg.name = this.anchor;
1688             if (this.html !== false || this.fa !== false) {
1689                 cfg.html = html;
1690             }
1691             if (this.href !== false) {
1692                 cfg.href = this.href;
1693             }
1694         }
1695         
1696         if(this.alt !== false){
1697             cfg.alt = this.alt;
1698         }
1699         
1700         
1701         if(this.target !== false) {
1702             cfg.target = this.target;
1703         }
1704         
1705         return cfg;
1706     },
1707     
1708     initEvents: function() {
1709         
1710         if(!this.href || this.preventDefault){
1711             this.el.on('click', this.onClick, this);
1712         }
1713     },
1714     
1715     onClick : function(e)
1716     {
1717         if(this.preventDefault){
1718             e.preventDefault();
1719         }
1720         //Roo.log('img onclick');
1721         this.fireEvent('click', this, e);
1722     }
1723    
1724 });
1725
1726  /*
1727  * - LGPL
1728  *
1729  * header
1730  * 
1731  */
1732
1733 /**
1734  * @class Roo.bootstrap.Header
1735  * @extends Roo.bootstrap.Component
1736  * Bootstrap Header class
1737  * @cfg {String} html content of header
1738  * @cfg {Number} level (1|2|3|4|5|6) default 1
1739  * 
1740  * @constructor
1741  * Create a new Header
1742  * @param {Object} config The config object
1743  */
1744
1745
1746 Roo.bootstrap.Header  = function(config){
1747     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1748 };
1749
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1751     
1752     //href : false,
1753     html : false,
1754     level : 1,
1755     
1756     
1757     
1758     getAutoCreate : function(){
1759         
1760         
1761         
1762         var cfg = {
1763             tag: 'h' + (1 *this.level),
1764             html: this.html || ''
1765         } ;
1766         
1767         return cfg;
1768     }
1769    
1770 });
1771
1772  
1773
1774  /*
1775  * Based on:
1776  * Ext JS Library 1.1.1
1777  * Copyright(c) 2006-2007, Ext JS, LLC.
1778  *
1779  * Originally Released Under LGPL - original licence link has changed is not relivant.
1780  *
1781  * Fork - LGPL
1782  * <script type="text/javascript">
1783  */
1784  
1785 /**
1786  * @class Roo.bootstrap.MenuMgr
1787  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1788  * @singleton
1789  */
1790 Roo.bootstrap.MenuMgr = function(){
1791    var menus, active, groups = {}, attached = false, lastShow = new Date();
1792
1793    // private - called when first menu is created
1794    function init(){
1795        menus = {};
1796        active = new Roo.util.MixedCollection();
1797        Roo.get(document).addKeyListener(27, function(){
1798            if(active.length > 0){
1799                hideAll();
1800            }
1801        });
1802    }
1803
1804    // private
1805    function hideAll(){
1806        if(active && active.length > 0){
1807            var c = active.clone();
1808            c.each(function(m){
1809                m.hide();
1810            });
1811        }
1812    }
1813
1814    // private
1815    function onHide(m){
1816        active.remove(m);
1817        if(active.length < 1){
1818            Roo.get(document).un("mouseup", onMouseDown);
1819             
1820            attached = false;
1821        }
1822    }
1823
1824    // private
1825    function onShow(m){
1826        var last = active.last();
1827        lastShow = new Date();
1828        active.add(m);
1829        if(!attached){
1830           Roo.get(document).on("mouseup", onMouseDown);
1831            
1832            attached = true;
1833        }
1834        if(m.parentMenu){
1835           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836           m.parentMenu.activeChild = m;
1837        }else if(last && last.isVisible()){
1838           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839        }
1840    }
1841
1842    // private
1843    function onBeforeHide(m){
1844        if(m.activeChild){
1845            m.activeChild.hide();
1846        }
1847        if(m.autoHideTimer){
1848            clearTimeout(m.autoHideTimer);
1849            delete m.autoHideTimer;
1850        }
1851    }
1852
1853    // private
1854    function onBeforeShow(m){
1855        var pm = m.parentMenu;
1856        if(!pm && !m.allowOtherMenus){
1857            hideAll();
1858        }else if(pm && pm.activeChild && active != m){
1859            pm.activeChild.hide();
1860        }
1861    }
1862
1863    // private this should really trigger on mouseup..
1864    function onMouseDown(e){
1865         Roo.log("on Mouse Up");
1866         
1867         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868             Roo.log("MenuManager hideAll");
1869             hideAll();
1870             e.stopEvent();
1871         }
1872         
1873         
1874    }
1875
1876    // private
1877    function onBeforeCheck(mi, state){
1878        if(state){
1879            var g = groups[mi.group];
1880            for(var i = 0, l = g.length; i < l; i++){
1881                if(g[i] != mi){
1882                    g[i].setChecked(false);
1883                }
1884            }
1885        }
1886    }
1887
1888    return {
1889
1890        /**
1891         * Hides all menus that are currently visible
1892         */
1893        hideAll : function(){
1894             hideAll();  
1895        },
1896
1897        // private
1898        register : function(menu){
1899            if(!menus){
1900                init();
1901            }
1902            menus[menu.id] = menu;
1903            menu.on("beforehide", onBeforeHide);
1904            menu.on("hide", onHide);
1905            menu.on("beforeshow", onBeforeShow);
1906            menu.on("show", onShow);
1907            var g = menu.group;
1908            if(g && menu.events["checkchange"]){
1909                if(!groups[g]){
1910                    groups[g] = [];
1911                }
1912                groups[g].push(menu);
1913                menu.on("checkchange", onCheck);
1914            }
1915        },
1916
1917         /**
1918          * Returns a {@link Roo.menu.Menu} object
1919          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920          * be used to generate and return a new Menu instance.
1921          */
1922        get : function(menu){
1923            if(typeof menu == "string"){ // menu id
1924                return menus[menu];
1925            }else if(menu.events){  // menu instance
1926                return menu;
1927            }
1928            /*else if(typeof menu.length == 'number'){ // array of menu items?
1929                return new Roo.bootstrap.Menu({items:menu});
1930            }else{ // otherwise, must be a config
1931                return new Roo.bootstrap.Menu(menu);
1932            }
1933            */
1934            return false;
1935        },
1936
1937        // private
1938        unregister : function(menu){
1939            delete menus[menu.id];
1940            menu.un("beforehide", onBeforeHide);
1941            menu.un("hide", onHide);
1942            menu.un("beforeshow", onBeforeShow);
1943            menu.un("show", onShow);
1944            var g = menu.group;
1945            if(g && menu.events["checkchange"]){
1946                groups[g].remove(menu);
1947                menu.un("checkchange", onCheck);
1948            }
1949        },
1950
1951        // private
1952        registerCheckable : function(menuItem){
1953            var g = menuItem.group;
1954            if(g){
1955                if(!groups[g]){
1956                    groups[g] = [];
1957                }
1958                groups[g].push(menuItem);
1959                menuItem.on("beforecheckchange", onBeforeCheck);
1960            }
1961        },
1962
1963        // private
1964        unregisterCheckable : function(menuItem){
1965            var g = menuItem.group;
1966            if(g){
1967                groups[g].remove(menuItem);
1968                menuItem.un("beforecheckchange", onBeforeCheck);
1969            }
1970        }
1971    };
1972 }();/*
1973  * - LGPL
1974  *
1975  * menu
1976  * 
1977  */
1978
1979 /**
1980  * @class Roo.bootstrap.Menu
1981  * @extends Roo.bootstrap.Component
1982  * Bootstrap Menu class - container for MenuItems
1983  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1985  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1986  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1987  * 
1988  * @constructor
1989  * Create a new Menu
1990  * @param {Object} config The config object
1991  */
1992
1993
1994 Roo.bootstrap.Menu = function(config){
1995     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996     if (this.registerMenu && this.type != 'treeview')  {
1997         Roo.bootstrap.MenuMgr.register(this);
1998     }
1999     this.addEvents({
2000         /**
2001          * @event beforeshow
2002          * Fires before this menu is displayed
2003          * @param {Roo.menu.Menu} this
2004          */
2005         beforeshow : true,
2006         /**
2007          * @event beforehide
2008          * Fires before this menu is hidden
2009          * @param {Roo.menu.Menu} this
2010          */
2011         beforehide : true,
2012         /**
2013          * @event show
2014          * Fires after this menu is displayed
2015          * @param {Roo.menu.Menu} this
2016          */
2017         show : true,
2018         /**
2019          * @event hide
2020          * Fires after this menu is hidden
2021          * @param {Roo.menu.Menu} this
2022          */
2023         hide : true,
2024         /**
2025          * @event click
2026          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029          * @param {Roo.EventObject} e
2030          */
2031         click : true,
2032         /**
2033          * @event mouseover
2034          * Fires when the mouse is hovering over this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseover : true,
2040         /**
2041          * @event mouseout
2042          * Fires when the mouse exits this menu
2043          * @param {Roo.menu.Menu} this
2044          * @param {Roo.EventObject} e
2045          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046          */
2047         mouseout : true,
2048         /**
2049          * @event itemclick
2050          * Fires when a menu item contained in this menu is clicked
2051          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052          * @param {Roo.EventObject} e
2053          */
2054         itemclick: true
2055     });
2056     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2057 };
2058
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2060     
2061    /// html : false,
2062     //align : '',
2063     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2064     type: false,
2065     /**
2066      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2067      */
2068     registerMenu : true,
2069     
2070     menuItems :false, // stores the menu items..
2071     
2072     hidden:true,
2073         
2074     parentMenu : false,
2075     
2076     stopEvent : true,
2077     
2078     isLink : false,
2079     
2080     getChildContainer : function() {
2081         return this.el;  
2082     },
2083     
2084     getAutoCreate : function(){
2085          
2086         //if (['right'].indexOf(this.align)!==-1) {
2087         //    cfg.cn[1].cls += ' pull-right'
2088         //}
2089         
2090         
2091         var cfg = {
2092             tag : 'ul',
2093             cls : 'dropdown-menu' ,
2094             style : 'z-index:1000'
2095             
2096         };
2097         
2098         if (this.type === 'submenu') {
2099             cfg.cls = 'submenu active';
2100         }
2101         if (this.type === 'treeview') {
2102             cfg.cls = 'treeview-menu';
2103         }
2104         
2105         return cfg;
2106     },
2107     initEvents : function() {
2108         
2109        // Roo.log("ADD event");
2110        // Roo.log(this.triggerEl.dom);
2111         
2112         this.triggerEl.on('click', this.onTriggerClick, this);
2113         
2114         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2115         
2116         this.triggerEl.addClass('dropdown-toggle');
2117         
2118         if (Roo.isTouch) {
2119             this.el.on('touchstart'  , this.onTouch, this);
2120         }
2121         this.el.on('click' , this.onClick, this);
2122
2123         this.el.on("mouseover", this.onMouseOver, this);
2124         this.el.on("mouseout", this.onMouseOut, this);
2125         
2126     },
2127     
2128     findTargetItem : function(e)
2129     {
2130         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2131         if(!t){
2132             return false;
2133         }
2134         //Roo.log(t);         Roo.log(t.id);
2135         if(t && t.id){
2136             //Roo.log(this.menuitems);
2137             return this.menuitems.get(t.id);
2138             
2139             //return this.items.get(t.menuItemId);
2140         }
2141         
2142         return false;
2143     },
2144     
2145     onTouch : function(e) 
2146     {
2147         Roo.log("menu.onTouch");
2148         //e.stopEvent(); this make the user popdown broken
2149         this.onClick(e);
2150     },
2151     
2152     onClick : function(e)
2153     {
2154         Roo.log("menu.onClick");
2155         
2156         var t = this.findTargetItem(e);
2157         if(!t || t.isContainer){
2158             return;
2159         }
2160         Roo.log(e);
2161         /*
2162         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2163             if(t == this.activeItem && t.shouldDeactivate(e)){
2164                 this.activeItem.deactivate();
2165                 delete this.activeItem;
2166                 return;
2167             }
2168             if(t.canActivate){
2169                 this.setActiveItem(t, true);
2170             }
2171             return;
2172             
2173             
2174         }
2175         */
2176        
2177         Roo.log('pass click event');
2178         
2179         t.onClick(e);
2180         
2181         this.fireEvent("click", this, t, e);
2182         
2183         var _this = this;
2184         
2185         if(!t.href.length || t.href == '#'){
2186             (function() { _this.hide(); }).defer(100);
2187         }
2188         
2189     },
2190     
2191     onMouseOver : function(e){
2192         var t  = this.findTargetItem(e);
2193         //Roo.log(t);
2194         //if(t){
2195         //    if(t.canActivate && !t.disabled){
2196         //        this.setActiveItem(t, true);
2197         //    }
2198         //}
2199         
2200         this.fireEvent("mouseover", this, e, t);
2201     },
2202     isVisible : function(){
2203         return !this.hidden;
2204     },
2205      onMouseOut : function(e){
2206         var t  = this.findTargetItem(e);
2207         
2208         //if(t ){
2209         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2210         //        this.activeItem.deactivate();
2211         //        delete this.activeItem;
2212         //    }
2213         //}
2214         this.fireEvent("mouseout", this, e, t);
2215     },
2216     
2217     
2218     /**
2219      * Displays this menu relative to another element
2220      * @param {String/HTMLElement/Roo.Element} element The element to align to
2221      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222      * the element (defaults to this.defaultAlign)
2223      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2224      */
2225     show : function(el, pos, parentMenu){
2226         this.parentMenu = parentMenu;
2227         if(!this.el){
2228             this.render();
2229         }
2230         this.fireEvent("beforeshow", this);
2231         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2232     },
2233      /**
2234      * Displays this menu at a specific xy position
2235      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2237      */
2238     showAt : function(xy, parentMenu, /* private: */_e){
2239         this.parentMenu = parentMenu;
2240         if(!this.el){
2241             this.render();
2242         }
2243         if(_e !== false){
2244             this.fireEvent("beforeshow", this);
2245             //xy = this.el.adjustForConstraints(xy);
2246         }
2247         
2248         //this.el.show();
2249         this.hideMenuItems();
2250         this.hidden = false;
2251         this.triggerEl.addClass('open');
2252         
2253         // reassign x when hitting right
2254         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2256         }
2257         
2258         // reassign y when hitting bottom
2259         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2261         }
2262         
2263         // but the list may align on trigger left or trigger top... should it be a properity?
2264         
2265         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2266             this.el.setXY(xy);
2267         }
2268         
2269         this.focus();
2270         this.fireEvent("show", this);
2271     },
2272     
2273     focus : function(){
2274         return;
2275         if(!this.hidden){
2276             this.doFocus.defer(50, this);
2277         }
2278     },
2279
2280     doFocus : function(){
2281         if(!this.hidden){
2282             this.focusEl.focus();
2283         }
2284     },
2285
2286     /**
2287      * Hides this menu and optionally all parent menus
2288      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2289      */
2290     hide : function(deep)
2291     {
2292         
2293         this.hideMenuItems();
2294         if(this.el && this.isVisible()){
2295             this.fireEvent("beforehide", this);
2296             if(this.activeItem){
2297                 this.activeItem.deactivate();
2298                 this.activeItem = null;
2299             }
2300             this.triggerEl.removeClass('open');;
2301             this.hidden = true;
2302             this.fireEvent("hide", this);
2303         }
2304         if(deep === true && this.parentMenu){
2305             this.parentMenu.hide(true);
2306         }
2307     },
2308     
2309     onTriggerClick : function(e)
2310     {
2311         Roo.log('trigger click');
2312         
2313         var target = e.getTarget();
2314         
2315         Roo.log(target.nodeName.toLowerCase());
2316         
2317         if(target.nodeName.toLowerCase() === 'i'){
2318             e.preventDefault();
2319         }
2320         
2321     },
2322     
2323     onTriggerPress  : function(e)
2324     {
2325         Roo.log('trigger press');
2326         //Roo.log(e.getTarget());
2327        // Roo.log(this.triggerEl.dom);
2328        
2329         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330         var pel = Roo.get(e.getTarget());
2331         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332             Roo.log('is treeview or dropdown?');
2333             return;
2334         }
2335         
2336         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2337             return;
2338         }
2339         
2340         if (this.isVisible()) {
2341             Roo.log('hide');
2342             this.hide();
2343         } else {
2344             Roo.log('show');
2345             this.show(this.triggerEl, false, false);
2346         }
2347         
2348         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2349             e.stopEvent();
2350         }
2351         
2352     },
2353        
2354     
2355     hideMenuItems : function()
2356     {
2357         Roo.log("hide Menu Items");
2358         if (!this.el) { 
2359             return;
2360         }
2361         //$(backdrop).remove()
2362         this.el.select('.open',true).each(function(aa) {
2363             
2364             aa.removeClass('open');
2365           //var parent = getParent($(this))
2366           //var relatedTarget = { relatedTarget: this }
2367           
2368            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369           //if (e.isDefaultPrevented()) return
2370            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2371         });
2372     },
2373     addxtypeChild : function (tree, cntr) {
2374         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2375           
2376         this.menuitems.add(comp);
2377         return comp;
2378
2379     },
2380     getEl : function()
2381     {
2382         Roo.log(this.el);
2383         return this.el;
2384     },
2385     
2386     clear : function()
2387     {
2388         this.getEl().dom.innerHTML = '';
2389         this.menuitems.clear();
2390     }
2391 });
2392
2393  
2394  /*
2395  * - LGPL
2396  *
2397  * menu item
2398  * 
2399  */
2400
2401
2402 /**
2403  * @class Roo.bootstrap.MenuItem
2404  * @extends Roo.bootstrap.Component
2405  * Bootstrap MenuItem class
2406  * @cfg {String} html the menu label
2407  * @cfg {String} href the link
2408  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2411  * @cfg {String} fa favicon to show on left of menu item.
2412  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2413  * 
2414  * 
2415  * @constructor
2416  * Create a new MenuItem
2417  * @param {Object} config The config object
2418  */
2419
2420
2421 Roo.bootstrap.MenuItem = function(config){
2422     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423     this.addEvents({
2424         // raw events
2425         /**
2426          * @event click
2427          * The raw click event for the entire grid.
2428          * @param {Roo.bootstrap.MenuItem} this
2429          * @param {Roo.EventObject} e
2430          */
2431         "click" : true
2432     });
2433 };
2434
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2436     
2437     href : false,
2438     html : false,
2439     preventDefault: false,
2440     isContainer : false,
2441     active : false,
2442     fa: false,
2443     
2444     getAutoCreate : function(){
2445         
2446         if(this.isContainer){
2447             return {
2448                 tag: 'li',
2449                 cls: 'dropdown-menu-item'
2450             };
2451         }
2452         var ctag = {
2453             tag: 'span',
2454             html: 'Link'
2455         };
2456         
2457         var anc = {
2458             tag : 'a',
2459             href : '#',
2460             cn : [  ]
2461         };
2462         
2463         if (this.fa !== false) {
2464             anc.cn.push({
2465                 tag : 'i',
2466                 cls : 'fa fa-' + this.fa
2467             });
2468         }
2469         
2470         anc.cn.push(ctag);
2471         
2472         
2473         var cfg= {
2474             tag: 'li',
2475             cls: 'dropdown-menu-item',
2476             cn: [ anc ]
2477         };
2478         if (this.parent().type == 'treeview') {
2479             cfg.cls = 'treeview-menu';
2480         }
2481         if (this.active) {
2482             cfg.cls += ' active';
2483         }
2484         
2485         
2486         
2487         anc.href = this.href || cfg.cn[0].href ;
2488         ctag.html = this.html || cfg.cn[0].html ;
2489         return cfg;
2490     },
2491     
2492     initEvents: function()
2493     {
2494         if (this.parent().type == 'treeview') {
2495             this.el.select('a').on('click', this.onClick, this);
2496         }
2497         
2498         if (this.menu) {
2499             this.menu.parentType = this.xtype;
2500             this.menu.triggerEl = this.el;
2501             this.menu = this.addxtype(Roo.apply({}, this.menu));
2502         }
2503         
2504     },
2505     onClick : function(e)
2506     {
2507         Roo.log('item on click ');
2508         
2509         if(this.preventDefault){
2510             e.preventDefault();
2511         }
2512         //this.parent().hideMenuItems();
2513         
2514         this.fireEvent('click', this, e);
2515     },
2516     getEl : function()
2517     {
2518         return this.el;
2519     } 
2520 });
2521
2522  
2523
2524  /*
2525  * - LGPL
2526  *
2527  * menu separator
2528  * 
2529  */
2530
2531
2532 /**
2533  * @class Roo.bootstrap.MenuSeparator
2534  * @extends Roo.bootstrap.Component
2535  * Bootstrap MenuSeparator class
2536  * 
2537  * @constructor
2538  * Create a new MenuItem
2539  * @param {Object} config The config object
2540  */
2541
2542
2543 Roo.bootstrap.MenuSeparator = function(config){
2544     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2545 };
2546
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2548     
2549     getAutoCreate : function(){
2550         var cfg = {
2551             cls: 'divider',
2552             tag : 'li'
2553         };
2554         
2555         return cfg;
2556     }
2557    
2558 });
2559
2560  
2561
2562  
2563 /*
2564 * Licence: LGPL
2565 */
2566
2567 /**
2568  * @class Roo.bootstrap.Modal
2569  * @extends Roo.bootstrap.Component
2570  * Bootstrap Modal class
2571  * @cfg {String} title Title of dialog
2572  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2574  * @cfg {Boolean} specificTitle default false
2575  * @cfg {Array} buttons Array of buttons or standard button set..
2576  * @cfg {String} buttonPosition (left|right|center) default right
2577  * @cfg {Boolean} animate default true
2578  * @cfg {Boolean} allow_close default true
2579  * @cfg {Boolean} fitwindow default false
2580  * @cfg {String} size (sm|lg) default empty
2581  * @cfg {Number} max_width set the max width of modal
2582  *
2583  *
2584  * @constructor
2585  * Create a new Modal Dialog
2586  * @param {Object} config The config object
2587  */
2588
2589 Roo.bootstrap.Modal = function(config){
2590     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2591     this.addEvents({
2592         // raw events
2593         /**
2594          * @event btnclick
2595          * The raw btnclick event for the button
2596          * @param {Roo.EventObject} e
2597          */
2598         "btnclick" : true,
2599         /**
2600          * @event resize
2601          * Fire when dialog resize
2602          * @param {Roo.bootstrap.Modal} this
2603          * @param {Roo.EventObject} e
2604          */
2605         "resize" : true
2606     });
2607     this.buttons = this.buttons || [];
2608
2609     if (this.tmpl) {
2610         this.tmpl = Roo.factory(this.tmpl);
2611     }
2612
2613 };
2614
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2616
2617     title : 'test dialog',
2618
2619     buttons : false,
2620
2621     // set on load...
2622
2623     html: false,
2624
2625     tmp: false,
2626
2627     specificTitle: false,
2628
2629     buttonPosition: 'right',
2630
2631     allow_close : true,
2632
2633     animate : true,
2634
2635     fitwindow: false,
2636
2637
2638      // private
2639     dialogEl: false,
2640     bodyEl:  false,
2641     footerEl:  false,
2642     titleEl:  false,
2643     closeEl:  false,
2644
2645     size: '',
2646     
2647     max_width: 0,
2648     
2649     fit_content: false,
2650
2651     onRender : function(ct, position)
2652     {
2653         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2654
2655         if(!this.el){
2656             var cfg = Roo.apply({},  this.getAutoCreate());
2657             cfg.id = Roo.id();
2658             //if(!cfg.name){
2659             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2660             //}
2661             //if (!cfg.name.length) {
2662             //    delete cfg.name;
2663            // }
2664             if (this.cls) {
2665                 cfg.cls += ' ' + this.cls;
2666             }
2667             if (this.style) {
2668                 cfg.style = this.style;
2669             }
2670             this.el = Roo.get(document.body).createChild(cfg, position);
2671         }
2672         //var type = this.el.dom.type;
2673
2674
2675         if(this.tabIndex !== undefined){
2676             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2677         }
2678
2679         this.dialogEl = this.el.select('.modal-dialog',true).first();
2680         this.bodyEl = this.el.select('.modal-body',true).first();
2681         this.closeEl = this.el.select('.modal-header .close', true).first();
2682         this.headerEl = this.el.select('.modal-header',true).first();
2683         this.titleEl = this.el.select('.modal-title',true).first();
2684         this.footerEl = this.el.select('.modal-footer',true).first();
2685
2686         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2687         
2688         //this.el.addClass("x-dlg-modal");
2689
2690         if (this.buttons.length) {
2691             Roo.each(this.buttons, function(bb) {
2692                 var b = Roo.apply({}, bb);
2693                 b.xns = b.xns || Roo.bootstrap;
2694                 b.xtype = b.xtype || 'Button';
2695                 if (typeof(b.listeners) == 'undefined') {
2696                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2697                 }
2698
2699                 var btn = Roo.factory(b);
2700
2701                 btn.render(this.el.select('.modal-footer div').first());
2702
2703             },this);
2704         }
2705         // render the children.
2706         var nitems = [];
2707
2708         if(typeof(this.items) != 'undefined'){
2709             var items = this.items;
2710             delete this.items;
2711
2712             for(var i =0;i < items.length;i++) {
2713                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2714             }
2715         }
2716
2717         this.items = nitems;
2718
2719         // where are these used - they used to be body/close/footer
2720
2721
2722         this.initEvents();
2723         //this.el.addClass([this.fieldClass, this.cls]);
2724
2725     },
2726
2727     getAutoCreate : function()
2728     {
2729         var bdy = {
2730                 cls : 'modal-body',
2731                 html : this.html || ''
2732         };
2733
2734         var title = {
2735             tag: 'h4',
2736             cls : 'modal-title',
2737             html : this.title
2738         };
2739
2740         if(this.specificTitle){
2741             title = this.title;
2742
2743         };
2744
2745         var header = [];
2746         if (this.allow_close) {
2747             header.push({
2748                 tag: 'button',
2749                 cls : 'close',
2750                 html : '&times'
2751             });
2752         }
2753
2754         header.push(title);
2755
2756         var size = '';
2757
2758         if(this.size.length){
2759             size = 'modal-' + this.size;
2760         }
2761
2762         var modal = {
2763             cls: "modal",
2764              cn : [
2765                 {
2766                     cls: "modal-dialog " + size,
2767                     cn : [
2768                         {
2769                             cls : "modal-content",
2770                             cn : [
2771                                 {
2772                                     cls : 'modal-header',
2773                                     cn : header
2774                                 },
2775                                 bdy,
2776                                 {
2777                                     cls : 'modal-footer',
2778                                     cn : [
2779                                         {
2780                                             tag: 'div',
2781                                             cls: 'btn-' + this.buttonPosition
2782                                         }
2783                                     ]
2784
2785                                 }
2786
2787
2788                             ]
2789
2790                         }
2791                     ]
2792
2793                 }
2794             ]
2795         };
2796
2797         if(this.animate){
2798             modal.cls += ' fade';
2799         }
2800
2801         return modal;
2802
2803     },
2804     getChildContainer : function() {
2805
2806          return this.bodyEl;
2807
2808     },
2809     getButtonContainer : function() {
2810          return this.el.select('.modal-footer div',true).first();
2811
2812     },
2813     initEvents : function()
2814     {
2815         if (this.allow_close) {
2816             this.closeEl.on('click', this.hide, this);
2817         }
2818         Roo.EventManager.onWindowResize(this.resize, this, true);
2819
2820
2821     },
2822
2823     resize : function()
2824     {
2825         this.maskEl.setSize(
2826             Roo.lib.Dom.getViewWidth(true),
2827             Roo.lib.Dom.getViewHeight(true)
2828         );
2829         
2830         if (this.fitwindow) {
2831             this.setSize(
2832                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2833                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2834             );
2835             return;
2836         }
2837         
2838         if(this.max_width !== 0) {
2839             
2840             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2841             
2842             if(this.height) {
2843                 this.setSize(
2844                     w,
2845                     this.height <= Roo.lib.Dom.getViewportHeight(true) - 60 ? 
2846                         this.height : Roo.lib.Dom.getViewportHeight(true) - 60
2847                 );
2848                 return;
2849             }
2850             
2851             if(!this.fit_content) {
2852                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2853                 return;
2854             }
2855             
2856             this.setSize(w, Math.min(
2857                 60 +
2858                 this.headerEl.getHeight() + 
2859                 this.footerEl.getHeight() + 
2860                 this.getChildHeight(this.bodyEl.dom.childNodes),
2861                 Roo.lib.Dom.getViewportHeight(true) - 60)
2862             );
2863         }
2864         
2865     },
2866
2867     setSize : function(w,h)
2868     {
2869         if (!w && !h) {
2870             return;
2871         }
2872         
2873         this.resizeTo(w,h);
2874     },
2875
2876     show : function() {
2877
2878         if (!this.rendered) {
2879             this.render();
2880         }
2881
2882         //this.el.setStyle('display', 'block');
2883         this.el.removeClass('hideing');        
2884         this.el.addClass('show');
2885  
2886         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2887             var _this = this;
2888             (function(){
2889                 this.el.addClass('in');
2890             }).defer(50, this);
2891         }else{
2892             this.el.addClass('in');
2893         }
2894
2895         // not sure how we can show data in here..
2896         //if (this.tmpl) {
2897         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2898         //}
2899
2900         Roo.get(document.body).addClass("x-body-masked");
2901         
2902         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2903         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2904         this.maskEl.addClass('show');
2905         
2906         this.resize();
2907         
2908         this.fireEvent('show', this);
2909
2910         // set zindex here - otherwise it appears to be ignored...
2911         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2912
2913         (function () {
2914             this.items.forEach( function(e) {
2915                 e.layout ? e.layout() : false;
2916
2917             });
2918         }).defer(100,this);
2919
2920     },
2921     hide : function()
2922     {
2923         if(this.fireEvent("beforehide", this) !== false){
2924             this.maskEl.removeClass('show');
2925             Roo.get(document.body).removeClass("x-body-masked");
2926             this.el.removeClass('in');
2927             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2928
2929             if(this.animate){ // why
2930                 this.el.addClass('hideing');
2931                 (function(){
2932                     if (!this.el.hasClass('hideing')) {
2933                         return; // it's been shown again...
2934                     }
2935                     this.el.removeClass('show');
2936                     this.el.removeClass('hideing');
2937                 }).defer(150,this);
2938                 
2939             }else{
2940                  this.el.removeClass('show');
2941             }
2942             this.fireEvent('hide', this);
2943         }
2944     },
2945     isVisible : function()
2946     {
2947         
2948         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2949         
2950     },
2951
2952     addButton : function(str, cb)
2953     {
2954
2955
2956         var b = Roo.apply({}, { html : str } );
2957         b.xns = b.xns || Roo.bootstrap;
2958         b.xtype = b.xtype || 'Button';
2959         if (typeof(b.listeners) == 'undefined') {
2960             b.listeners = { click : cb.createDelegate(this)  };
2961         }
2962
2963         var btn = Roo.factory(b);
2964
2965         btn.render(this.el.select('.modal-footer div').first());
2966
2967         return btn;
2968
2969     },
2970
2971     setDefaultButton : function(btn)
2972     {
2973         //this.el.select('.modal-footer').()
2974     },
2975     diff : false,
2976
2977     resizeTo: function(w,h)
2978     {
2979         // skip.. ?? why??
2980
2981         this.dialogEl.setWidth(w);
2982         if (this.diff === false) {
2983             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2984         }
2985
2986         this.bodyEl.setHeight(h - this.diff);
2987
2988         this.fireEvent('resize', this);
2989
2990     },
2991     setContentSize  : function(w, h)
2992     {
2993
2994     },
2995     onButtonClick: function(btn,e)
2996     {
2997         //Roo.log([a,b,c]);
2998         this.fireEvent('btnclick', btn.name, e);
2999     },
3000      /**
3001      * Set the title of the Dialog
3002      * @param {String} str new Title
3003      */
3004     setTitle: function(str) {
3005         this.titleEl.dom.innerHTML = str;
3006     },
3007     /**
3008      * Set the body of the Dialog
3009      * @param {String} str new Title
3010      */
3011     setBody: function(str) {
3012         this.bodyEl.dom.innerHTML = str;
3013     },
3014     /**
3015      * Set the body of the Dialog using the template
3016      * @param {Obj} data - apply this data to the template and replace the body contents.
3017      */
3018     applyBody: function(obj)
3019     {
3020         if (!this.tmpl) {
3021             Roo.log("Error - using apply Body without a template");
3022             //code
3023         }
3024         this.tmpl.overwrite(this.bodyEl, obj);
3025     },
3026     
3027     getChildHeight : function(child_nodes)
3028     {
3029         if(
3030             !child_nodes ||
3031             child_nodes.length == 0
3032         ) {
3033             return;
3034         }
3035         
3036         var child_height = 0;
3037         
3038         for(var i = 0; i < child_nodes.length; i++) {
3039             
3040             // for modal with tabs...
3041             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3042                 
3043                 var layout_childs = child_nodes[i].childNodes;
3044                 
3045                 for(var j = 0; j < layout_childs.length; j++) {
3046                     
3047                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3048                         
3049                         var layout_body_childs = layout_childs[j].childNodes;
3050                         
3051                         for(var k = 0; k < layout_body_childs.length; k++) {
3052                             
3053                             if(layout_body_childs[k].classList.contains('navbar')) {
3054                                 child_height += layout_body_childs[k].offsetHeight;
3055                                 // Roo.log('nav height: '+ layout_body_childs[k].offsetHeight);
3056                                 continue;
3057                             }
3058                             
3059                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3060                                 
3061                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3062                                 
3063                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3064                                     
3065                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3066                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3067                                         // Roo.log('active panel height: '+this.getChildHeight(layout_body_tab_childs[m].childNodes));
3068                                         continue;
3069                                     }
3070                                     
3071                                 }
3072                                 
3073                             }
3074                             
3075                         }
3076                     }
3077                 }
3078                 continue;
3079             }
3080             
3081             child_height += child_nodes[i].offsetHeight;
3082         }
3083         
3084         return child_height;
3085     }
3086
3087 });
3088
3089
3090 Roo.apply(Roo.bootstrap.Modal,  {
3091     /**
3092          * Button config that displays a single OK button
3093          * @type Object
3094          */
3095         OK :  [{
3096             name : 'ok',
3097             weight : 'primary',
3098             html : 'OK'
3099         }],
3100         /**
3101          * Button config that displays Yes and No buttons
3102          * @type Object
3103          */
3104         YESNO : [
3105             {
3106                 name  : 'no',
3107                 html : 'No'
3108             },
3109             {
3110                 name  :'yes',
3111                 weight : 'primary',
3112                 html : 'Yes'
3113             }
3114         ],
3115
3116         /**
3117          * Button config that displays OK and Cancel buttons
3118          * @type Object
3119          */
3120         OKCANCEL : [
3121             {
3122                name : 'cancel',
3123                 html : 'Cancel'
3124             },
3125             {
3126                 name : 'ok',
3127                 weight : 'primary',
3128                 html : 'OK'
3129             }
3130         ],
3131         /**
3132          * Button config that displays Yes, No and Cancel buttons
3133          * @type Object
3134          */
3135         YESNOCANCEL : [
3136             {
3137                 name : 'yes',
3138                 weight : 'primary',
3139                 html : 'Yes'
3140             },
3141             {
3142                 name : 'no',
3143                 html : 'No'
3144             },
3145             {
3146                 name : 'cancel',
3147                 html : 'Cancel'
3148             }
3149         ],
3150         
3151         zIndex : 10001
3152 });
3153 /*
3154  * - LGPL
3155  *
3156  * messagebox - can be used as a replace
3157  * 
3158  */
3159 /**
3160  * @class Roo.MessageBox
3161  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3162  * Example usage:
3163  *<pre><code>
3164 // Basic alert:
3165 Roo.Msg.alert('Status', 'Changes saved successfully.');
3166
3167 // Prompt for user data:
3168 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3169     if (btn == 'ok'){
3170         // process text value...
3171     }
3172 });
3173
3174 // Show a dialog using config options:
3175 Roo.Msg.show({
3176    title:'Save Changes?',
3177    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3178    buttons: Roo.Msg.YESNOCANCEL,
3179    fn: processResult,
3180    animEl: 'elId'
3181 });
3182 </code></pre>
3183  * @singleton
3184  */
3185 Roo.bootstrap.MessageBox = function(){
3186     var dlg, opt, mask, waitTimer;
3187     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3188     var buttons, activeTextEl, bwidth;
3189
3190     
3191     // private
3192     var handleButton = function(button){
3193         dlg.hide();
3194         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3195     };
3196
3197     // private
3198     var handleHide = function(){
3199         if(opt && opt.cls){
3200             dlg.el.removeClass(opt.cls);
3201         }
3202         //if(waitTimer){
3203         //    Roo.TaskMgr.stop(waitTimer);
3204         //    waitTimer = null;
3205         //}
3206     };
3207
3208     // private
3209     var updateButtons = function(b){
3210         var width = 0;
3211         if(!b){
3212             buttons["ok"].hide();
3213             buttons["cancel"].hide();
3214             buttons["yes"].hide();
3215             buttons["no"].hide();
3216             //dlg.footer.dom.style.display = 'none';
3217             return width;
3218         }
3219         dlg.footerEl.dom.style.display = '';
3220         for(var k in buttons){
3221             if(typeof buttons[k] != "function"){
3222                 if(b[k]){
3223                     buttons[k].show();
3224                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3225                     width += buttons[k].el.getWidth()+15;
3226                 }else{
3227                     buttons[k].hide();
3228                 }
3229             }
3230         }
3231         return width;
3232     };
3233
3234     // private
3235     var handleEsc = function(d, k, e){
3236         if(opt && opt.closable !== false){
3237             dlg.hide();
3238         }
3239         if(e){
3240             e.stopEvent();
3241         }
3242     };
3243
3244     return {
3245         /**
3246          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3247          * @return {Roo.BasicDialog} The BasicDialog element
3248          */
3249         getDialog : function(){
3250            if(!dlg){
3251                 dlg = new Roo.bootstrap.Modal( {
3252                     //draggable: true,
3253                     //resizable:false,
3254                     //constraintoviewport:false,
3255                     //fixedcenter:true,
3256                     //collapsible : false,
3257                     //shim:true,
3258                     //modal: true,
3259                 //    width: 'auto',
3260                   //  height:100,
3261                     //buttonAlign:"center",
3262                     closeClick : function(){
3263                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3264                             handleButton("no");
3265                         }else{
3266                             handleButton("cancel");
3267                         }
3268                     }
3269                 });
3270                 dlg.render();
3271                 dlg.on("hide", handleHide);
3272                 mask = dlg.mask;
3273                 //dlg.addKeyListener(27, handleEsc);
3274                 buttons = {};
3275                 this.buttons = buttons;
3276                 var bt = this.buttonText;
3277                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3278                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3279                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3280                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3281                 //Roo.log(buttons);
3282                 bodyEl = dlg.bodyEl.createChild({
3283
3284                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3285                         '<textarea class="roo-mb-textarea"></textarea>' +
3286                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3287                 });
3288                 msgEl = bodyEl.dom.firstChild;
3289                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3290                 textboxEl.enableDisplayMode();
3291                 textboxEl.addKeyListener([10,13], function(){
3292                     if(dlg.isVisible() && opt && opt.buttons){
3293                         if(opt.buttons.ok){
3294                             handleButton("ok");
3295                         }else if(opt.buttons.yes){
3296                             handleButton("yes");
3297                         }
3298                     }
3299                 });
3300                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3301                 textareaEl.enableDisplayMode();
3302                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3303                 progressEl.enableDisplayMode();
3304                 
3305                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3306                 var pf = progressEl.dom.firstChild;
3307                 if (pf) {
3308                     pp = Roo.get(pf.firstChild);
3309                     pp.setHeight(pf.offsetHeight);
3310                 }
3311                 
3312             }
3313             return dlg;
3314         },
3315
3316         /**
3317          * Updates the message box body text
3318          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3319          * the XHTML-compliant non-breaking space character '&amp;#160;')
3320          * @return {Roo.MessageBox} This message box
3321          */
3322         updateText : function(text)
3323         {
3324             if(!dlg.isVisible() && !opt.width){
3325                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3326                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3327             }
3328             msgEl.innerHTML = text || '&#160;';
3329       
3330             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3331             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3332             var w = Math.max(
3333                     Math.min(opt.width || cw , this.maxWidth), 
3334                     Math.max(opt.minWidth || this.minWidth, bwidth)
3335             );
3336             if(opt.prompt){
3337                 activeTextEl.setWidth(w);
3338             }
3339             if(dlg.isVisible()){
3340                 dlg.fixedcenter = false;
3341             }
3342             // to big, make it scroll. = But as usual stupid IE does not support
3343             // !important..
3344             
3345             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3346                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3347                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3348             } else {
3349                 bodyEl.dom.style.height = '';
3350                 bodyEl.dom.style.overflowY = '';
3351             }
3352             if (cw > w) {
3353                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3354             } else {
3355                 bodyEl.dom.style.overflowX = '';
3356             }
3357             
3358             dlg.setContentSize(w, bodyEl.getHeight());
3359             if(dlg.isVisible()){
3360                 dlg.fixedcenter = true;
3361             }
3362             return this;
3363         },
3364
3365         /**
3366          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3367          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3368          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3369          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3370          * @return {Roo.MessageBox} This message box
3371          */
3372         updateProgress : function(value, text){
3373             if(text){
3374                 this.updateText(text);
3375             }
3376             
3377             if (pp) { // weird bug on my firefox - for some reason this is not defined
3378                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3379                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3380             }
3381             return this;
3382         },        
3383
3384         /**
3385          * Returns true if the message box is currently displayed
3386          * @return {Boolean} True if the message box is visible, else false
3387          */
3388         isVisible : function(){
3389             return dlg && dlg.isVisible();  
3390         },
3391
3392         /**
3393          * Hides the message box if it is displayed
3394          */
3395         hide : function(){
3396             if(this.isVisible()){
3397                 dlg.hide();
3398             }  
3399         },
3400
3401         /**
3402          * Displays a new message box, or reinitializes an existing message box, based on the config options
3403          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3404          * The following config object properties are supported:
3405          * <pre>
3406 Property    Type             Description
3407 ----------  ---------------  ------------------------------------------------------------------------------------
3408 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3409                                    closes (defaults to undefined)
3410 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3411                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3412 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3413                                    progress and wait dialogs will ignore this property and always hide the
3414                                    close button as they can only be closed programmatically.
3415 cls               String           A custom CSS class to apply to the message box element
3416 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3417                                    displayed (defaults to 75)
3418 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3419                                    function will be btn (the name of the button that was clicked, if applicable,
3420                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3421                                    Progress and wait dialogs will ignore this option since they do not respond to
3422                                    user actions and can only be closed programmatically, so any required function
3423                                    should be called by the same code after it closes the dialog.
3424 icon              String           A CSS class that provides a background image to be used as an icon for
3425                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3426 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3427 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3428 modal             Boolean          False to allow user interaction with the page while the message box is
3429                                    displayed (defaults to true)
3430 msg               String           A string that will replace the existing message box body text (defaults
3431                                    to the XHTML-compliant non-breaking space character '&#160;')
3432 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3433 progress          Boolean          True to display a progress bar (defaults to false)
3434 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3435 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3436 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3437 title             String           The title text
3438 value             String           The string value to set into the active textbox element if displayed
3439 wait              Boolean          True to display a progress bar (defaults to false)
3440 width             Number           The width of the dialog in pixels
3441 </pre>
3442          *
3443          * Example usage:
3444          * <pre><code>
3445 Roo.Msg.show({
3446    title: 'Address',
3447    msg: 'Please enter your address:',
3448    width: 300,
3449    buttons: Roo.MessageBox.OKCANCEL,
3450    multiline: true,
3451    fn: saveAddress,
3452    animEl: 'addAddressBtn'
3453 });
3454 </code></pre>
3455          * @param {Object} config Configuration options
3456          * @return {Roo.MessageBox} This message box
3457          */
3458         show : function(options)
3459         {
3460             
3461             // this causes nightmares if you show one dialog after another
3462             // especially on callbacks..
3463              
3464             if(this.isVisible()){
3465                 
3466                 this.hide();
3467                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3468                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3469                 Roo.log("New Dialog Message:" +  options.msg )
3470                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3471                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3472                 
3473             }
3474             var d = this.getDialog();
3475             opt = options;
3476             d.setTitle(opt.title || "&#160;");
3477             d.closeEl.setDisplayed(opt.closable !== false);
3478             activeTextEl = textboxEl;
3479             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3480             if(opt.prompt){
3481                 if(opt.multiline){
3482                     textboxEl.hide();
3483                     textareaEl.show();
3484                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3485                         opt.multiline : this.defaultTextHeight);
3486                     activeTextEl = textareaEl;
3487                 }else{
3488                     textboxEl.show();
3489                     textareaEl.hide();
3490                 }
3491             }else{
3492                 textboxEl.hide();
3493                 textareaEl.hide();
3494             }
3495             progressEl.setDisplayed(opt.progress === true);
3496             this.updateProgress(0);
3497             activeTextEl.dom.value = opt.value || "";
3498             if(opt.prompt){
3499                 dlg.setDefaultButton(activeTextEl);
3500             }else{
3501                 var bs = opt.buttons;
3502                 var db = null;
3503                 if(bs && bs.ok){
3504                     db = buttons["ok"];
3505                 }else if(bs && bs.yes){
3506                     db = buttons["yes"];
3507                 }
3508                 dlg.setDefaultButton(db);
3509             }
3510             bwidth = updateButtons(opt.buttons);
3511             this.updateText(opt.msg);
3512             if(opt.cls){
3513                 d.el.addClass(opt.cls);
3514             }
3515             d.proxyDrag = opt.proxyDrag === true;
3516             d.modal = opt.modal !== false;
3517             d.mask = opt.modal !== false ? mask : false;
3518             if(!d.isVisible()){
3519                 // force it to the end of the z-index stack so it gets a cursor in FF
3520                 document.body.appendChild(dlg.el.dom);
3521                 d.animateTarget = null;
3522                 d.show(options.animEl);
3523             }
3524             return this;
3525         },
3526
3527         /**
3528          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3529          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3530          * and closing the message box when the process is complete.
3531          * @param {String} title The title bar text
3532          * @param {String} msg The message box body text
3533          * @return {Roo.MessageBox} This message box
3534          */
3535         progress : function(title, msg){
3536             this.show({
3537                 title : title,
3538                 msg : msg,
3539                 buttons: false,
3540                 progress:true,
3541                 closable:false,
3542                 minWidth: this.minProgressWidth,
3543                 modal : true
3544             });
3545             return this;
3546         },
3547
3548         /**
3549          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3550          * If a callback function is passed it will be called after the user clicks the button, and the
3551          * id of the button that was clicked will be passed as the only parameter to the callback
3552          * (could also be the top-right close button).
3553          * @param {String} title The title bar text
3554          * @param {String} msg The message box body text
3555          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3556          * @param {Object} scope (optional) The scope of the callback function
3557          * @return {Roo.MessageBox} This message box
3558          */
3559         alert : function(title, msg, fn, scope)
3560         {
3561             this.show({
3562                 title : title,
3563                 msg : msg,
3564                 buttons: this.OK,
3565                 fn: fn,
3566                 closable : false,
3567                 scope : scope,
3568                 modal : true
3569             });
3570             return this;
3571         },
3572
3573         /**
3574          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3575          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3576          * You are responsible for closing the message box when the process is complete.
3577          * @param {String} msg The message box body text
3578          * @param {String} title (optional) The title bar text
3579          * @return {Roo.MessageBox} This message box
3580          */
3581         wait : function(msg, title){
3582             this.show({
3583                 title : title,
3584                 msg : msg,
3585                 buttons: false,
3586                 closable:false,
3587                 progress:true,
3588                 modal:true,
3589                 width:300,
3590                 wait:true
3591             });
3592             waitTimer = Roo.TaskMgr.start({
3593                 run: function(i){
3594                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3595                 },
3596                 interval: 1000
3597             });
3598             return this;
3599         },
3600
3601         /**
3602          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3603          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3604          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3605          * @param {String} title The title bar text
3606          * @param {String} msg The message box body text
3607          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3608          * @param {Object} scope (optional) The scope of the callback function
3609          * @return {Roo.MessageBox} This message box
3610          */
3611         confirm : function(title, msg, fn, scope){
3612             this.show({
3613                 title : title,
3614                 msg : msg,
3615                 buttons: this.YESNO,
3616                 fn: fn,
3617                 scope : scope,
3618                 modal : true
3619             });
3620             return this;
3621         },
3622
3623         /**
3624          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3625          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3626          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3627          * (could also be the top-right close button) and the text that was entered will be passed as the two
3628          * parameters to the callback.
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          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3634          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3635          * @return {Roo.MessageBox} This message box
3636          */
3637         prompt : function(title, msg, fn, scope, multiline){
3638             this.show({
3639                 title : title,
3640                 msg : msg,
3641                 buttons: this.OKCANCEL,
3642                 fn: fn,
3643                 minWidth:250,
3644                 scope : scope,
3645                 prompt:true,
3646                 multiline: multiline,
3647                 modal : true
3648             });
3649             return this;
3650         },
3651
3652         /**
3653          * Button config that displays a single OK button
3654          * @type Object
3655          */
3656         OK : {ok:true},
3657         /**
3658          * Button config that displays Yes and No buttons
3659          * @type Object
3660          */
3661         YESNO : {yes:true, no:true},
3662         /**
3663          * Button config that displays OK and Cancel buttons
3664          * @type Object
3665          */
3666         OKCANCEL : {ok:true, cancel:true},
3667         /**
3668          * Button config that displays Yes, No and Cancel buttons
3669          * @type Object
3670          */
3671         YESNOCANCEL : {yes:true, no:true, cancel:true},
3672
3673         /**
3674          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3675          * @type Number
3676          */
3677         defaultTextHeight : 75,
3678         /**
3679          * The maximum width in pixels of the message box (defaults to 600)
3680          * @type Number
3681          */
3682         maxWidth : 600,
3683         /**
3684          * The minimum width in pixels of the message box (defaults to 100)
3685          * @type Number
3686          */
3687         minWidth : 100,
3688         /**
3689          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3690          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3691          * @type Number
3692          */
3693         minProgressWidth : 250,
3694         /**
3695          * An object containing the default button text strings that can be overriden for localized language support.
3696          * Supported properties are: ok, cancel, yes and no.
3697          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3698          * @type Object
3699          */
3700         buttonText : {
3701             ok : "OK",
3702             cancel : "Cancel",
3703             yes : "Yes",
3704             no : "No"
3705         }
3706     };
3707 }();
3708
3709 /**
3710  * Shorthand for {@link Roo.MessageBox}
3711  */
3712 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3713 Roo.Msg = Roo.Msg || Roo.MessageBox;
3714 /*
3715  * - LGPL
3716  *
3717  * navbar
3718  * 
3719  */
3720
3721 /**
3722  * @class Roo.bootstrap.Navbar
3723  * @extends Roo.bootstrap.Component
3724  * Bootstrap Navbar class
3725
3726  * @constructor
3727  * Create a new Navbar
3728  * @param {Object} config The config object
3729  */
3730
3731
3732 Roo.bootstrap.Navbar = function(config){
3733     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3734     this.addEvents({
3735         // raw events
3736         /**
3737          * @event beforetoggle
3738          * Fire before toggle the menu
3739          * @param {Roo.EventObject} e
3740          */
3741         "beforetoggle" : true
3742     });
3743 };
3744
3745 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3746     
3747     
3748    
3749     // private
3750     navItems : false,
3751     loadMask : false,
3752     
3753     
3754     getAutoCreate : function(){
3755         
3756         
3757         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3758         
3759     },
3760     
3761     initEvents :function ()
3762     {
3763         //Roo.log(this.el.select('.navbar-toggle',true));
3764         this.el.select('.navbar-toggle',true).on('click', function() {
3765             if(this.fireEvent('beforetoggle', this) !== false){
3766                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3767             }
3768             
3769         }, this);
3770         
3771         var mark = {
3772             tag: "div",
3773             cls:"x-dlg-mask"
3774         };
3775         
3776         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3777         
3778         var size = this.el.getSize();
3779         this.maskEl.setSize(size.width, size.height);
3780         this.maskEl.enableDisplayMode("block");
3781         this.maskEl.hide();
3782         
3783         if(this.loadMask){
3784             this.maskEl.show();
3785         }
3786     },
3787     
3788     
3789     getChildContainer : function()
3790     {
3791         if (this.el.select('.collapse').getCount()) {
3792             return this.el.select('.collapse',true).first();
3793         }
3794         
3795         return this.el;
3796     },
3797     
3798     mask : function()
3799     {
3800         this.maskEl.show();
3801     },
3802     
3803     unmask : function()
3804     {
3805         this.maskEl.hide();
3806     } 
3807     
3808     
3809     
3810     
3811 });
3812
3813
3814
3815  
3816
3817  /*
3818  * - LGPL
3819  *
3820  * navbar
3821  * 
3822  */
3823
3824 /**
3825  * @class Roo.bootstrap.NavSimplebar
3826  * @extends Roo.bootstrap.Navbar
3827  * Bootstrap Sidebar class
3828  *
3829  * @cfg {Boolean} inverse is inverted color
3830  * 
3831  * @cfg {String} type (nav | pills | tabs)
3832  * @cfg {Boolean} arrangement stacked | justified
3833  * @cfg {String} align (left | right) alignment
3834  * 
3835  * @cfg {Boolean} main (true|false) main nav bar? default false
3836  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3837  * 
3838  * @cfg {String} tag (header|footer|nav|div) default is nav 
3839
3840  * 
3841  * 
3842  * 
3843  * @constructor
3844  * Create a new Sidebar
3845  * @param {Object} config The config object
3846  */
3847
3848
3849 Roo.bootstrap.NavSimplebar = function(config){
3850     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3851 };
3852
3853 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3854     
3855     inverse: false,
3856     
3857     type: false,
3858     arrangement: '',
3859     align : false,
3860     
3861     
3862     
3863     main : false,
3864     
3865     
3866     tag : false,
3867     
3868     
3869     getAutoCreate : function(){
3870         
3871         
3872         var cfg = {
3873             tag : this.tag || 'div',
3874             cls : 'navbar'
3875         };
3876           
3877         
3878         cfg.cn = [
3879             {
3880                 cls: 'nav',
3881                 tag : 'ul'
3882             }
3883         ];
3884         
3885          
3886         this.type = this.type || 'nav';
3887         if (['tabs','pills'].indexOf(this.type)!==-1) {
3888             cfg.cn[0].cls += ' nav-' + this.type
3889         
3890         
3891         } else {
3892             if (this.type!=='nav') {
3893                 Roo.log('nav type must be nav/tabs/pills')
3894             }
3895             cfg.cn[0].cls += ' navbar-nav'
3896         }
3897         
3898         
3899         
3900         
3901         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3902             cfg.cn[0].cls += ' nav-' + this.arrangement;
3903         }
3904         
3905         
3906         if (this.align === 'right') {
3907             cfg.cn[0].cls += ' navbar-right';
3908         }
3909         
3910         if (this.inverse) {
3911             cfg.cls += ' navbar-inverse';
3912             
3913         }
3914         
3915         
3916         return cfg;
3917     
3918         
3919     }
3920     
3921     
3922     
3923 });
3924
3925
3926
3927  
3928
3929  
3930        /*
3931  * - LGPL
3932  *
3933  * navbar
3934  * 
3935  */
3936
3937 /**
3938  * @class Roo.bootstrap.NavHeaderbar
3939  * @extends Roo.bootstrap.NavSimplebar
3940  * Bootstrap Sidebar class
3941  *
3942  * @cfg {String} brand what is brand
3943  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3944  * @cfg {String} brand_href href of the brand
3945  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3946  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3947  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3948  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3949  * 
3950  * @constructor
3951  * Create a new Sidebar
3952  * @param {Object} config The config object
3953  */
3954
3955
3956 Roo.bootstrap.NavHeaderbar = function(config){
3957     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3958       
3959 };
3960
3961 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3962     
3963     position: '',
3964     brand: '',
3965     brand_href: false,
3966     srButton : true,
3967     autohide : false,
3968     desktopCenter : false,
3969    
3970     
3971     getAutoCreate : function(){
3972         
3973         var   cfg = {
3974             tag: this.nav || 'nav',
3975             cls: 'navbar',
3976             role: 'navigation',
3977             cn: []
3978         };
3979         
3980         var cn = cfg.cn;
3981         if (this.desktopCenter) {
3982             cn.push({cls : 'container', cn : []});
3983             cn = cn[0].cn;
3984         }
3985         
3986         if(this.srButton){
3987             cn.push({
3988                 tag: 'div',
3989                 cls: 'navbar-header',
3990                 cn: [
3991                     {
3992                         tag: 'button',
3993                         type: 'button',
3994                         cls: 'navbar-toggle',
3995                         'data-toggle': 'collapse',
3996                         cn: [
3997                             {
3998                                 tag: 'span',
3999                                 cls: 'sr-only',
4000                                 html: 'Toggle navigation'
4001                             },
4002                             {
4003                                 tag: 'span',
4004                                 cls: 'icon-bar'
4005                             },
4006                             {
4007                                 tag: 'span',
4008                                 cls: 'icon-bar'
4009                             },
4010                             {
4011                                 tag: 'span',
4012                                 cls: 'icon-bar'
4013                             }
4014                         ]
4015                     }
4016                 ]
4017             });
4018         }
4019         
4020         cn.push({
4021             tag: 'div',
4022             cls: 'collapse navbar-collapse',
4023             cn : []
4024         });
4025         
4026         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4027         
4028         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4029             cfg.cls += ' navbar-' + this.position;
4030             
4031             // tag can override this..
4032             
4033             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4034         }
4035         
4036         if (this.brand !== '') {
4037             cn[0].cn.push({
4038                 tag: 'a',
4039                 href: this.brand_href ? this.brand_href : '#',
4040                 cls: 'navbar-brand',
4041                 cn: [
4042                 this.brand
4043                 ]
4044             });
4045         }
4046         
4047         if(this.main){
4048             cfg.cls += ' main-nav';
4049         }
4050         
4051         
4052         return cfg;
4053
4054         
4055     },
4056     getHeaderChildContainer : function()
4057     {
4058         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4059             return this.el.select('.navbar-header',true).first();
4060         }
4061         
4062         return this.getChildContainer();
4063     },
4064     
4065     
4066     initEvents : function()
4067     {
4068         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4069         
4070         if (this.autohide) {
4071             
4072             var prevScroll = 0;
4073             var ft = this.el;
4074             
4075             Roo.get(document).on('scroll',function(e) {
4076                 var ns = Roo.get(document).getScroll().top;
4077                 var os = prevScroll;
4078                 prevScroll = ns;
4079                 
4080                 if(ns > os){
4081                     ft.removeClass('slideDown');
4082                     ft.addClass('slideUp');
4083                     return;
4084                 }
4085                 ft.removeClass('slideUp');
4086                 ft.addClass('slideDown');
4087                  
4088               
4089           },this);
4090         }
4091     }    
4092     
4093 });
4094
4095
4096
4097  
4098
4099  /*
4100  * - LGPL
4101  *
4102  * navbar
4103  * 
4104  */
4105
4106 /**
4107  * @class Roo.bootstrap.NavSidebar
4108  * @extends Roo.bootstrap.Navbar
4109  * Bootstrap Sidebar class
4110  * 
4111  * @constructor
4112  * Create a new Sidebar
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.NavSidebar = function(config){
4118     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4119 };
4120
4121 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4122     
4123     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4124     
4125     getAutoCreate : function(){
4126         
4127         
4128         return  {
4129             tag: 'div',
4130             cls: 'sidebar sidebar-nav'
4131         };
4132     
4133         
4134     }
4135     
4136     
4137     
4138 });
4139
4140
4141
4142  
4143
4144  /*
4145  * - LGPL
4146  *
4147  * nav group
4148  * 
4149  */
4150
4151 /**
4152  * @class Roo.bootstrap.NavGroup
4153  * @extends Roo.bootstrap.Component
4154  * Bootstrap NavGroup class
4155  * @cfg {String} align (left|right)
4156  * @cfg {Boolean} inverse
4157  * @cfg {String} type (nav|pills|tab) default nav
4158  * @cfg {String} navId - reference Id for navbar.
4159
4160  * 
4161  * @constructor
4162  * Create a new nav group
4163  * @param {Object} config The config object
4164  */
4165
4166 Roo.bootstrap.NavGroup = function(config){
4167     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4168     this.navItems = [];
4169    
4170     Roo.bootstrap.NavGroup.register(this);
4171      this.addEvents({
4172         /**
4173              * @event changed
4174              * Fires when the active item changes
4175              * @param {Roo.bootstrap.NavGroup} this
4176              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4177              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4178          */
4179         'changed': true
4180      });
4181     
4182 };
4183
4184 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4185     
4186     align: '',
4187     inverse: false,
4188     form: false,
4189     type: 'nav',
4190     navId : '',
4191     // private
4192     
4193     navItems : false, 
4194     
4195     getAutoCreate : function()
4196     {
4197         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4198         
4199         cfg = {
4200             tag : 'ul',
4201             cls: 'nav' 
4202         };
4203         
4204         if (['tabs','pills'].indexOf(this.type)!==-1) {
4205             cfg.cls += ' nav-' + this.type
4206         } else {
4207             if (this.type!=='nav') {
4208                 Roo.log('nav type must be nav/tabs/pills')
4209             }
4210             cfg.cls += ' navbar-nav'
4211         }
4212         
4213         if (this.parent() && this.parent().sidebar) {
4214             cfg = {
4215                 tag: 'ul',
4216                 cls: 'dashboard-menu sidebar-menu'
4217             };
4218             
4219             return cfg;
4220         }
4221         
4222         if (this.form === true) {
4223             cfg = {
4224                 tag: 'form',
4225                 cls: 'navbar-form'
4226             };
4227             
4228             if (this.align === 'right') {
4229                 cfg.cls += ' navbar-right';
4230             } else {
4231                 cfg.cls += ' navbar-left';
4232             }
4233         }
4234         
4235         if (this.align === 'right') {
4236             cfg.cls += ' navbar-right';
4237         }
4238         
4239         if (this.inverse) {
4240             cfg.cls += ' navbar-inverse';
4241             
4242         }
4243         
4244         
4245         return cfg;
4246     },
4247     /**
4248     * sets the active Navigation item
4249     * @param {Roo.bootstrap.NavItem} the new current navitem
4250     */
4251     setActiveItem : function(item)
4252     {
4253         var prev = false;
4254         Roo.each(this.navItems, function(v){
4255             if (v == item) {
4256                 return ;
4257             }
4258             if (v.isActive()) {
4259                 v.setActive(false, true);
4260                 prev = v;
4261                 
4262             }
4263             
4264         });
4265
4266         item.setActive(true, true);
4267         this.fireEvent('changed', this, item, prev);
4268         
4269         
4270     },
4271     /**
4272     * gets the active Navigation item
4273     * @return {Roo.bootstrap.NavItem} the current navitem
4274     */
4275     getActive : function()
4276     {
4277         
4278         var prev = false;
4279         Roo.each(this.navItems, function(v){
4280             
4281             if (v.isActive()) {
4282                 prev = v;
4283                 
4284             }
4285             
4286         });
4287         return prev;
4288     },
4289     
4290     indexOfNav : function()
4291     {
4292         
4293         var prev = false;
4294         Roo.each(this.navItems, function(v,i){
4295             
4296             if (v.isActive()) {
4297                 prev = i;
4298                 
4299             }
4300             
4301         });
4302         return prev;
4303     },
4304     /**
4305     * adds a Navigation item
4306     * @param {Roo.bootstrap.NavItem} the navitem to add
4307     */
4308     addItem : function(cfg)
4309     {
4310         var cn = new Roo.bootstrap.NavItem(cfg);
4311         this.register(cn);
4312         cn.parentId = this.id;
4313         cn.onRender(this.el, null);
4314         return cn;
4315     },
4316     /**
4317     * register a Navigation item
4318     * @param {Roo.bootstrap.NavItem} the navitem to add
4319     */
4320     register : function(item)
4321     {
4322         this.navItems.push( item);
4323         item.navId = this.navId;
4324     
4325     },
4326     
4327     /**
4328     * clear all the Navigation item
4329     */
4330    
4331     clearAll : function()
4332     {
4333         this.navItems = [];
4334         this.el.dom.innerHTML = '';
4335     },
4336     
4337     getNavItem: function(tabId)
4338     {
4339         var ret = false;
4340         Roo.each(this.navItems, function(e) {
4341             if (e.tabId == tabId) {
4342                ret =  e;
4343                return false;
4344             }
4345             return true;
4346             
4347         });
4348         return ret;
4349     },
4350     
4351     setActiveNext : function()
4352     {
4353         var i = this.indexOfNav(this.getActive());
4354         if (i > this.navItems.length) {
4355             return;
4356         }
4357         this.setActiveItem(this.navItems[i+1]);
4358     },
4359     setActivePrev : function()
4360     {
4361         var i = this.indexOfNav(this.getActive());
4362         if (i  < 1) {
4363             return;
4364         }
4365         this.setActiveItem(this.navItems[i-1]);
4366     },
4367     clearWasActive : function(except) {
4368         Roo.each(this.navItems, function(e) {
4369             if (e.tabId != except.tabId && e.was_active) {
4370                e.was_active = false;
4371                return false;
4372             }
4373             return true;
4374             
4375         });
4376     },
4377     getWasActive : function ()
4378     {
4379         var r = false;
4380         Roo.each(this.navItems, function(e) {
4381             if (e.was_active) {
4382                r = e;
4383                return false;
4384             }
4385             return true;
4386             
4387         });
4388         return r;
4389     }
4390     
4391     
4392 });
4393
4394  
4395 Roo.apply(Roo.bootstrap.NavGroup, {
4396     
4397     groups: {},
4398      /**
4399     * register a Navigation Group
4400     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4401     */
4402     register : function(navgrp)
4403     {
4404         this.groups[navgrp.navId] = navgrp;
4405         
4406     },
4407     /**
4408     * fetch a Navigation Group based on the navigation ID
4409     * @param {string} the navgroup to add
4410     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4411     */
4412     get: function(navId) {
4413         if (typeof(this.groups[navId]) == 'undefined') {
4414             return false;
4415             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4416         }
4417         return this.groups[navId] ;
4418     }
4419     
4420     
4421     
4422 });
4423
4424  /*
4425  * - LGPL
4426  *
4427  * row
4428  * 
4429  */
4430
4431 /**
4432  * @class Roo.bootstrap.NavItem
4433  * @extends Roo.bootstrap.Component
4434  * Bootstrap Navbar.NavItem class
4435  * @cfg {String} href  link to
4436  * @cfg {String} html content of button
4437  * @cfg {String} badge text inside badge
4438  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4439  * @cfg {String} glyphicon name of glyphicon
4440  * @cfg {String} icon name of font awesome icon
4441  * @cfg {Boolean} active Is item active
4442  * @cfg {Boolean} disabled Is item disabled
4443  
4444  * @cfg {Boolean} preventDefault (true | false) default false
4445  * @cfg {String} tabId the tab that this item activates.
4446  * @cfg {String} tagtype (a|span) render as a href or span?
4447  * @cfg {Boolean} animateRef (true|false) link to element default false  
4448   
4449  * @constructor
4450  * Create a new Navbar Item
4451  * @param {Object} config The config object
4452  */
4453 Roo.bootstrap.NavItem = function(config){
4454     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4455     this.addEvents({
4456         // raw events
4457         /**
4458          * @event click
4459          * The raw click event for the entire grid.
4460          * @param {Roo.EventObject} e
4461          */
4462         "click" : true,
4463          /**
4464             * @event changed
4465             * Fires when the active item active state changes
4466             * @param {Roo.bootstrap.NavItem} this
4467             * @param {boolean} state the new state
4468              
4469          */
4470         'changed': true,
4471         /**
4472             * @event scrollto
4473             * Fires when scroll to element
4474             * @param {Roo.bootstrap.NavItem} this
4475             * @param {Object} options
4476             * @param {Roo.EventObject} e
4477              
4478          */
4479         'scrollto': true
4480     });
4481    
4482 };
4483
4484 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4485     
4486     href: false,
4487     html: '',
4488     badge: '',
4489     icon: false,
4490     glyphicon: false,
4491     active: false,
4492     preventDefault : false,
4493     tabId : false,
4494     tagtype : 'a',
4495     disabled : false,
4496     animateRef : false,
4497     was_active : false,
4498     
4499     getAutoCreate : function(){
4500          
4501         var cfg = {
4502             tag: 'li',
4503             cls: 'nav-item'
4504             
4505         };
4506         
4507         if (this.active) {
4508             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4509         }
4510         if (this.disabled) {
4511             cfg.cls += ' disabled';
4512         }
4513         
4514         if (this.href || this.html || this.glyphicon || this.icon) {
4515             cfg.cn = [
4516                 {
4517                     tag: this.tagtype,
4518                     href : this.href || "#",
4519                     html: this.html || ''
4520                 }
4521             ];
4522             
4523             if (this.icon) {
4524                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4525             }
4526
4527             if(this.glyphicon) {
4528                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4529             }
4530             
4531             if (this.menu) {
4532                 
4533                 cfg.cn[0].html += " <span class='caret'></span>";
4534              
4535             }
4536             
4537             if (this.badge !== '') {
4538                  
4539                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4540             }
4541         }
4542         
4543         
4544         
4545         return cfg;
4546     },
4547     initEvents: function() 
4548     {
4549         if (typeof (this.menu) != 'undefined') {
4550             this.menu.parentType = this.xtype;
4551             this.menu.triggerEl = this.el;
4552             this.menu = this.addxtype(Roo.apply({}, this.menu));
4553         }
4554         
4555         this.el.select('a',true).on('click', this.onClick, this);
4556         
4557         if(this.tagtype == 'span'){
4558             this.el.select('span',true).on('click', this.onClick, this);
4559         }
4560        
4561         // at this point parent should be available..
4562         this.parent().register(this);
4563     },
4564     
4565     onClick : function(e)
4566     {
4567         if (e.getTarget('.dropdown-menu-item')) {
4568             // did you click on a menu itemm.... - then don't trigger onclick..
4569             return;
4570         }
4571         
4572         if(
4573                 this.preventDefault || 
4574                 this.href == '#' 
4575         ){
4576             Roo.log("NavItem - prevent Default?");
4577             e.preventDefault();
4578         }
4579         
4580         if (this.disabled) {
4581             return;
4582         }
4583         
4584         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4585         if (tg && tg.transition) {
4586             Roo.log("waiting for the transitionend");
4587             return;
4588         }
4589         
4590         
4591         
4592         //Roo.log("fire event clicked");
4593         if(this.fireEvent('click', this, e) === false){
4594             return;
4595         };
4596         
4597         if(this.tagtype == 'span'){
4598             return;
4599         }
4600         
4601         //Roo.log(this.href);
4602         var ael = this.el.select('a',true).first();
4603         //Roo.log(ael);
4604         
4605         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4606             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4607             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4608                 return; // ignore... - it's a 'hash' to another page.
4609             }
4610             Roo.log("NavItem - prevent Default?");
4611             e.preventDefault();
4612             this.scrollToElement(e);
4613         }
4614         
4615         
4616         var p =  this.parent();
4617    
4618         if (['tabs','pills'].indexOf(p.type)!==-1) {
4619             if (typeof(p.setActiveItem) !== 'undefined') {
4620                 p.setActiveItem(this);
4621             }
4622         }
4623         
4624         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4625         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4626             // remove the collapsed menu expand...
4627             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4628         }
4629     },
4630     
4631     isActive: function () {
4632         return this.active
4633     },
4634     setActive : function(state, fire, is_was_active)
4635     {
4636         if (this.active && !state && this.navId) {
4637             this.was_active = true;
4638             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4639             if (nv) {
4640                 nv.clearWasActive(this);
4641             }
4642             
4643         }
4644         this.active = state;
4645         
4646         if (!state ) {
4647             this.el.removeClass('active');
4648         } else if (!this.el.hasClass('active')) {
4649             this.el.addClass('active');
4650         }
4651         if (fire) {
4652             this.fireEvent('changed', this, state);
4653         }
4654         
4655         // show a panel if it's registered and related..
4656         
4657         if (!this.navId || !this.tabId || !state || is_was_active) {
4658             return;
4659         }
4660         
4661         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4662         if (!tg) {
4663             return;
4664         }
4665         var pan = tg.getPanelByName(this.tabId);
4666         if (!pan) {
4667             return;
4668         }
4669         // if we can not flip to new panel - go back to old nav highlight..
4670         if (false == tg.showPanel(pan)) {
4671             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4672             if (nv) {
4673                 var onav = nv.getWasActive();
4674                 if (onav) {
4675                     onav.setActive(true, false, true);
4676                 }
4677             }
4678             
4679         }
4680         
4681         
4682         
4683     },
4684      // this should not be here...
4685     setDisabled : function(state)
4686     {
4687         this.disabled = state;
4688         if (!state ) {
4689             this.el.removeClass('disabled');
4690         } else if (!this.el.hasClass('disabled')) {
4691             this.el.addClass('disabled');
4692         }
4693         
4694     },
4695     
4696     /**
4697      * Fetch the element to display the tooltip on.
4698      * @return {Roo.Element} defaults to this.el
4699      */
4700     tooltipEl : function()
4701     {
4702         return this.el.select('' + this.tagtype + '', true).first();
4703     },
4704     
4705     scrollToElement : function(e)
4706     {
4707         var c = document.body;
4708         
4709         /*
4710          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4711          */
4712         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4713             c = document.documentElement;
4714         }
4715         
4716         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4717         
4718         if(!target){
4719             return;
4720         }
4721
4722         var o = target.calcOffsetsTo(c);
4723         
4724         var options = {
4725             target : target,
4726             value : o[1]
4727         };
4728         
4729         this.fireEvent('scrollto', this, options, e);
4730         
4731         Roo.get(c).scrollTo('top', options.value, true);
4732         
4733         return;
4734     }
4735 });
4736  
4737
4738  /*
4739  * - LGPL
4740  *
4741  * sidebar item
4742  *
4743  *  li
4744  *    <span> icon </span>
4745  *    <span> text </span>
4746  *    <span>badge </span>
4747  */
4748
4749 /**
4750  * @class Roo.bootstrap.NavSidebarItem
4751  * @extends Roo.bootstrap.NavItem
4752  * Bootstrap Navbar.NavSidebarItem class
4753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4754  * {Boolean} open is the menu open
4755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4757  * {String} buttonSize (sm|md|lg)the extra classes for the button
4758  * {Boolean} showArrow show arrow next to the text (default true)
4759  * @constructor
4760  * Create a new Navbar Button
4761  * @param {Object} config The config object
4762  */
4763 Roo.bootstrap.NavSidebarItem = function(config){
4764     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4765     this.addEvents({
4766         // raw events
4767         /**
4768          * @event click
4769          * The raw click event for the entire grid.
4770          * @param {Roo.EventObject} e
4771          */
4772         "click" : true,
4773          /**
4774             * @event changed
4775             * Fires when the active item active state changes
4776             * @param {Roo.bootstrap.NavSidebarItem} this
4777             * @param {boolean} state the new state
4778              
4779          */
4780         'changed': true
4781     });
4782    
4783 };
4784
4785 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4786     
4787     badgeWeight : 'default',
4788     
4789     open: false,
4790     
4791     buttonView : false,
4792     
4793     buttonWeight : 'default',
4794     
4795     buttonSize : 'md',
4796     
4797     showArrow : true,
4798     
4799     getAutoCreate : function(){
4800         
4801         
4802         var a = {
4803                 tag: 'a',
4804                 href : this.href || '#',
4805                 cls: '',
4806                 html : '',
4807                 cn : []
4808         };
4809         
4810         if(this.buttonView){
4811             a = {
4812                 tag: 'button',
4813                 href : this.href || '#',
4814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4815                 html : this.html,
4816                 cn : []
4817             };
4818         }
4819         
4820         var cfg = {
4821             tag: 'li',
4822             cls: '',
4823             cn: [ a ]
4824         };
4825         
4826         if (this.active) {
4827             cfg.cls += ' active';
4828         }
4829         
4830         if (this.disabled) {
4831             cfg.cls += ' disabled';
4832         }
4833         if (this.open) {
4834             cfg.cls += ' open x-open';
4835         }
4836         // left icon..
4837         if (this.glyphicon || this.icon) {
4838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4839             a.cn.push({ tag : 'i', cls : c }) ;
4840         }
4841         
4842         if(!this.buttonView){
4843             var span = {
4844                 tag: 'span',
4845                 html : this.html || ''
4846             };
4847
4848             a.cn.push(span);
4849             
4850         }
4851         
4852         if (this.badge !== '') {
4853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4854         }
4855         
4856         if (this.menu) {
4857             
4858             if(this.showArrow){
4859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4860             }
4861             
4862             a.cls += ' dropdown-toggle treeview' ;
4863         }
4864         
4865         return cfg;
4866     },
4867     
4868     initEvents : function()
4869     { 
4870         if (typeof (this.menu) != 'undefined') {
4871             this.menu.parentType = this.xtype;
4872             this.menu.triggerEl = this.el;
4873             this.menu = this.addxtype(Roo.apply({}, this.menu));
4874         }
4875         
4876         this.el.on('click', this.onClick, this);
4877         
4878         if(this.badge !== ''){
4879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4880         }
4881         
4882     },
4883     
4884     onClick : function(e)
4885     {
4886         if(this.disabled){
4887             e.preventDefault();
4888             return;
4889         }
4890         
4891         if(this.preventDefault){
4892             e.preventDefault();
4893         }
4894         
4895         this.fireEvent('click', this);
4896     },
4897     
4898     disable : function()
4899     {
4900         this.setDisabled(true);
4901     },
4902     
4903     enable : function()
4904     {
4905         this.setDisabled(false);
4906     },
4907     
4908     setDisabled : function(state)
4909     {
4910         if(this.disabled == state){
4911             return;
4912         }
4913         
4914         this.disabled = state;
4915         
4916         if (state) {
4917             this.el.addClass('disabled');
4918             return;
4919         }
4920         
4921         this.el.removeClass('disabled');
4922         
4923         return;
4924     },
4925     
4926     setActive : function(state)
4927     {
4928         if(this.active == state){
4929             return;
4930         }
4931         
4932         this.active = state;
4933         
4934         if (state) {
4935             this.el.addClass('active');
4936             return;
4937         }
4938         
4939         this.el.removeClass('active');
4940         
4941         return;
4942     },
4943     
4944     isActive: function () 
4945     {
4946         return this.active;
4947     },
4948     
4949     setBadge : function(str)
4950     {
4951         if(!this.badgeEl){
4952             return;
4953         }
4954         
4955         this.badgeEl.dom.innerHTML = str;
4956     }
4957     
4958    
4959      
4960  
4961 });
4962  
4963
4964  /*
4965  * - LGPL
4966  *
4967  * row
4968  * 
4969  */
4970
4971 /**
4972  * @class Roo.bootstrap.Row
4973  * @extends Roo.bootstrap.Component
4974  * Bootstrap Row class (contains columns...)
4975  * 
4976  * @constructor
4977  * Create a new Row
4978  * @param {Object} config The config object
4979  */
4980
4981 Roo.bootstrap.Row = function(config){
4982     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4983 };
4984
4985 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4986     
4987     getAutoCreate : function(){
4988        return {
4989             cls: 'row clearfix'
4990        };
4991     }
4992     
4993     
4994 });
4995
4996  
4997
4998  /*
4999  * - LGPL
5000  *
5001  * element
5002  * 
5003  */
5004
5005 /**
5006  * @class Roo.bootstrap.Element
5007  * @extends Roo.bootstrap.Component
5008  * Bootstrap Element class
5009  * @cfg {String} html contents of the element
5010  * @cfg {String} tag tag of the element
5011  * @cfg {String} cls class of the element
5012  * @cfg {Boolean} preventDefault (true|false) default false
5013  * @cfg {Boolean} clickable (true|false) default false
5014  * 
5015  * @constructor
5016  * Create a new Element
5017  * @param {Object} config The config object
5018  */
5019
5020 Roo.bootstrap.Element = function(config){
5021     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5022     
5023     this.addEvents({
5024         // raw events
5025         /**
5026          * @event click
5027          * When a element is chick
5028          * @param {Roo.bootstrap.Element} this
5029          * @param {Roo.EventObject} e
5030          */
5031         "click" : true
5032     });
5033 };
5034
5035 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5036     
5037     tag: 'div',
5038     cls: '',
5039     html: '',
5040     preventDefault: false, 
5041     clickable: false,
5042     
5043     getAutoCreate : function(){
5044         
5045         var cfg = {
5046             tag: this.tag,
5047             // cls: this.cls, double assign in parent class Component.js :: onRender
5048             html: this.html
5049         };
5050         
5051         return cfg;
5052     },
5053     
5054     initEvents: function() 
5055     {
5056         Roo.bootstrap.Element.superclass.initEvents.call(this);
5057         
5058         if(this.clickable){
5059             this.el.on('click', this.onClick, this);
5060         }
5061         
5062     },
5063     
5064     onClick : function(e)
5065     {
5066         if(this.preventDefault){
5067             e.preventDefault();
5068         }
5069         
5070         this.fireEvent('click', this, e);
5071     },
5072     
5073     getValue : function()
5074     {
5075         return this.el.dom.innerHTML;
5076     },
5077     
5078     setValue : function(value)
5079     {
5080         this.el.dom.innerHTML = value;
5081     }
5082    
5083 });
5084
5085  
5086
5087  /*
5088  * - LGPL
5089  *
5090  * pagination
5091  * 
5092  */
5093
5094 /**
5095  * @class Roo.bootstrap.Pagination
5096  * @extends Roo.bootstrap.Component
5097  * Bootstrap Pagination class
5098  * @cfg {String} size xs | sm | md | lg
5099  * @cfg {Boolean} inverse false | true
5100  * 
5101  * @constructor
5102  * Create a new Pagination
5103  * @param {Object} config The config object
5104  */
5105
5106 Roo.bootstrap.Pagination = function(config){
5107     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5108 };
5109
5110 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5111     
5112     cls: false,
5113     size: false,
5114     inverse: false,
5115     
5116     getAutoCreate : function(){
5117         var cfg = {
5118             tag: 'ul',
5119                 cls: 'pagination'
5120         };
5121         if (this.inverse) {
5122             cfg.cls += ' inverse';
5123         }
5124         if (this.html) {
5125             cfg.html=this.html;
5126         }
5127         if (this.cls) {
5128             cfg.cls += " " + this.cls;
5129         }
5130         return cfg;
5131     }
5132    
5133 });
5134
5135  
5136
5137  /*
5138  * - LGPL
5139  *
5140  * Pagination item
5141  * 
5142  */
5143
5144
5145 /**
5146  * @class Roo.bootstrap.PaginationItem
5147  * @extends Roo.bootstrap.Component
5148  * Bootstrap PaginationItem class
5149  * @cfg {String} html text
5150  * @cfg {String} href the link
5151  * @cfg {Boolean} preventDefault (true | false) default true
5152  * @cfg {Boolean} active (true | false) default false
5153  * @cfg {Boolean} disabled default false
5154  * 
5155  * 
5156  * @constructor
5157  * Create a new PaginationItem
5158  * @param {Object} config The config object
5159  */
5160
5161
5162 Roo.bootstrap.PaginationItem = function(config){
5163     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5164     this.addEvents({
5165         // raw events
5166         /**
5167          * @event click
5168          * The raw click event for the entire grid.
5169          * @param {Roo.EventObject} e
5170          */
5171         "click" : true
5172     });
5173 };
5174
5175 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5176     
5177     href : false,
5178     html : false,
5179     preventDefault: true,
5180     active : false,
5181     cls : false,
5182     disabled: false,
5183     
5184     getAutoCreate : function(){
5185         var cfg= {
5186             tag: 'li',
5187             cn: [
5188                 {
5189                     tag : 'a',
5190                     href : this.href ? this.href : '#',
5191                     html : this.html ? this.html : ''
5192                 }
5193             ]
5194         };
5195         
5196         if(this.cls){
5197             cfg.cls = this.cls;
5198         }
5199         
5200         if(this.disabled){
5201             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5202         }
5203         
5204         if(this.active){
5205             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5206         }
5207         
5208         return cfg;
5209     },
5210     
5211     initEvents: function() {
5212         
5213         this.el.on('click', this.onClick, this);
5214         
5215     },
5216     onClick : function(e)
5217     {
5218         Roo.log('PaginationItem on click ');
5219         if(this.preventDefault){
5220             e.preventDefault();
5221         }
5222         
5223         if(this.disabled){
5224             return;
5225         }
5226         
5227         this.fireEvent('click', this, e);
5228     }
5229    
5230 });
5231
5232  
5233
5234  /*
5235  * - LGPL
5236  *
5237  * slider
5238  * 
5239  */
5240
5241
5242 /**
5243  * @class Roo.bootstrap.Slider
5244  * @extends Roo.bootstrap.Component
5245  * Bootstrap Slider class
5246  *    
5247  * @constructor
5248  * Create a new Slider
5249  * @param {Object} config The config object
5250  */
5251
5252 Roo.bootstrap.Slider = function(config){
5253     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5254 };
5255
5256 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5257     
5258     getAutoCreate : function(){
5259         
5260         var cfg = {
5261             tag: 'div',
5262             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5263             cn: [
5264                 {
5265                     tag: 'a',
5266                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5267                 }
5268             ]
5269         };
5270         
5271         return cfg;
5272     }
5273    
5274 });
5275
5276  /*
5277  * Based on:
5278  * Ext JS Library 1.1.1
5279  * Copyright(c) 2006-2007, Ext JS, LLC.
5280  *
5281  * Originally Released Under LGPL - original licence link has changed is not relivant.
5282  *
5283  * Fork - LGPL
5284  * <script type="text/javascript">
5285  */
5286  
5287
5288 /**
5289  * @class Roo.grid.ColumnModel
5290  * @extends Roo.util.Observable
5291  * This is the default implementation of a ColumnModel used by the Grid. It defines
5292  * the columns in the grid.
5293  * <br>Usage:<br>
5294  <pre><code>
5295  var colModel = new Roo.grid.ColumnModel([
5296         {header: "Ticker", width: 60, sortable: true, locked: true},
5297         {header: "Company Name", width: 150, sortable: true},
5298         {header: "Market Cap.", width: 100, sortable: true},
5299         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5300         {header: "Employees", width: 100, sortable: true, resizable: false}
5301  ]);
5302  </code></pre>
5303  * <p>
5304  
5305  * The config options listed for this class are options which may appear in each
5306  * individual column definition.
5307  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5308  * @constructor
5309  * @param {Object} config An Array of column config objects. See this class's
5310  * config objects for details.
5311 */
5312 Roo.grid.ColumnModel = function(config){
5313         /**
5314      * The config passed into the constructor
5315      */
5316     this.config = config;
5317     this.lookup = {};
5318
5319     // if no id, create one
5320     // if the column does not have a dataIndex mapping,
5321     // map it to the order it is in the config
5322     for(var i = 0, len = config.length; i < len; i++){
5323         var c = config[i];
5324         if(typeof c.dataIndex == "undefined"){
5325             c.dataIndex = i;
5326         }
5327         if(typeof c.renderer == "string"){
5328             c.renderer = Roo.util.Format[c.renderer];
5329         }
5330         if(typeof c.id == "undefined"){
5331             c.id = Roo.id();
5332         }
5333         if(c.editor && c.editor.xtype){
5334             c.editor  = Roo.factory(c.editor, Roo.grid);
5335         }
5336         if(c.editor && c.editor.isFormField){
5337             c.editor = new Roo.grid.GridEditor(c.editor);
5338         }
5339         this.lookup[c.id] = c;
5340     }
5341
5342     /**
5343      * The width of columns which have no width specified (defaults to 100)
5344      * @type Number
5345      */
5346     this.defaultWidth = 100;
5347
5348     /**
5349      * Default sortable of columns which have no sortable specified (defaults to false)
5350      * @type Boolean
5351      */
5352     this.defaultSortable = false;
5353
5354     this.addEvents({
5355         /**
5356              * @event widthchange
5357              * Fires when the width of a column changes.
5358              * @param {ColumnModel} this
5359              * @param {Number} columnIndex The column index
5360              * @param {Number} newWidth The new width
5361              */
5362             "widthchange": true,
5363         /**
5364              * @event headerchange
5365              * Fires when the text of a header changes.
5366              * @param {ColumnModel} this
5367              * @param {Number} columnIndex The column index
5368              * @param {Number} newText The new header text
5369              */
5370             "headerchange": true,
5371         /**
5372              * @event hiddenchange
5373              * Fires when a column is hidden or "unhidden".
5374              * @param {ColumnModel} this
5375              * @param {Number} columnIndex The column index
5376              * @param {Boolean} hidden true if hidden, false otherwise
5377              */
5378             "hiddenchange": true,
5379             /**
5380          * @event columnmoved
5381          * Fires when a column is moved.
5382          * @param {ColumnModel} this
5383          * @param {Number} oldIndex
5384          * @param {Number} newIndex
5385          */
5386         "columnmoved" : true,
5387         /**
5388          * @event columlockchange
5389          * Fires when a column's locked state is changed
5390          * @param {ColumnModel} this
5391          * @param {Number} colIndex
5392          * @param {Boolean} locked true if locked
5393          */
5394         "columnlockchange" : true
5395     });
5396     Roo.grid.ColumnModel.superclass.constructor.call(this);
5397 };
5398 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5399     /**
5400      * @cfg {String} header The header text to display in the Grid view.
5401      */
5402     /**
5403      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5404      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5405      * specified, the column's index is used as an index into the Record's data Array.
5406      */
5407     /**
5408      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5409      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5410      */
5411     /**
5412      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5413      * Defaults to the value of the {@link #defaultSortable} property.
5414      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5415      */
5416     /**
5417      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5418      */
5419     /**
5420      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5421      */
5422     /**
5423      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5424      */
5425     /**
5426      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5427      */
5428     /**
5429      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5430      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5431      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5432      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5433      */
5434        /**
5435      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5436      */
5437     /**
5438      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5439      */
5440     /**
5441      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5442      */
5443     /**
5444      * @cfg {String} cursor (Optional)
5445      */
5446     /**
5447      * @cfg {String} tooltip (Optional)
5448      */
5449     /**
5450      * @cfg {Number} xs (Optional)
5451      */
5452     /**
5453      * @cfg {Number} sm (Optional)
5454      */
5455     /**
5456      * @cfg {Number} md (Optional)
5457      */
5458     /**
5459      * @cfg {Number} lg (Optional)
5460      */
5461     /**
5462      * Returns the id of the column at the specified index.
5463      * @param {Number} index The column index
5464      * @return {String} the id
5465      */
5466     getColumnId : function(index){
5467         return this.config[index].id;
5468     },
5469
5470     /**
5471      * Returns the column for a specified id.
5472      * @param {String} id The column id
5473      * @return {Object} the column
5474      */
5475     getColumnById : function(id){
5476         return this.lookup[id];
5477     },
5478
5479     
5480     /**
5481      * Returns the column for a specified dataIndex.
5482      * @param {String} dataIndex The column dataIndex
5483      * @return {Object|Boolean} the column or false if not found
5484      */
5485     getColumnByDataIndex: function(dataIndex){
5486         var index = this.findColumnIndex(dataIndex);
5487         return index > -1 ? this.config[index] : false;
5488     },
5489     
5490     /**
5491      * Returns the index for a specified column id.
5492      * @param {String} id The column id
5493      * @return {Number} the index, or -1 if not found
5494      */
5495     getIndexById : function(id){
5496         for(var i = 0, len = this.config.length; i < len; i++){
5497             if(this.config[i].id == id){
5498                 return i;
5499             }
5500         }
5501         return -1;
5502     },
5503     
5504     /**
5505      * Returns the index for a specified column dataIndex.
5506      * @param {String} dataIndex The column dataIndex
5507      * @return {Number} the index, or -1 if not found
5508      */
5509     
5510     findColumnIndex : function(dataIndex){
5511         for(var i = 0, len = this.config.length; i < len; i++){
5512             if(this.config[i].dataIndex == dataIndex){
5513                 return i;
5514             }
5515         }
5516         return -1;
5517     },
5518     
5519     
5520     moveColumn : function(oldIndex, newIndex){
5521         var c = this.config[oldIndex];
5522         this.config.splice(oldIndex, 1);
5523         this.config.splice(newIndex, 0, c);
5524         this.dataMap = null;
5525         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5526     },
5527
5528     isLocked : function(colIndex){
5529         return this.config[colIndex].locked === true;
5530     },
5531
5532     setLocked : function(colIndex, value, suppressEvent){
5533         if(this.isLocked(colIndex) == value){
5534             return;
5535         }
5536         this.config[colIndex].locked = value;
5537         if(!suppressEvent){
5538             this.fireEvent("columnlockchange", this, colIndex, value);
5539         }
5540     },
5541
5542     getTotalLockedWidth : function(){
5543         var totalWidth = 0;
5544         for(var i = 0; i < this.config.length; i++){
5545             if(this.isLocked(i) && !this.isHidden(i)){
5546                 this.totalWidth += this.getColumnWidth(i);
5547             }
5548         }
5549         return totalWidth;
5550     },
5551
5552     getLockedCount : function(){
5553         for(var i = 0, len = this.config.length; i < len; i++){
5554             if(!this.isLocked(i)){
5555                 return i;
5556             }
5557         }
5558         
5559         return this.config.length;
5560     },
5561
5562     /**
5563      * Returns the number of columns.
5564      * @return {Number}
5565      */
5566     getColumnCount : function(visibleOnly){
5567         if(visibleOnly === true){
5568             var c = 0;
5569             for(var i = 0, len = this.config.length; i < len; i++){
5570                 if(!this.isHidden(i)){
5571                     c++;
5572                 }
5573             }
5574             return c;
5575         }
5576         return this.config.length;
5577     },
5578
5579     /**
5580      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5581      * @param {Function} fn
5582      * @param {Object} scope (optional)
5583      * @return {Array} result
5584      */
5585     getColumnsBy : function(fn, scope){
5586         var r = [];
5587         for(var i = 0, len = this.config.length; i < len; i++){
5588             var c = this.config[i];
5589             if(fn.call(scope||this, c, i) === true){
5590                 r[r.length] = c;
5591             }
5592         }
5593         return r;
5594     },
5595
5596     /**
5597      * Returns true if the specified column is sortable.
5598      * @param {Number} col The column index
5599      * @return {Boolean}
5600      */
5601     isSortable : function(col){
5602         if(typeof this.config[col].sortable == "undefined"){
5603             return this.defaultSortable;
5604         }
5605         return this.config[col].sortable;
5606     },
5607
5608     /**
5609      * Returns the rendering (formatting) function defined for the column.
5610      * @param {Number} col The column index.
5611      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5612      */
5613     getRenderer : function(col){
5614         if(!this.config[col].renderer){
5615             return Roo.grid.ColumnModel.defaultRenderer;
5616         }
5617         return this.config[col].renderer;
5618     },
5619
5620     /**
5621      * Sets the rendering (formatting) function for a column.
5622      * @param {Number} col The column index
5623      * @param {Function} fn The function to use to process the cell's raw data
5624      * to return HTML markup for the grid view. The render function is called with
5625      * the following parameters:<ul>
5626      * <li>Data value.</li>
5627      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5628      * <li>css A CSS style string to apply to the table cell.</li>
5629      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5630      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5631      * <li>Row index</li>
5632      * <li>Column index</li>
5633      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5634      */
5635     setRenderer : function(col, fn){
5636         this.config[col].renderer = fn;
5637     },
5638
5639     /**
5640      * Returns the width for the specified column.
5641      * @param {Number} col The column index
5642      * @return {Number}
5643      */
5644     getColumnWidth : function(col){
5645         return this.config[col].width * 1 || this.defaultWidth;
5646     },
5647
5648     /**
5649      * Sets the width for a column.
5650      * @param {Number} col The column index
5651      * @param {Number} width The new width
5652      */
5653     setColumnWidth : function(col, width, suppressEvent){
5654         this.config[col].width = width;
5655         this.totalWidth = null;
5656         if(!suppressEvent){
5657              this.fireEvent("widthchange", this, col, width);
5658         }
5659     },
5660
5661     /**
5662      * Returns the total width of all columns.
5663      * @param {Boolean} includeHidden True to include hidden column widths
5664      * @return {Number}
5665      */
5666     getTotalWidth : function(includeHidden){
5667         if(!this.totalWidth){
5668             this.totalWidth = 0;
5669             for(var i = 0, len = this.config.length; i < len; i++){
5670                 if(includeHidden || !this.isHidden(i)){
5671                     this.totalWidth += this.getColumnWidth(i);
5672                 }
5673             }
5674         }
5675         return this.totalWidth;
5676     },
5677
5678     /**
5679      * Returns the header for the specified column.
5680      * @param {Number} col The column index
5681      * @return {String}
5682      */
5683     getColumnHeader : function(col){
5684         return this.config[col].header;
5685     },
5686
5687     /**
5688      * Sets the header for a column.
5689      * @param {Number} col The column index
5690      * @param {String} header The new header
5691      */
5692     setColumnHeader : function(col, header){
5693         this.config[col].header = header;
5694         this.fireEvent("headerchange", this, col, header);
5695     },
5696
5697     /**
5698      * Returns the tooltip for the specified column.
5699      * @param {Number} col The column index
5700      * @return {String}
5701      */
5702     getColumnTooltip : function(col){
5703             return this.config[col].tooltip;
5704     },
5705     /**
5706      * Sets the tooltip for a column.
5707      * @param {Number} col The column index
5708      * @param {String} tooltip The new tooltip
5709      */
5710     setColumnTooltip : function(col, tooltip){
5711             this.config[col].tooltip = tooltip;
5712     },
5713
5714     /**
5715      * Returns the dataIndex for the specified column.
5716      * @param {Number} col The column index
5717      * @return {Number}
5718      */
5719     getDataIndex : function(col){
5720         return this.config[col].dataIndex;
5721     },
5722
5723     /**
5724      * Sets the dataIndex for a column.
5725      * @param {Number} col The column index
5726      * @param {Number} dataIndex The new dataIndex
5727      */
5728     setDataIndex : function(col, dataIndex){
5729         this.config[col].dataIndex = dataIndex;
5730     },
5731
5732     
5733     
5734     /**
5735      * Returns true if the cell is editable.
5736      * @param {Number} colIndex The column index
5737      * @param {Number} rowIndex The row index - this is nto actually used..?
5738      * @return {Boolean}
5739      */
5740     isCellEditable : function(colIndex, rowIndex){
5741         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5742     },
5743
5744     /**
5745      * Returns the editor defined for the cell/column.
5746      * return false or null to disable editing.
5747      * @param {Number} colIndex The column index
5748      * @param {Number} rowIndex The row index
5749      * @return {Object}
5750      */
5751     getCellEditor : function(colIndex, rowIndex){
5752         return this.config[colIndex].editor;
5753     },
5754
5755     /**
5756      * Sets if a column is editable.
5757      * @param {Number} col The column index
5758      * @param {Boolean} editable True if the column is editable
5759      */
5760     setEditable : function(col, editable){
5761         this.config[col].editable = editable;
5762     },
5763
5764
5765     /**
5766      * Returns true if the column is hidden.
5767      * @param {Number} colIndex The column index
5768      * @return {Boolean}
5769      */
5770     isHidden : function(colIndex){
5771         return this.config[colIndex].hidden;
5772     },
5773
5774
5775     /**
5776      * Returns true if the column width cannot be changed
5777      */
5778     isFixed : function(colIndex){
5779         return this.config[colIndex].fixed;
5780     },
5781
5782     /**
5783      * Returns true if the column can be resized
5784      * @return {Boolean}
5785      */
5786     isResizable : function(colIndex){
5787         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5788     },
5789     /**
5790      * Sets if a column is hidden.
5791      * @param {Number} colIndex The column index
5792      * @param {Boolean} hidden True if the column is hidden
5793      */
5794     setHidden : function(colIndex, hidden){
5795         this.config[colIndex].hidden = hidden;
5796         this.totalWidth = null;
5797         this.fireEvent("hiddenchange", this, colIndex, hidden);
5798     },
5799
5800     /**
5801      * Sets the editor for a column.
5802      * @param {Number} col The column index
5803      * @param {Object} editor The editor object
5804      */
5805     setEditor : function(col, editor){
5806         this.config[col].editor = editor;
5807     }
5808 });
5809
5810 Roo.grid.ColumnModel.defaultRenderer = function(value)
5811 {
5812     if(typeof value == "object") {
5813         return value;
5814     }
5815         if(typeof value == "string" && value.length < 1){
5816             return "&#160;";
5817         }
5818     
5819         return String.format("{0}", value);
5820 };
5821
5822 // Alias for backwards compatibility
5823 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5824 /*
5825  * Based on:
5826  * Ext JS Library 1.1.1
5827  * Copyright(c) 2006-2007, Ext JS, LLC.
5828  *
5829  * Originally Released Under LGPL - original licence link has changed is not relivant.
5830  *
5831  * Fork - LGPL
5832  * <script type="text/javascript">
5833  */
5834  
5835 /**
5836  * @class Roo.LoadMask
5837  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5838  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5839  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5840  * element's UpdateManager load indicator and will be destroyed after the initial load.
5841  * @constructor
5842  * Create a new LoadMask
5843  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5844  * @param {Object} config The config object
5845  */
5846 Roo.LoadMask = function(el, config){
5847     this.el = Roo.get(el);
5848     Roo.apply(this, config);
5849     if(this.store){
5850         this.store.on('beforeload', this.onBeforeLoad, this);
5851         this.store.on('load', this.onLoad, this);
5852         this.store.on('loadexception', this.onLoadException, this);
5853         this.removeMask = false;
5854     }else{
5855         var um = this.el.getUpdateManager();
5856         um.showLoadIndicator = false; // disable the default indicator
5857         um.on('beforeupdate', this.onBeforeLoad, this);
5858         um.on('update', this.onLoad, this);
5859         um.on('failure', this.onLoad, this);
5860         this.removeMask = true;
5861     }
5862 };
5863
5864 Roo.LoadMask.prototype = {
5865     /**
5866      * @cfg {Boolean} removeMask
5867      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5868      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5869      */
5870     /**
5871      * @cfg {String} msg
5872      * The text to display in a centered loading message box (defaults to 'Loading...')
5873      */
5874     msg : 'Loading...',
5875     /**
5876      * @cfg {String} msgCls
5877      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5878      */
5879     msgCls : 'x-mask-loading',
5880
5881     /**
5882      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5883      * @type Boolean
5884      */
5885     disabled: false,
5886
5887     /**
5888      * Disables the mask to prevent it from being displayed
5889      */
5890     disable : function(){
5891        this.disabled = true;
5892     },
5893
5894     /**
5895      * Enables the mask so that it can be displayed
5896      */
5897     enable : function(){
5898         this.disabled = false;
5899     },
5900     
5901     onLoadException : function()
5902     {
5903         Roo.log(arguments);
5904         
5905         if (typeof(arguments[3]) != 'undefined') {
5906             Roo.MessageBox.alert("Error loading",arguments[3]);
5907         } 
5908         /*
5909         try {
5910             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5911                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5912             }   
5913         } catch(e) {
5914             
5915         }
5916         */
5917     
5918         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5919     },
5920     // private
5921     onLoad : function()
5922     {
5923         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5924     },
5925
5926     // private
5927     onBeforeLoad : function(){
5928         if(!this.disabled){
5929             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5930         }
5931     },
5932
5933     // private
5934     destroy : function(){
5935         if(this.store){
5936             this.store.un('beforeload', this.onBeforeLoad, this);
5937             this.store.un('load', this.onLoad, this);
5938             this.store.un('loadexception', this.onLoadException, this);
5939         }else{
5940             var um = this.el.getUpdateManager();
5941             um.un('beforeupdate', this.onBeforeLoad, this);
5942             um.un('update', this.onLoad, this);
5943             um.un('failure', this.onLoad, this);
5944         }
5945     }
5946 };/*
5947  * - LGPL
5948  *
5949  * table
5950  * 
5951  */
5952
5953 /**
5954  * @class Roo.bootstrap.Table
5955  * @extends Roo.bootstrap.Component
5956  * Bootstrap Table class
5957  * @cfg {String} cls table class
5958  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5959  * @cfg {String} bgcolor Specifies the background color for a table
5960  * @cfg {Number} border Specifies whether the table cells should have borders or not
5961  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5962  * @cfg {Number} cellspacing Specifies the space between cells
5963  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5964  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5965  * @cfg {String} sortable Specifies that the table should be sortable
5966  * @cfg {String} summary Specifies a summary of the content of a table
5967  * @cfg {Number} width Specifies the width of a table
5968  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5969  * 
5970  * @cfg {boolean} striped Should the rows be alternative striped
5971  * @cfg {boolean} bordered Add borders to the table
5972  * @cfg {boolean} hover Add hover highlighting
5973  * @cfg {boolean} condensed Format condensed
5974  * @cfg {boolean} responsive Format condensed
5975  * @cfg {Boolean} loadMask (true|false) default false
5976  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5977  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5978  * @cfg {Boolean} rowSelection (true|false) default false
5979  * @cfg {Boolean} cellSelection (true|false) default false
5980  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5981  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5982  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5983  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5984  
5985  * 
5986  * @constructor
5987  * Create a new Table
5988  * @param {Object} config The config object
5989  */
5990
5991 Roo.bootstrap.Table = function(config){
5992     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5993     
5994   
5995     
5996     // BC...
5997     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5998     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5999     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6000     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6001     
6002     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6003     if (this.sm) {
6004         this.sm.grid = this;
6005         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6006         this.sm = this.selModel;
6007         this.sm.xmodule = this.xmodule || false;
6008     }
6009     
6010     if (this.cm && typeof(this.cm.config) == 'undefined') {
6011         this.colModel = new Roo.grid.ColumnModel(this.cm);
6012         this.cm = this.colModel;
6013         this.cm.xmodule = this.xmodule || false;
6014     }
6015     if (this.store) {
6016         this.store= Roo.factory(this.store, Roo.data);
6017         this.ds = this.store;
6018         this.ds.xmodule = this.xmodule || false;
6019          
6020     }
6021     if (this.footer && this.store) {
6022         this.footer.dataSource = this.ds;
6023         this.footer = Roo.factory(this.footer);
6024     }
6025     
6026     /** @private */
6027     this.addEvents({
6028         /**
6029          * @event cellclick
6030          * Fires when a cell is clicked
6031          * @param {Roo.bootstrap.Table} this
6032          * @param {Roo.Element} el
6033          * @param {Number} rowIndex
6034          * @param {Number} columnIndex
6035          * @param {Roo.EventObject} e
6036          */
6037         "cellclick" : true,
6038         /**
6039          * @event celldblclick
6040          * Fires when a cell is double clicked
6041          * @param {Roo.bootstrap.Table} this
6042          * @param {Roo.Element} el
6043          * @param {Number} rowIndex
6044          * @param {Number} columnIndex
6045          * @param {Roo.EventObject} e
6046          */
6047         "celldblclick" : true,
6048         /**
6049          * @event rowclick
6050          * Fires when a row is clicked
6051          * @param {Roo.bootstrap.Table} this
6052          * @param {Roo.Element} el
6053          * @param {Number} rowIndex
6054          * @param {Roo.EventObject} e
6055          */
6056         "rowclick" : true,
6057         /**
6058          * @event rowdblclick
6059          * Fires when a row is double clicked
6060          * @param {Roo.bootstrap.Table} this
6061          * @param {Roo.Element} el
6062          * @param {Number} rowIndex
6063          * @param {Roo.EventObject} e
6064          */
6065         "rowdblclick" : true,
6066         /**
6067          * @event mouseover
6068          * Fires when a mouseover occur
6069          * @param {Roo.bootstrap.Table} this
6070          * @param {Roo.Element} el
6071          * @param {Number} rowIndex
6072          * @param {Number} columnIndex
6073          * @param {Roo.EventObject} e
6074          */
6075         "mouseover" : true,
6076         /**
6077          * @event mouseout
6078          * Fires when a mouseout occur
6079          * @param {Roo.bootstrap.Table} this
6080          * @param {Roo.Element} el
6081          * @param {Number} rowIndex
6082          * @param {Number} columnIndex
6083          * @param {Roo.EventObject} e
6084          */
6085         "mouseout" : true,
6086         /**
6087          * @event rowclass
6088          * Fires when a row is rendered, so you can change add a style to it.
6089          * @param {Roo.bootstrap.Table} this
6090          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6091          */
6092         'rowclass' : true,
6093           /**
6094          * @event rowsrendered
6095          * Fires when all the  rows have been rendered
6096          * @param {Roo.bootstrap.Table} this
6097          */
6098         'rowsrendered' : true,
6099         /**
6100          * @event contextmenu
6101          * The raw contextmenu event for the entire grid.
6102          * @param {Roo.EventObject} e
6103          */
6104         "contextmenu" : true,
6105         /**
6106          * @event rowcontextmenu
6107          * Fires when a row is right clicked
6108          * @param {Roo.bootstrap.Table} this
6109          * @param {Number} rowIndex
6110          * @param {Roo.EventObject} e
6111          */
6112         "rowcontextmenu" : true,
6113         /**
6114          * @event cellcontextmenu
6115          * Fires when a cell is right clicked
6116          * @param {Roo.bootstrap.Table} this
6117          * @param {Number} rowIndex
6118          * @param {Number} cellIndex
6119          * @param {Roo.EventObject} e
6120          */
6121          "cellcontextmenu" : true,
6122          /**
6123          * @event headercontextmenu
6124          * Fires when a header is right clicked
6125          * @param {Roo.bootstrap.Table} this
6126          * @param {Number} columnIndex
6127          * @param {Roo.EventObject} e
6128          */
6129         "headercontextmenu" : true
6130     });
6131 };
6132
6133 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6134     
6135     cls: false,
6136     align: false,
6137     bgcolor: false,
6138     border: false,
6139     cellpadding: false,
6140     cellspacing: false,
6141     frame: false,
6142     rules: false,
6143     sortable: false,
6144     summary: false,
6145     width: false,
6146     striped : false,
6147     scrollBody : false,
6148     bordered: false,
6149     hover:  false,
6150     condensed : false,
6151     responsive : false,
6152     sm : false,
6153     cm : false,
6154     store : false,
6155     loadMask : false,
6156     footerShow : true,
6157     headerShow : true,
6158   
6159     rowSelection : false,
6160     cellSelection : false,
6161     layout : false,
6162     
6163     // Roo.Element - the tbody
6164     mainBody: false,
6165     // Roo.Element - thead element
6166     mainHead: false,
6167     
6168     container: false, // used by gridpanel...
6169     
6170     lazyLoad : false,
6171     
6172     CSS : Roo.util.CSS,
6173     
6174     auto_hide_footer : false,
6175     
6176     getAutoCreate : function()
6177     {
6178         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6179         
6180         cfg = {
6181             tag: 'table',
6182             cls : 'table',
6183             cn : []
6184         };
6185         if (this.scrollBody) {
6186             cfg.cls += ' table-body-fixed';
6187         }    
6188         if (this.striped) {
6189             cfg.cls += ' table-striped';
6190         }
6191         
6192         if (this.hover) {
6193             cfg.cls += ' table-hover';
6194         }
6195         if (this.bordered) {
6196             cfg.cls += ' table-bordered';
6197         }
6198         if (this.condensed) {
6199             cfg.cls += ' table-condensed';
6200         }
6201         if (this.responsive) {
6202             cfg.cls += ' table-responsive';
6203         }
6204         
6205         if (this.cls) {
6206             cfg.cls+=  ' ' +this.cls;
6207         }
6208         
6209         // this lot should be simplifed...
6210         var _t = this;
6211         var cp = [
6212             'align',
6213             'bgcolor',
6214             'border',
6215             'cellpadding',
6216             'cellspacing',
6217             'frame',
6218             'rules',
6219             'sortable',
6220             'summary',
6221             'width'
6222         ].forEach(function(k) {
6223             if (_t[k]) {
6224                 cfg[k] = _t[k];
6225             }
6226         });
6227         
6228         
6229         if (this.layout) {
6230             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6231         }
6232         
6233         if(this.store || this.cm){
6234             if(this.headerShow){
6235                 cfg.cn.push(this.renderHeader());
6236             }
6237             
6238             cfg.cn.push(this.renderBody());
6239             
6240             if(this.footerShow){
6241                 cfg.cn.push(this.renderFooter());
6242             }
6243             // where does this come from?
6244             //cfg.cls+=  ' TableGrid';
6245         }
6246         
6247         return { cn : [ cfg ] };
6248     },
6249     
6250     initEvents : function()
6251     {   
6252         if(!this.store || !this.cm){
6253             return;
6254         }
6255         if (this.selModel) {
6256             this.selModel.initEvents();
6257         }
6258         
6259         
6260         //Roo.log('initEvents with ds!!!!');
6261         
6262         this.mainBody = this.el.select('tbody', true).first();
6263         this.mainHead = this.el.select('thead', true).first();
6264         this.mainFoot = this.el.select('tfoot', true).first();
6265         
6266         
6267         
6268         var _this = this;
6269         
6270         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6271             e.on('click', _this.sort, _this);
6272         });
6273         
6274         this.mainBody.on("click", this.onClick, this);
6275         this.mainBody.on("dblclick", this.onDblClick, this);
6276         
6277         // why is this done????? = it breaks dialogs??
6278         //this.parent().el.setStyle('position', 'relative');
6279         
6280         
6281         if (this.footer) {
6282             this.footer.parentId = this.id;
6283             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6284             
6285             if(this.lazyLoad){
6286                 this.el.select('tfoot tr td').first().addClass('hide');
6287             }
6288         } 
6289         
6290         if(this.loadMask) {
6291             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6292         }
6293         
6294         this.store.on('load', this.onLoad, this);
6295         this.store.on('beforeload', this.onBeforeLoad, this);
6296         this.store.on('update', this.onUpdate, this);
6297         this.store.on('add', this.onAdd, this);
6298         this.store.on("clear", this.clear, this);
6299         
6300         this.el.on("contextmenu", this.onContextMenu, this);
6301         
6302         this.mainBody.on('scroll', this.onBodyScroll, this);
6303         
6304         this.cm.on("headerchange", this.onHeaderChange, this);
6305         
6306         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6307         
6308     },
6309     
6310     onContextMenu : function(e, t)
6311     {
6312         this.processEvent("contextmenu", e);
6313     },
6314     
6315     processEvent : function(name, e)
6316     {
6317         if (name != 'touchstart' ) {
6318             this.fireEvent(name, e);    
6319         }
6320         
6321         var t = e.getTarget();
6322         
6323         var cell = Roo.get(t);
6324         
6325         if(!cell){
6326             return;
6327         }
6328         
6329         if(cell.findParent('tfoot', false, true)){
6330             return;
6331         }
6332         
6333         if(cell.findParent('thead', false, true)){
6334             
6335             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6336                 cell = Roo.get(t).findParent('th', false, true);
6337                 if (!cell) {
6338                     Roo.log("failed to find th in thead?");
6339                     Roo.log(e.getTarget());
6340                     return;
6341                 }
6342             }
6343             
6344             var cellIndex = cell.dom.cellIndex;
6345             
6346             var ename = name == 'touchstart' ? 'click' : name;
6347             this.fireEvent("header" + ename, this, cellIndex, e);
6348             
6349             return;
6350         }
6351         
6352         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6353             cell = Roo.get(t).findParent('td', false, true);
6354             if (!cell) {
6355                 Roo.log("failed to find th in tbody?");
6356                 Roo.log(e.getTarget());
6357                 return;
6358             }
6359         }
6360         
6361         var row = cell.findParent('tr', false, true);
6362         var cellIndex = cell.dom.cellIndex;
6363         var rowIndex = row.dom.rowIndex - 1;
6364         
6365         if(row !== false){
6366             
6367             this.fireEvent("row" + name, this, rowIndex, e);
6368             
6369             if(cell !== false){
6370             
6371                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6372             }
6373         }
6374         
6375     },
6376     
6377     onMouseover : function(e, el)
6378     {
6379         var cell = Roo.get(el);
6380         
6381         if(!cell){
6382             return;
6383         }
6384         
6385         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6386             cell = cell.findParent('td', false, true);
6387         }
6388         
6389         var row = cell.findParent('tr', false, true);
6390         var cellIndex = cell.dom.cellIndex;
6391         var rowIndex = row.dom.rowIndex - 1; // start from 0
6392         
6393         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6394         
6395     },
6396     
6397     onMouseout : function(e, el)
6398     {
6399         var cell = Roo.get(el);
6400         
6401         if(!cell){
6402             return;
6403         }
6404         
6405         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6406             cell = cell.findParent('td', false, true);
6407         }
6408         
6409         var row = cell.findParent('tr', false, true);
6410         var cellIndex = cell.dom.cellIndex;
6411         var rowIndex = row.dom.rowIndex - 1; // start from 0
6412         
6413         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6414         
6415     },
6416     
6417     onClick : function(e, el)
6418     {
6419         var cell = Roo.get(el);
6420         
6421         if(!cell || (!this.cellSelection && !this.rowSelection)){
6422             return;
6423         }
6424         
6425         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6426             cell = cell.findParent('td', false, true);
6427         }
6428         
6429         if(!cell || typeof(cell) == 'undefined'){
6430             return;
6431         }
6432         
6433         var row = cell.findParent('tr', false, true);
6434         
6435         if(!row || typeof(row) == 'undefined'){
6436             return;
6437         }
6438         
6439         var cellIndex = cell.dom.cellIndex;
6440         var rowIndex = this.getRowIndex(row);
6441         
6442         // why??? - should these not be based on SelectionModel?
6443         if(this.cellSelection){
6444             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6445         }
6446         
6447         if(this.rowSelection){
6448             this.fireEvent('rowclick', this, row, rowIndex, e);
6449         }
6450         
6451         
6452     },
6453         
6454     onDblClick : function(e,el)
6455     {
6456         var cell = Roo.get(el);
6457         
6458         if(!cell || (!this.cellSelection && !this.rowSelection)){
6459             return;
6460         }
6461         
6462         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6463             cell = cell.findParent('td', false, true);
6464         }
6465         
6466         if(!cell || typeof(cell) == 'undefined'){
6467             return;
6468         }
6469         
6470         var row = cell.findParent('tr', false, true);
6471         
6472         if(!row || typeof(row) == 'undefined'){
6473             return;
6474         }
6475         
6476         var cellIndex = cell.dom.cellIndex;
6477         var rowIndex = this.getRowIndex(row);
6478         
6479         if(this.cellSelection){
6480             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6481         }
6482         
6483         if(this.rowSelection){
6484             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6485         }
6486     },
6487     
6488     sort : function(e,el)
6489     {
6490         var col = Roo.get(el);
6491         
6492         if(!col.hasClass('sortable')){
6493             return;
6494         }
6495         
6496         var sort = col.attr('sort');
6497         var dir = 'ASC';
6498         
6499         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6500             dir = 'DESC';
6501         }
6502         
6503         this.store.sortInfo = {field : sort, direction : dir};
6504         
6505         if (this.footer) {
6506             Roo.log("calling footer first");
6507             this.footer.onClick('first');
6508         } else {
6509         
6510             this.store.load({ params : { start : 0 } });
6511         }
6512     },
6513     
6514     renderHeader : function()
6515     {
6516         var header = {
6517             tag: 'thead',
6518             cn : []
6519         };
6520         
6521         var cm = this.cm;
6522         this.totalWidth = 0;
6523         
6524         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6525             
6526             var config = cm.config[i];
6527             
6528             var c = {
6529                 tag: 'th',
6530                 cls : 'x-hcol-' + i,
6531                 style : '',
6532                 html: cm.getColumnHeader(i)
6533             };
6534             
6535             var hh = '';
6536             
6537             if(typeof(config.sortable) != 'undefined' && config.sortable){
6538                 c.cls = 'sortable';
6539                 c.html = '<i class="glyphicon"></i>' + c.html;
6540             }
6541             
6542             if(typeof(config.lgHeader) != 'undefined'){
6543                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6544             }
6545             
6546             if(typeof(config.mdHeader) != 'undefined'){
6547                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6548             }
6549             
6550             if(typeof(config.smHeader) != 'undefined'){
6551                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6552             }
6553             
6554             if(typeof(config.xsHeader) != 'undefined'){
6555                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6556             }
6557             
6558             if(hh.length){
6559                 c.html = hh;
6560             }
6561             
6562             if(typeof(config.tooltip) != 'undefined'){
6563                 c.tooltip = config.tooltip;
6564             }
6565             
6566             if(typeof(config.colspan) != 'undefined'){
6567                 c.colspan = config.colspan;
6568             }
6569             
6570             if(typeof(config.hidden) != 'undefined' && config.hidden){
6571                 c.style += ' display:none;';
6572             }
6573             
6574             if(typeof(config.dataIndex) != 'undefined'){
6575                 c.sort = config.dataIndex;
6576             }
6577             
6578            
6579             
6580             if(typeof(config.align) != 'undefined' && config.align.length){
6581                 c.style += ' text-align:' + config.align + ';';
6582             }
6583             
6584             if(typeof(config.width) != 'undefined'){
6585                 c.style += ' width:' + config.width + 'px;';
6586                 this.totalWidth += config.width;
6587             } else {
6588                 this.totalWidth += 100; // assume minimum of 100 per column?
6589             }
6590             
6591             if(typeof(config.cls) != 'undefined'){
6592                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6593             }
6594             
6595             ['xs','sm','md','lg'].map(function(size){
6596                 
6597                 if(typeof(config[size]) == 'undefined'){
6598                     return;
6599                 }
6600                 
6601                 if (!config[size]) { // 0 = hidden
6602                     c.cls += ' hidden-' + size;
6603                     return;
6604                 }
6605                 
6606                 c.cls += ' col-' + size + '-' + config[size];
6607
6608             });
6609             
6610             header.cn.push(c)
6611         }
6612         
6613         return header;
6614     },
6615     
6616     renderBody : function()
6617     {
6618         var body = {
6619             tag: 'tbody',
6620             cn : [
6621                 {
6622                     tag: 'tr',
6623                     cn : [
6624                         {
6625                             tag : 'td',
6626                             colspan :  this.cm.getColumnCount()
6627                         }
6628                     ]
6629                 }
6630             ]
6631         };
6632         
6633         return body;
6634     },
6635     
6636     renderFooter : function()
6637     {
6638         var footer = {
6639             tag: 'tfoot',
6640             cn : [
6641                 {
6642                     tag: 'tr',
6643                     cn : [
6644                         {
6645                             tag : 'td',
6646                             colspan :  this.cm.getColumnCount()
6647                         }
6648                     ]
6649                 }
6650             ]
6651         };
6652         
6653         return footer;
6654     },
6655     
6656     
6657     
6658     onLoad : function()
6659     {
6660 //        Roo.log('ds onload');
6661         this.clear();
6662         
6663         var _this = this;
6664         var cm = this.cm;
6665         var ds = this.store;
6666         
6667         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6668             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6669             if (_this.store.sortInfo) {
6670                     
6671                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6672                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6673                 }
6674                 
6675                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6676                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6677                 }
6678             }
6679         });
6680         
6681         var tbody =  this.mainBody;
6682               
6683         if(ds.getCount() > 0){
6684             ds.data.each(function(d,rowIndex){
6685                 var row =  this.renderRow(cm, ds, rowIndex);
6686                 
6687                 tbody.createChild(row);
6688                 
6689                 var _this = this;
6690                 
6691                 if(row.cellObjects.length){
6692                     Roo.each(row.cellObjects, function(r){
6693                         _this.renderCellObject(r);
6694                     })
6695                 }
6696                 
6697             }, this);
6698         }
6699         
6700         var tfoot = this.el.select('tfoot', true).first();
6701         
6702         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6703             
6704             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6705             
6706             var total = this.ds.getTotalCount();
6707             
6708             if(this.footer.pageSize < total){
6709                 this.mainFoot.show();
6710             }
6711         }
6712         
6713         Roo.each(this.el.select('tbody td', true).elements, function(e){
6714             e.on('mouseover', _this.onMouseover, _this);
6715         });
6716         
6717         Roo.each(this.el.select('tbody td', true).elements, function(e){
6718             e.on('mouseout', _this.onMouseout, _this);
6719         });
6720         this.fireEvent('rowsrendered', this);
6721         
6722         this.autoSize();
6723     },
6724     
6725     
6726     onUpdate : function(ds,record)
6727     {
6728         this.refreshRow(record);
6729         this.autoSize();
6730     },
6731     
6732     onRemove : function(ds, record, index, isUpdate){
6733         if(isUpdate !== true){
6734             this.fireEvent("beforerowremoved", this, index, record);
6735         }
6736         var bt = this.mainBody.dom;
6737         
6738         var rows = this.el.select('tbody > tr', true).elements;
6739         
6740         if(typeof(rows[index]) != 'undefined'){
6741             bt.removeChild(rows[index].dom);
6742         }
6743         
6744 //        if(bt.rows[index]){
6745 //            bt.removeChild(bt.rows[index]);
6746 //        }
6747         
6748         if(isUpdate !== true){
6749             //this.stripeRows(index);
6750             //this.syncRowHeights(index, index);
6751             //this.layout();
6752             this.fireEvent("rowremoved", this, index, record);
6753         }
6754     },
6755     
6756     onAdd : function(ds, records, rowIndex)
6757     {
6758         //Roo.log('on Add called');
6759         // - note this does not handle multiple adding very well..
6760         var bt = this.mainBody.dom;
6761         for (var i =0 ; i < records.length;i++) {
6762             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6763             //Roo.log(records[i]);
6764             //Roo.log(this.store.getAt(rowIndex+i));
6765             this.insertRow(this.store, rowIndex + i, false);
6766             return;
6767         }
6768         
6769     },
6770     
6771     
6772     refreshRow : function(record){
6773         var ds = this.store, index;
6774         if(typeof record == 'number'){
6775             index = record;
6776             record = ds.getAt(index);
6777         }else{
6778             index = ds.indexOf(record);
6779         }
6780         this.insertRow(ds, index, true);
6781         this.autoSize();
6782         this.onRemove(ds, record, index+1, true);
6783         this.autoSize();
6784         //this.syncRowHeights(index, index);
6785         //this.layout();
6786         this.fireEvent("rowupdated", this, index, record);
6787     },
6788     
6789     insertRow : function(dm, rowIndex, isUpdate){
6790         
6791         if(!isUpdate){
6792             this.fireEvent("beforerowsinserted", this, rowIndex);
6793         }
6794             //var s = this.getScrollState();
6795         var row = this.renderRow(this.cm, this.store, rowIndex);
6796         // insert before rowIndex..
6797         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6798         
6799         var _this = this;
6800                 
6801         if(row.cellObjects.length){
6802             Roo.each(row.cellObjects, function(r){
6803                 _this.renderCellObject(r);
6804             })
6805         }
6806             
6807         if(!isUpdate){
6808             this.fireEvent("rowsinserted", this, rowIndex);
6809             //this.syncRowHeights(firstRow, lastRow);
6810             //this.stripeRows(firstRow);
6811             //this.layout();
6812         }
6813         
6814     },
6815     
6816     
6817     getRowDom : function(rowIndex)
6818     {
6819         var rows = this.el.select('tbody > tr', true).elements;
6820         
6821         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6822         
6823     },
6824     // returns the object tree for a tr..
6825   
6826     
6827     renderRow : function(cm, ds, rowIndex) 
6828     {
6829         var d = ds.getAt(rowIndex);
6830         
6831         var row = {
6832             tag : 'tr',
6833             cls : 'x-row-' + rowIndex,
6834             cn : []
6835         };
6836             
6837         var cellObjects = [];
6838         
6839         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6840             var config = cm.config[i];
6841             
6842             var renderer = cm.getRenderer(i);
6843             var value = '';
6844             var id = false;
6845             
6846             if(typeof(renderer) !== 'undefined'){
6847                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6848             }
6849             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6850             // and are rendered into the cells after the row is rendered - using the id for the element.
6851             
6852             if(typeof(value) === 'object'){
6853                 id = Roo.id();
6854                 cellObjects.push({
6855                     container : id,
6856                     cfg : value 
6857                 })
6858             }
6859             
6860             var rowcfg = {
6861                 record: d,
6862                 rowIndex : rowIndex,
6863                 colIndex : i,
6864                 rowClass : ''
6865             };
6866
6867             this.fireEvent('rowclass', this, rowcfg);
6868             
6869             var td = {
6870                 tag: 'td',
6871                 cls : rowcfg.rowClass + ' x-col-' + i,
6872                 style: '',
6873                 html: (typeof(value) === 'object') ? '' : value
6874             };
6875             
6876             if (id) {
6877                 td.id = id;
6878             }
6879             
6880             if(typeof(config.colspan) != 'undefined'){
6881                 td.colspan = config.colspan;
6882             }
6883             
6884             if(typeof(config.hidden) != 'undefined' && config.hidden){
6885                 td.style += ' display:none;';
6886             }
6887             
6888             if(typeof(config.align) != 'undefined' && config.align.length){
6889                 td.style += ' text-align:' + config.align + ';';
6890             }
6891             if(typeof(config.valign) != 'undefined' && config.valign.length){
6892                 td.style += ' vertical-align:' + config.valign + ';';
6893             }
6894             
6895             if(typeof(config.width) != 'undefined'){
6896                 td.style += ' width:' +  config.width + 'px;';
6897             }
6898             
6899             if(typeof(config.cursor) != 'undefined'){
6900                 td.style += ' cursor:' +  config.cursor + ';';
6901             }
6902             
6903             if(typeof(config.cls) != 'undefined'){
6904                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6905             }
6906             
6907             ['xs','sm','md','lg'].map(function(size){
6908                 
6909                 if(typeof(config[size]) == 'undefined'){
6910                     return;
6911                 }
6912                 
6913                 if (!config[size]) { // 0 = hidden
6914                     td.cls += ' hidden-' + size;
6915                     return;
6916                 }
6917                 
6918                 td.cls += ' col-' + size + '-' + config[size];
6919
6920             });
6921             
6922             row.cn.push(td);
6923            
6924         }
6925         
6926         row.cellObjects = cellObjects;
6927         
6928         return row;
6929           
6930     },
6931     
6932     
6933     
6934     onBeforeLoad : function()
6935     {
6936         
6937     },
6938      /**
6939      * Remove all rows
6940      */
6941     clear : function()
6942     {
6943         this.el.select('tbody', true).first().dom.innerHTML = '';
6944     },
6945     /**
6946      * Show or hide a row.
6947      * @param {Number} rowIndex to show or hide
6948      * @param {Boolean} state hide
6949      */
6950     setRowVisibility : function(rowIndex, state)
6951     {
6952         var bt = this.mainBody.dom;
6953         
6954         var rows = this.el.select('tbody > tr', true).elements;
6955         
6956         if(typeof(rows[rowIndex]) == 'undefined'){
6957             return;
6958         }
6959         rows[rowIndex].dom.style.display = state ? '' : 'none';
6960     },
6961     
6962     
6963     getSelectionModel : function(){
6964         if(!this.selModel){
6965             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6966         }
6967         return this.selModel;
6968     },
6969     /*
6970      * Render the Roo.bootstrap object from renderder
6971      */
6972     renderCellObject : function(r)
6973     {
6974         var _this = this;
6975         
6976         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6977         
6978         var t = r.cfg.render(r.container);
6979         
6980         if(r.cfg.cn){
6981             Roo.each(r.cfg.cn, function(c){
6982                 var child = {
6983                     container: t.getChildContainer(),
6984                     cfg: c
6985                 };
6986                 _this.renderCellObject(child);
6987             })
6988         }
6989     },
6990     
6991     getRowIndex : function(row)
6992     {
6993         var rowIndex = -1;
6994         
6995         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6996             if(el != row){
6997                 return;
6998             }
6999             
7000             rowIndex = index;
7001         });
7002         
7003         return rowIndex;
7004     },
7005      /**
7006      * Returns the grid's underlying element = used by panel.Grid
7007      * @return {Element} The element
7008      */
7009     getGridEl : function(){
7010         return this.el;
7011     },
7012      /**
7013      * Forces a resize - used by panel.Grid
7014      * @return {Element} The element
7015      */
7016     autoSize : function()
7017     {
7018         //var ctr = Roo.get(this.container.dom.parentElement);
7019         var ctr = Roo.get(this.el.dom);
7020         
7021         var thd = this.getGridEl().select('thead',true).first();
7022         var tbd = this.getGridEl().select('tbody', true).first();
7023         var tfd = this.getGridEl().select('tfoot', true).first();
7024         
7025         var cw = ctr.getWidth();
7026         
7027         if (tbd) {
7028             
7029             tbd.setSize(ctr.getWidth(),
7030                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7031             );
7032             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7033             cw -= barsize;
7034         }
7035         cw = Math.max(cw, this.totalWidth);
7036         this.getGridEl().select('tr',true).setWidth(cw);
7037         // resize 'expandable coloumn?
7038         
7039         return; // we doe not have a view in this design..
7040         
7041     },
7042     onBodyScroll: function()
7043     {
7044         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7045         if(this.mainHead){
7046             this.mainHead.setStyle({
7047                 'position' : 'relative',
7048                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7049             });
7050         }
7051         
7052         if(this.lazyLoad){
7053             
7054             var scrollHeight = this.mainBody.dom.scrollHeight;
7055             
7056             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7057             
7058             var height = this.mainBody.getHeight();
7059             
7060             if(scrollHeight - height == scrollTop) {
7061                 
7062                 var total = this.ds.getTotalCount();
7063                 
7064                 if(this.footer.cursor + this.footer.pageSize < total){
7065                     
7066                     this.footer.ds.load({
7067                         params : {
7068                             start : this.footer.cursor + this.footer.pageSize,
7069                             limit : this.footer.pageSize
7070                         },
7071                         add : true
7072                     });
7073                 }
7074             }
7075             
7076         }
7077     },
7078     
7079     onHeaderChange : function()
7080     {
7081         var header = this.renderHeader();
7082         var table = this.el.select('table', true).first();
7083         
7084         this.mainHead.remove();
7085         this.mainHead = table.createChild(header, this.mainBody, false);
7086     },
7087     
7088     onHiddenChange : function(colModel, colIndex, hidden)
7089     {
7090         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7091         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7092         
7093         this.CSS.updateRule(thSelector, "display", "");
7094         this.CSS.updateRule(tdSelector, "display", "");
7095         
7096         if(hidden){
7097             this.CSS.updateRule(thSelector, "display", "none");
7098             this.CSS.updateRule(tdSelector, "display", "none");
7099         }
7100         
7101         this.onHeaderChange();
7102         this.onLoad();
7103         
7104     }
7105     
7106 });
7107
7108  
7109
7110  /*
7111  * - LGPL
7112  *
7113  * table cell
7114  * 
7115  */
7116
7117 /**
7118  * @class Roo.bootstrap.TableCell
7119  * @extends Roo.bootstrap.Component
7120  * Bootstrap TableCell class
7121  * @cfg {String} html cell contain text
7122  * @cfg {String} cls cell class
7123  * @cfg {String} tag cell tag (td|th) default td
7124  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7125  * @cfg {String} align Aligns the content in a cell
7126  * @cfg {String} axis Categorizes cells
7127  * @cfg {String} bgcolor Specifies the background color of a cell
7128  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7129  * @cfg {Number} colspan Specifies the number of columns a cell should span
7130  * @cfg {String} headers Specifies one or more header cells a cell is related to
7131  * @cfg {Number} height Sets the height of a cell
7132  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7133  * @cfg {Number} rowspan Sets the number of rows a cell should span
7134  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7135  * @cfg {String} valign Vertical aligns the content in a cell
7136  * @cfg {Number} width Specifies the width of a cell
7137  * 
7138  * @constructor
7139  * Create a new TableCell
7140  * @param {Object} config The config object
7141  */
7142
7143 Roo.bootstrap.TableCell = function(config){
7144     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7145 };
7146
7147 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7148     
7149     html: false,
7150     cls: false,
7151     tag: false,
7152     abbr: false,
7153     align: false,
7154     axis: false,
7155     bgcolor: false,
7156     charoff: false,
7157     colspan: false,
7158     headers: false,
7159     height: false,
7160     nowrap: false,
7161     rowspan: false,
7162     scope: false,
7163     valign: false,
7164     width: false,
7165     
7166     
7167     getAutoCreate : function(){
7168         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7169         
7170         cfg = {
7171             tag: 'td'
7172         };
7173         
7174         if(this.tag){
7175             cfg.tag = this.tag;
7176         }
7177         
7178         if (this.html) {
7179             cfg.html=this.html
7180         }
7181         if (this.cls) {
7182             cfg.cls=this.cls
7183         }
7184         if (this.abbr) {
7185             cfg.abbr=this.abbr
7186         }
7187         if (this.align) {
7188             cfg.align=this.align
7189         }
7190         if (this.axis) {
7191             cfg.axis=this.axis
7192         }
7193         if (this.bgcolor) {
7194             cfg.bgcolor=this.bgcolor
7195         }
7196         if (this.charoff) {
7197             cfg.charoff=this.charoff
7198         }
7199         if (this.colspan) {
7200             cfg.colspan=this.colspan
7201         }
7202         if (this.headers) {
7203             cfg.headers=this.headers
7204         }
7205         if (this.height) {
7206             cfg.height=this.height
7207         }
7208         if (this.nowrap) {
7209             cfg.nowrap=this.nowrap
7210         }
7211         if (this.rowspan) {
7212             cfg.rowspan=this.rowspan
7213         }
7214         if (this.scope) {
7215             cfg.scope=this.scope
7216         }
7217         if (this.valign) {
7218             cfg.valign=this.valign
7219         }
7220         if (this.width) {
7221             cfg.width=this.width
7222         }
7223         
7224         
7225         return cfg;
7226     }
7227    
7228 });
7229
7230  
7231
7232  /*
7233  * - LGPL
7234  *
7235  * table row
7236  * 
7237  */
7238
7239 /**
7240  * @class Roo.bootstrap.TableRow
7241  * @extends Roo.bootstrap.Component
7242  * Bootstrap TableRow class
7243  * @cfg {String} cls row class
7244  * @cfg {String} align Aligns the content in a table row
7245  * @cfg {String} bgcolor Specifies a background color for a table row
7246  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7247  * @cfg {String} valign Vertical aligns the content in a table row
7248  * 
7249  * @constructor
7250  * Create a new TableRow
7251  * @param {Object} config The config object
7252  */
7253
7254 Roo.bootstrap.TableRow = function(config){
7255     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7256 };
7257
7258 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7259     
7260     cls: false,
7261     align: false,
7262     bgcolor: false,
7263     charoff: false,
7264     valign: false,
7265     
7266     getAutoCreate : function(){
7267         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7268         
7269         cfg = {
7270             tag: 'tr'
7271         };
7272             
7273         if(this.cls){
7274             cfg.cls = this.cls;
7275         }
7276         if(this.align){
7277             cfg.align = this.align;
7278         }
7279         if(this.bgcolor){
7280             cfg.bgcolor = this.bgcolor;
7281         }
7282         if(this.charoff){
7283             cfg.charoff = this.charoff;
7284         }
7285         if(this.valign){
7286             cfg.valign = this.valign;
7287         }
7288         
7289         return cfg;
7290     }
7291    
7292 });
7293
7294  
7295
7296  /*
7297  * - LGPL
7298  *
7299  * table body
7300  * 
7301  */
7302
7303 /**
7304  * @class Roo.bootstrap.TableBody
7305  * @extends Roo.bootstrap.Component
7306  * Bootstrap TableBody class
7307  * @cfg {String} cls element class
7308  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7309  * @cfg {String} align Aligns the content inside the element
7310  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7311  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7312  * 
7313  * @constructor
7314  * Create a new TableBody
7315  * @param {Object} config The config object
7316  */
7317
7318 Roo.bootstrap.TableBody = function(config){
7319     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7320 };
7321
7322 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7323     
7324     cls: false,
7325     tag: false,
7326     align: false,
7327     charoff: false,
7328     valign: false,
7329     
7330     getAutoCreate : function(){
7331         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7332         
7333         cfg = {
7334             tag: 'tbody'
7335         };
7336             
7337         if (this.cls) {
7338             cfg.cls=this.cls
7339         }
7340         if(this.tag){
7341             cfg.tag = this.tag;
7342         }
7343         
7344         if(this.align){
7345             cfg.align = this.align;
7346         }
7347         if(this.charoff){
7348             cfg.charoff = this.charoff;
7349         }
7350         if(this.valign){
7351             cfg.valign = this.valign;
7352         }
7353         
7354         return cfg;
7355     }
7356     
7357     
7358 //    initEvents : function()
7359 //    {
7360 //        
7361 //        if(!this.store){
7362 //            return;
7363 //        }
7364 //        
7365 //        this.store = Roo.factory(this.store, Roo.data);
7366 //        this.store.on('load', this.onLoad, this);
7367 //        
7368 //        this.store.load();
7369 //        
7370 //    },
7371 //    
7372 //    onLoad: function () 
7373 //    {   
7374 //        this.fireEvent('load', this);
7375 //    }
7376 //    
7377 //   
7378 });
7379
7380  
7381
7382  /*
7383  * Based on:
7384  * Ext JS Library 1.1.1
7385  * Copyright(c) 2006-2007, Ext JS, LLC.
7386  *
7387  * Originally Released Under LGPL - original licence link has changed is not relivant.
7388  *
7389  * Fork - LGPL
7390  * <script type="text/javascript">
7391  */
7392
7393 // as we use this in bootstrap.
7394 Roo.namespace('Roo.form');
7395  /**
7396  * @class Roo.form.Action
7397  * Internal Class used to handle form actions
7398  * @constructor
7399  * @param {Roo.form.BasicForm} el The form element or its id
7400  * @param {Object} config Configuration options
7401  */
7402
7403  
7404  
7405 // define the action interface
7406 Roo.form.Action = function(form, options){
7407     this.form = form;
7408     this.options = options || {};
7409 };
7410 /**
7411  * Client Validation Failed
7412  * @const 
7413  */
7414 Roo.form.Action.CLIENT_INVALID = 'client';
7415 /**
7416  * Server Validation Failed
7417  * @const 
7418  */
7419 Roo.form.Action.SERVER_INVALID = 'server';
7420  /**
7421  * Connect to Server Failed
7422  * @const 
7423  */
7424 Roo.form.Action.CONNECT_FAILURE = 'connect';
7425 /**
7426  * Reading Data from Server Failed
7427  * @const 
7428  */
7429 Roo.form.Action.LOAD_FAILURE = 'load';
7430
7431 Roo.form.Action.prototype = {
7432     type : 'default',
7433     failureType : undefined,
7434     response : undefined,
7435     result : undefined,
7436
7437     // interface method
7438     run : function(options){
7439
7440     },
7441
7442     // interface method
7443     success : function(response){
7444
7445     },
7446
7447     // interface method
7448     handleResponse : function(response){
7449
7450     },
7451
7452     // default connection failure
7453     failure : function(response){
7454         
7455         this.response = response;
7456         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7457         this.form.afterAction(this, false);
7458     },
7459
7460     processResponse : function(response){
7461         this.response = response;
7462         if(!response.responseText){
7463             return true;
7464         }
7465         this.result = this.handleResponse(response);
7466         return this.result;
7467     },
7468
7469     // utility functions used internally
7470     getUrl : function(appendParams){
7471         var url = this.options.url || this.form.url || this.form.el.dom.action;
7472         if(appendParams){
7473             var p = this.getParams();
7474             if(p){
7475                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7476             }
7477         }
7478         return url;
7479     },
7480
7481     getMethod : function(){
7482         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7483     },
7484
7485     getParams : function(){
7486         var bp = this.form.baseParams;
7487         var p = this.options.params;
7488         if(p){
7489             if(typeof p == "object"){
7490                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7491             }else if(typeof p == 'string' && bp){
7492                 p += '&' + Roo.urlEncode(bp);
7493             }
7494         }else if(bp){
7495             p = Roo.urlEncode(bp);
7496         }
7497         return p;
7498     },
7499
7500     createCallback : function(){
7501         return {
7502             success: this.success,
7503             failure: this.failure,
7504             scope: this,
7505             timeout: (this.form.timeout*1000),
7506             upload: this.form.fileUpload ? this.success : undefined
7507         };
7508     }
7509 };
7510
7511 Roo.form.Action.Submit = function(form, options){
7512     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7513 };
7514
7515 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7516     type : 'submit',
7517
7518     haveProgress : false,
7519     uploadComplete : false,
7520     
7521     // uploadProgress indicator.
7522     uploadProgress : function()
7523     {
7524         if (!this.form.progressUrl) {
7525             return;
7526         }
7527         
7528         if (!this.haveProgress) {
7529             Roo.MessageBox.progress("Uploading", "Uploading");
7530         }
7531         if (this.uploadComplete) {
7532            Roo.MessageBox.hide();
7533            return;
7534         }
7535         
7536         this.haveProgress = true;
7537    
7538         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7539         
7540         var c = new Roo.data.Connection();
7541         c.request({
7542             url : this.form.progressUrl,
7543             params: {
7544                 id : uid
7545             },
7546             method: 'GET',
7547             success : function(req){
7548                //console.log(data);
7549                 var rdata = false;
7550                 var edata;
7551                 try  {
7552                    rdata = Roo.decode(req.responseText)
7553                 } catch (e) {
7554                     Roo.log("Invalid data from server..");
7555                     Roo.log(edata);
7556                     return;
7557                 }
7558                 if (!rdata || !rdata.success) {
7559                     Roo.log(rdata);
7560                     Roo.MessageBox.alert(Roo.encode(rdata));
7561                     return;
7562                 }
7563                 var data = rdata.data;
7564                 
7565                 if (this.uploadComplete) {
7566                    Roo.MessageBox.hide();
7567                    return;
7568                 }
7569                    
7570                 if (data){
7571                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7572                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7573                     );
7574                 }
7575                 this.uploadProgress.defer(2000,this);
7576             },
7577        
7578             failure: function(data) {
7579                 Roo.log('progress url failed ');
7580                 Roo.log(data);
7581             },
7582             scope : this
7583         });
7584            
7585     },
7586     
7587     
7588     run : function()
7589     {
7590         // run get Values on the form, so it syncs any secondary forms.
7591         this.form.getValues();
7592         
7593         var o = this.options;
7594         var method = this.getMethod();
7595         var isPost = method == 'POST';
7596         if(o.clientValidation === false || this.form.isValid()){
7597             
7598             if (this.form.progressUrl) {
7599                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7600                     (new Date() * 1) + '' + Math.random());
7601                     
7602             } 
7603             
7604             
7605             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7606                 form:this.form.el.dom,
7607                 url:this.getUrl(!isPost),
7608                 method: method,
7609                 params:isPost ? this.getParams() : null,
7610                 isUpload: this.form.fileUpload
7611             }));
7612             
7613             this.uploadProgress();
7614
7615         }else if (o.clientValidation !== false){ // client validation failed
7616             this.failureType = Roo.form.Action.CLIENT_INVALID;
7617             this.form.afterAction(this, false);
7618         }
7619     },
7620
7621     success : function(response)
7622     {
7623         this.uploadComplete= true;
7624         if (this.haveProgress) {
7625             Roo.MessageBox.hide();
7626         }
7627         
7628         
7629         var result = this.processResponse(response);
7630         if(result === true || result.success){
7631             this.form.afterAction(this, true);
7632             return;
7633         }
7634         if(result.errors){
7635             this.form.markInvalid(result.errors);
7636             this.failureType = Roo.form.Action.SERVER_INVALID;
7637         }
7638         this.form.afterAction(this, false);
7639     },
7640     failure : function(response)
7641     {
7642         this.uploadComplete= true;
7643         if (this.haveProgress) {
7644             Roo.MessageBox.hide();
7645         }
7646         
7647         this.response = response;
7648         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7649         this.form.afterAction(this, false);
7650     },
7651     
7652     handleResponse : function(response){
7653         if(this.form.errorReader){
7654             var rs = this.form.errorReader.read(response);
7655             var errors = [];
7656             if(rs.records){
7657                 for(var i = 0, len = rs.records.length; i < len; i++) {
7658                     var r = rs.records[i];
7659                     errors[i] = r.data;
7660                 }
7661             }
7662             if(errors.length < 1){
7663                 errors = null;
7664             }
7665             return {
7666                 success : rs.success,
7667                 errors : errors
7668             };
7669         }
7670         var ret = false;
7671         try {
7672             ret = Roo.decode(response.responseText);
7673         } catch (e) {
7674             ret = {
7675                 success: false,
7676                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7677                 errors : []
7678             };
7679         }
7680         return ret;
7681         
7682     }
7683 });
7684
7685
7686 Roo.form.Action.Load = function(form, options){
7687     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7688     this.reader = this.form.reader;
7689 };
7690
7691 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7692     type : 'load',
7693
7694     run : function(){
7695         
7696         Roo.Ajax.request(Roo.apply(
7697                 this.createCallback(), {
7698                     method:this.getMethod(),
7699                     url:this.getUrl(false),
7700                     params:this.getParams()
7701         }));
7702     },
7703
7704     success : function(response){
7705         
7706         var result = this.processResponse(response);
7707         if(result === true || !result.success || !result.data){
7708             this.failureType = Roo.form.Action.LOAD_FAILURE;
7709             this.form.afterAction(this, false);
7710             return;
7711         }
7712         this.form.clearInvalid();
7713         this.form.setValues(result.data);
7714         this.form.afterAction(this, true);
7715     },
7716
7717     handleResponse : function(response){
7718         if(this.form.reader){
7719             var rs = this.form.reader.read(response);
7720             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7721             return {
7722                 success : rs.success,
7723                 data : data
7724             };
7725         }
7726         return Roo.decode(response.responseText);
7727     }
7728 });
7729
7730 Roo.form.Action.ACTION_TYPES = {
7731     'load' : Roo.form.Action.Load,
7732     'submit' : Roo.form.Action.Submit
7733 };/*
7734  * - LGPL
7735  *
7736  * form
7737  *
7738  */
7739
7740 /**
7741  * @class Roo.bootstrap.Form
7742  * @extends Roo.bootstrap.Component
7743  * Bootstrap Form class
7744  * @cfg {String} method  GET | POST (default POST)
7745  * @cfg {String} labelAlign top | left (default top)
7746  * @cfg {String} align left  | right - for navbars
7747  * @cfg {Boolean} loadMask load mask when submit (default true)
7748
7749  *
7750  * @constructor
7751  * Create a new Form
7752  * @param {Object} config The config object
7753  */
7754
7755
7756 Roo.bootstrap.Form = function(config){
7757     
7758     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7759     
7760     Roo.bootstrap.Form.popover.apply();
7761     
7762     this.addEvents({
7763         /**
7764          * @event clientvalidation
7765          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7766          * @param {Form} this
7767          * @param {Boolean} valid true if the form has passed client-side validation
7768          */
7769         clientvalidation: true,
7770         /**
7771          * @event beforeaction
7772          * Fires before any action is performed. Return false to cancel the action.
7773          * @param {Form} this
7774          * @param {Action} action The action to be performed
7775          */
7776         beforeaction: true,
7777         /**
7778          * @event actionfailed
7779          * Fires when an action fails.
7780          * @param {Form} this
7781          * @param {Action} action The action that failed
7782          */
7783         actionfailed : true,
7784         /**
7785          * @event actioncomplete
7786          * Fires when an action is completed.
7787          * @param {Form} this
7788          * @param {Action} action The action that completed
7789          */
7790         actioncomplete : true
7791     });
7792 };
7793
7794 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7795
7796      /**
7797      * @cfg {String} method
7798      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7799      */
7800     method : 'POST',
7801     /**
7802      * @cfg {String} url
7803      * The URL to use for form actions if one isn't supplied in the action options.
7804      */
7805     /**
7806      * @cfg {Boolean} fileUpload
7807      * Set to true if this form is a file upload.
7808      */
7809
7810     /**
7811      * @cfg {Object} baseParams
7812      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7813      */
7814
7815     /**
7816      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7817      */
7818     timeout: 30,
7819     /**
7820      * @cfg {Sting} align (left|right) for navbar forms
7821      */
7822     align : 'left',
7823
7824     // private
7825     activeAction : null,
7826
7827     /**
7828      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7829      * element by passing it or its id or mask the form itself by passing in true.
7830      * @type Mixed
7831      */
7832     waitMsgTarget : false,
7833
7834     loadMask : true,
7835     
7836     /**
7837      * @cfg {Boolean} errorMask (true|false) default false
7838      */
7839     errorMask : false,
7840     
7841     /**
7842      * @cfg {Number} maskOffset Default 100
7843      */
7844     maskOffset : 100,
7845     
7846     /**
7847      * @cfg {Boolean} maskBody
7848      */
7849     maskBody : false,
7850
7851     getAutoCreate : function(){
7852
7853         var cfg = {
7854             tag: 'form',
7855             method : this.method || 'POST',
7856             id : this.id || Roo.id(),
7857             cls : ''
7858         };
7859         if (this.parent().xtype.match(/^Nav/)) {
7860             cfg.cls = 'navbar-form navbar-' + this.align;
7861
7862         }
7863
7864         if (this.labelAlign == 'left' ) {
7865             cfg.cls += ' form-horizontal';
7866         }
7867
7868
7869         return cfg;
7870     },
7871     initEvents : function()
7872     {
7873         this.el.on('submit', this.onSubmit, this);
7874         // this was added as random key presses on the form where triggering form submit.
7875         this.el.on('keypress', function(e) {
7876             if (e.getCharCode() != 13) {
7877                 return true;
7878             }
7879             // we might need to allow it for textareas.. and some other items.
7880             // check e.getTarget().
7881
7882             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7883                 return true;
7884             }
7885
7886             Roo.log("keypress blocked");
7887
7888             e.preventDefault();
7889             return false;
7890         });
7891         
7892     },
7893     // private
7894     onSubmit : function(e){
7895         e.stopEvent();
7896     },
7897
7898      /**
7899      * Returns true if client-side validation on the form is successful.
7900      * @return Boolean
7901      */
7902     isValid : function(){
7903         var items = this.getItems();
7904         var valid = true;
7905         var target = false;
7906         
7907         items.each(function(f){
7908             
7909             if(f.validate()){
7910                 return;
7911             }
7912             
7913             Roo.log('invalid field: ' + f.name);
7914             
7915             valid = false;
7916
7917             if(!target && f.el.isVisible(true)){
7918                 target = f;
7919             }
7920            
7921         });
7922         
7923         if(this.errorMask && !valid){
7924             Roo.bootstrap.Form.popover.mask(this, target);
7925         }
7926         
7927         return valid;
7928     },
7929     
7930     /**
7931      * Returns true if any fields in this form have changed since their original load.
7932      * @return Boolean
7933      */
7934     isDirty : function(){
7935         var dirty = false;
7936         var items = this.getItems();
7937         items.each(function(f){
7938            if(f.isDirty()){
7939                dirty = true;
7940                return false;
7941            }
7942            return true;
7943         });
7944         return dirty;
7945     },
7946      /**
7947      * Performs a predefined action (submit or load) or custom actions you define on this form.
7948      * @param {String} actionName The name of the action type
7949      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7950      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7951      * accept other config options):
7952      * <pre>
7953 Property          Type             Description
7954 ----------------  ---------------  ----------------------------------------------------------------------------------
7955 url               String           The url for the action (defaults to the form's url)
7956 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7957 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7958 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7959                                    validate the form on the client (defaults to false)
7960      * </pre>
7961      * @return {BasicForm} this
7962      */
7963     doAction : function(action, options){
7964         if(typeof action == 'string'){
7965             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7966         }
7967         if(this.fireEvent('beforeaction', this, action) !== false){
7968             this.beforeAction(action);
7969             action.run.defer(100, action);
7970         }
7971         return this;
7972     },
7973
7974     // private
7975     beforeAction : function(action){
7976         var o = action.options;
7977         
7978         if(this.loadMask){
7979             
7980             if(this.maskBody){
7981                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7982             } else {
7983                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7984             }
7985         }
7986         // not really supported yet.. ??
7987
7988         //if(this.waitMsgTarget === true){
7989         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7990         //}else if(this.waitMsgTarget){
7991         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7992         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7993         //}else {
7994         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7995        // }
7996
7997     },
7998
7999     // private
8000     afterAction : function(action, success){
8001         this.activeAction = null;
8002         var o = action.options;
8003
8004         if(this.loadMask){
8005             
8006             if(this.maskBody){
8007                 Roo.get(document.body).unmask();
8008             } else {
8009                 this.el.unmask();
8010             }
8011         }
8012         
8013         //if(this.waitMsgTarget === true){
8014 //            this.el.unmask();
8015         //}else if(this.waitMsgTarget){
8016         //    this.waitMsgTarget.unmask();
8017         //}else{
8018         //    Roo.MessageBox.updateProgress(1);
8019         //    Roo.MessageBox.hide();
8020        // }
8021         //
8022         if(success){
8023             if(o.reset){
8024                 this.reset();
8025             }
8026             Roo.callback(o.success, o.scope, [this, action]);
8027             this.fireEvent('actioncomplete', this, action);
8028
8029         }else{
8030
8031             // failure condition..
8032             // we have a scenario where updates need confirming.
8033             // eg. if a locking scenario exists..
8034             // we look for { errors : { needs_confirm : true }} in the response.
8035             if (
8036                 (typeof(action.result) != 'undefined')  &&
8037                 (typeof(action.result.errors) != 'undefined')  &&
8038                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8039            ){
8040                 var _t = this;
8041                 Roo.log("not supported yet");
8042                  /*
8043
8044                 Roo.MessageBox.confirm(
8045                     "Change requires confirmation",
8046                     action.result.errorMsg,
8047                     function(r) {
8048                         if (r != 'yes') {
8049                             return;
8050                         }
8051                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8052                     }
8053
8054                 );
8055                 */
8056
8057
8058                 return;
8059             }
8060
8061             Roo.callback(o.failure, o.scope, [this, action]);
8062             // show an error message if no failed handler is set..
8063             if (!this.hasListener('actionfailed')) {
8064                 Roo.log("need to add dialog support");
8065                 /*
8066                 Roo.MessageBox.alert("Error",
8067                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8068                         action.result.errorMsg :
8069                         "Saving Failed, please check your entries or try again"
8070                 );
8071                 */
8072             }
8073
8074             this.fireEvent('actionfailed', this, action);
8075         }
8076
8077     },
8078     /**
8079      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8080      * @param {String} id The value to search for
8081      * @return Field
8082      */
8083     findField : function(id){
8084         var items = this.getItems();
8085         var field = items.get(id);
8086         if(!field){
8087              items.each(function(f){
8088                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8089                     field = f;
8090                     return false;
8091                 }
8092                 return true;
8093             });
8094         }
8095         return field || null;
8096     },
8097      /**
8098      * Mark fields in this form invalid in bulk.
8099      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8100      * @return {BasicForm} this
8101      */
8102     markInvalid : function(errors){
8103         if(errors instanceof Array){
8104             for(var i = 0, len = errors.length; i < len; i++){
8105                 var fieldError = errors[i];
8106                 var f = this.findField(fieldError.id);
8107                 if(f){
8108                     f.markInvalid(fieldError.msg);
8109                 }
8110             }
8111         }else{
8112             var field, id;
8113             for(id in errors){
8114                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8115                     field.markInvalid(errors[id]);
8116                 }
8117             }
8118         }
8119         //Roo.each(this.childForms || [], function (f) {
8120         //    f.markInvalid(errors);
8121         //});
8122
8123         return this;
8124     },
8125
8126     /**
8127      * Set values for fields in this form in bulk.
8128      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8129      * @return {BasicForm} this
8130      */
8131     setValues : function(values){
8132         if(values instanceof Array){ // array of objects
8133             for(var i = 0, len = values.length; i < len; i++){
8134                 var v = values[i];
8135                 var f = this.findField(v.id);
8136                 if(f){
8137                     f.setValue(v.value);
8138                     if(this.trackResetOnLoad){
8139                         f.originalValue = f.getValue();
8140                     }
8141                 }
8142             }
8143         }else{ // object hash
8144             var field, id;
8145             for(id in values){
8146                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8147
8148                     if (field.setFromData &&
8149                         field.valueField &&
8150                         field.displayField &&
8151                         // combos' with local stores can
8152                         // be queried via setValue()
8153                         // to set their value..
8154                         (field.store && !field.store.isLocal)
8155                         ) {
8156                         // it's a combo
8157                         var sd = { };
8158                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8159                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8160                         field.setFromData(sd);
8161
8162                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8163                         
8164                         field.setFromData(values);
8165                         
8166                     } else {
8167                         field.setValue(values[id]);
8168                     }
8169
8170
8171                     if(this.trackResetOnLoad){
8172                         field.originalValue = field.getValue();
8173                     }
8174                 }
8175             }
8176         }
8177
8178         //Roo.each(this.childForms || [], function (f) {
8179         //    f.setValues(values);
8180         //});
8181
8182         return this;
8183     },
8184
8185     /**
8186      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8187      * they are returned as an array.
8188      * @param {Boolean} asString
8189      * @return {Object}
8190      */
8191     getValues : function(asString){
8192         //if (this.childForms) {
8193             // copy values from the child forms
8194         //    Roo.each(this.childForms, function (f) {
8195         //        this.setValues(f.getValues());
8196         //    }, this);
8197         //}
8198
8199
8200
8201         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8202         if(asString === true){
8203             return fs;
8204         }
8205         return Roo.urlDecode(fs);
8206     },
8207
8208     /**
8209      * Returns the fields in this form as an object with key/value pairs.
8210      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8211      * @return {Object}
8212      */
8213     getFieldValues : function(with_hidden)
8214     {
8215         var items = this.getItems();
8216         var ret = {};
8217         items.each(function(f){
8218             
8219             if (!f.getName()) {
8220                 return;
8221             }
8222             
8223             var v = f.getValue();
8224             
8225             if (f.inputType =='radio') {
8226                 if (typeof(ret[f.getName()]) == 'undefined') {
8227                     ret[f.getName()] = ''; // empty..
8228                 }
8229
8230                 if (!f.el.dom.checked) {
8231                     return;
8232
8233                 }
8234                 v = f.el.dom.value;
8235
8236             }
8237             
8238             if(f.xtype == 'MoneyField'){
8239                 ret[f.currencyName] = f.getCurrency();
8240             }
8241
8242             // not sure if this supported any more..
8243             if ((typeof(v) == 'object') && f.getRawValue) {
8244                 v = f.getRawValue() ; // dates..
8245             }
8246             // combo boxes where name != hiddenName...
8247             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8248                 ret[f.name] = f.getRawValue();
8249             }
8250             ret[f.getName()] = v;
8251         });
8252
8253         return ret;
8254     },
8255
8256     /**
8257      * Clears all invalid messages in this form.
8258      * @return {BasicForm} this
8259      */
8260     clearInvalid : function(){
8261         var items = this.getItems();
8262
8263         items.each(function(f){
8264            f.clearInvalid();
8265         });
8266
8267         return this;
8268     },
8269
8270     /**
8271      * Resets this form.
8272      * @return {BasicForm} this
8273      */
8274     reset : function(){
8275         var items = this.getItems();
8276         items.each(function(f){
8277             f.reset();
8278         });
8279
8280         Roo.each(this.childForms || [], function (f) {
8281             f.reset();
8282         });
8283
8284
8285         return this;
8286     },
8287     
8288     getItems : function()
8289     {
8290         var r=new Roo.util.MixedCollection(false, function(o){
8291             return o.id || (o.id = Roo.id());
8292         });
8293         var iter = function(el) {
8294             if (el.inputEl) {
8295                 r.add(el);
8296             }
8297             if (!el.items) {
8298                 return;
8299             }
8300             Roo.each(el.items,function(e) {
8301                 iter(e);
8302             });
8303         };
8304
8305         iter(this);
8306         return r;
8307     },
8308     
8309     hideFields : function(items)
8310     {
8311         Roo.each(items, function(i){
8312             
8313             var f = this.findField(i);
8314             
8315             if(!f){
8316                 return;
8317             }
8318             
8319             f.hide();
8320             
8321         }, this);
8322     },
8323     
8324     showFields : function(items)
8325     {
8326         Roo.each(items, function(i){
8327             
8328             var f = this.findField(i);
8329             
8330             if(!f){
8331                 return;
8332             }
8333             
8334             f.show();
8335             
8336         }, this);
8337     }
8338
8339 });
8340
8341 Roo.apply(Roo.bootstrap.Form, {
8342     
8343     popover : {
8344         
8345         padding : 5,
8346         
8347         isApplied : false,
8348         
8349         isMasked : false,
8350         
8351         form : false,
8352         
8353         target : false,
8354         
8355         toolTip : false,
8356         
8357         intervalID : false,
8358         
8359         maskEl : false,
8360         
8361         apply : function()
8362         {
8363             if(this.isApplied){
8364                 return;
8365             }
8366             
8367             this.maskEl = {
8368                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8369                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8370                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8371                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8372             };
8373             
8374             this.maskEl.top.enableDisplayMode("block");
8375             this.maskEl.left.enableDisplayMode("block");
8376             this.maskEl.bottom.enableDisplayMode("block");
8377             this.maskEl.right.enableDisplayMode("block");
8378             
8379             this.toolTip = new Roo.bootstrap.Tooltip({
8380                 cls : 'roo-form-error-popover',
8381                 alignment : {
8382                     'left' : ['r-l', [-2,0], 'right'],
8383                     'right' : ['l-r', [2,0], 'left'],
8384                     'bottom' : ['tl-bl', [0,2], 'top'],
8385                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8386                 }
8387             });
8388             
8389             this.toolTip.render(Roo.get(document.body));
8390
8391             this.toolTip.el.enableDisplayMode("block");
8392             
8393             Roo.get(document.body).on('click', function(){
8394                 this.unmask();
8395             }, this);
8396             
8397             Roo.get(document.body).on('touchstart', function(){
8398                 this.unmask();
8399             }, this);
8400             
8401             this.isApplied = true
8402         },
8403         
8404         mask : function(form, target)
8405         {
8406             this.form = form;
8407             
8408             this.target = target;
8409             
8410             if(!this.form.errorMask || !target.el){
8411                 return;
8412             }
8413             
8414             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8415             
8416             Roo.log(scrollable);
8417             
8418             var ot = this.target.el.calcOffsetsTo(scrollable);
8419             
8420             var scrollTo = ot[1] - this.form.maskOffset;
8421             
8422             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8423             
8424             scrollable.scrollTo('top', scrollTo);
8425             
8426             var box = this.target.el.getBox();
8427             Roo.log(box);
8428             var zIndex = Roo.bootstrap.Modal.zIndex++;
8429
8430             
8431             this.maskEl.top.setStyle('position', 'absolute');
8432             this.maskEl.top.setStyle('z-index', zIndex);
8433             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8434             this.maskEl.top.setLeft(0);
8435             this.maskEl.top.setTop(0);
8436             this.maskEl.top.show();
8437             
8438             this.maskEl.left.setStyle('position', 'absolute');
8439             this.maskEl.left.setStyle('z-index', zIndex);
8440             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8441             this.maskEl.left.setLeft(0);
8442             this.maskEl.left.setTop(box.y - this.padding);
8443             this.maskEl.left.show();
8444
8445             this.maskEl.bottom.setStyle('position', 'absolute');
8446             this.maskEl.bottom.setStyle('z-index', zIndex);
8447             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8448             this.maskEl.bottom.setLeft(0);
8449             this.maskEl.bottom.setTop(box.bottom + this.padding);
8450             this.maskEl.bottom.show();
8451
8452             this.maskEl.right.setStyle('position', 'absolute');
8453             this.maskEl.right.setStyle('z-index', zIndex);
8454             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8455             this.maskEl.right.setLeft(box.right + this.padding);
8456             this.maskEl.right.setTop(box.y - this.padding);
8457             this.maskEl.right.show();
8458
8459             this.toolTip.bindEl = this.target.el;
8460
8461             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8462
8463             var tip = this.target.blankText;
8464
8465             if(this.target.getValue() !== '' ) {
8466                 
8467                 if (this.target.invalidText.length) {
8468                     tip = this.target.invalidText;
8469                 } else if (this.target.regexText.length){
8470                     tip = this.target.regexText;
8471                 }
8472             }
8473
8474             this.toolTip.show(tip);
8475
8476             this.intervalID = window.setInterval(function() {
8477                 Roo.bootstrap.Form.popover.unmask();
8478             }, 10000);
8479
8480             window.onwheel = function(){ return false;};
8481             
8482             (function(){ this.isMasked = true; }).defer(500, this);
8483             
8484         },
8485         
8486         unmask : function()
8487         {
8488             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8489                 return;
8490             }
8491             
8492             this.maskEl.top.setStyle('position', 'absolute');
8493             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8494             this.maskEl.top.hide();
8495
8496             this.maskEl.left.setStyle('position', 'absolute');
8497             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8498             this.maskEl.left.hide();
8499
8500             this.maskEl.bottom.setStyle('position', 'absolute');
8501             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8502             this.maskEl.bottom.hide();
8503
8504             this.maskEl.right.setStyle('position', 'absolute');
8505             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8506             this.maskEl.right.hide();
8507             
8508             this.toolTip.hide();
8509             
8510             this.toolTip.el.hide();
8511             
8512             window.onwheel = function(){ return true;};
8513             
8514             if(this.intervalID){
8515                 window.clearInterval(this.intervalID);
8516                 this.intervalID = false;
8517             }
8518             
8519             this.isMasked = false;
8520             
8521         }
8522         
8523     }
8524     
8525 });
8526
8527 /*
8528  * Based on:
8529  * Ext JS Library 1.1.1
8530  * Copyright(c) 2006-2007, Ext JS, LLC.
8531  *
8532  * Originally Released Under LGPL - original licence link has changed is not relivant.
8533  *
8534  * Fork - LGPL
8535  * <script type="text/javascript">
8536  */
8537 /**
8538  * @class Roo.form.VTypes
8539  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8540  * @singleton
8541  */
8542 Roo.form.VTypes = function(){
8543     // closure these in so they are only created once.
8544     var alpha = /^[a-zA-Z_]+$/;
8545     var alphanum = /^[a-zA-Z0-9_]+$/;
8546     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8547     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8548
8549     // All these messages and functions are configurable
8550     return {
8551         /**
8552          * The function used to validate email addresses
8553          * @param {String} value The email address
8554          */
8555         'email' : function(v){
8556             return email.test(v);
8557         },
8558         /**
8559          * The error text to display when the email validation function returns false
8560          * @type String
8561          */
8562         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8563         /**
8564          * The keystroke filter mask to be applied on email input
8565          * @type RegExp
8566          */
8567         'emailMask' : /[a-z0-9_\.\-@]/i,
8568
8569         /**
8570          * The function used to validate URLs
8571          * @param {String} value The URL
8572          */
8573         'url' : function(v){
8574             return url.test(v);
8575         },
8576         /**
8577          * The error text to display when the url validation function returns false
8578          * @type String
8579          */
8580         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8581         
8582         /**
8583          * The function used to validate alpha values
8584          * @param {String} value The value
8585          */
8586         'alpha' : function(v){
8587             return alpha.test(v);
8588         },
8589         /**
8590          * The error text to display when the alpha validation function returns false
8591          * @type String
8592          */
8593         'alphaText' : 'This field should only contain letters and _',
8594         /**
8595          * The keystroke filter mask to be applied on alpha input
8596          * @type RegExp
8597          */
8598         'alphaMask' : /[a-z_]/i,
8599
8600         /**
8601          * The function used to validate alphanumeric values
8602          * @param {String} value The value
8603          */
8604         'alphanum' : function(v){
8605             return alphanum.test(v);
8606         },
8607         /**
8608          * The error text to display when the alphanumeric validation function returns false
8609          * @type String
8610          */
8611         'alphanumText' : 'This field should only contain letters, numbers and _',
8612         /**
8613          * The keystroke filter mask to be applied on alphanumeric input
8614          * @type RegExp
8615          */
8616         'alphanumMask' : /[a-z0-9_]/i
8617     };
8618 }();/*
8619  * - LGPL
8620  *
8621  * Input
8622  * 
8623  */
8624
8625 /**
8626  * @class Roo.bootstrap.Input
8627  * @extends Roo.bootstrap.Component
8628  * Bootstrap Input class
8629  * @cfg {Boolean} disabled is it disabled
8630  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8631  * @cfg {String} name name of the input
8632  * @cfg {string} fieldLabel - the label associated
8633  * @cfg {string} placeholder - placeholder to put in text.
8634  * @cfg {string}  before - input group add on before
8635  * @cfg {string} after - input group add on after
8636  * @cfg {string} size - (lg|sm) or leave empty..
8637  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8638  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8639  * @cfg {Number} md colspan out of 12 for computer-sized screens
8640  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8641  * @cfg {string} value default value of the input
8642  * @cfg {Number} labelWidth set the width of label 
8643  * @cfg {Number} labellg set the width of label (1-12)
8644  * @cfg {Number} labelmd set the width of label (1-12)
8645  * @cfg {Number} labelsm set the width of label (1-12)
8646  * @cfg {Number} labelxs set the width of label (1-12)
8647  * @cfg {String} labelAlign (top|left)
8648  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8649  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8650  * @cfg {String} indicatorpos (left|right) default left
8651  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8652  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8653
8654  * @cfg {String} align (left|center|right) Default left
8655  * @cfg {Boolean} forceFeedback (true|false) Default false
8656  * 
8657  * @constructor
8658  * Create a new Input
8659  * @param {Object} config The config object
8660  */
8661
8662 Roo.bootstrap.Input = function(config){
8663     
8664     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8665     
8666     this.addEvents({
8667         /**
8668          * @event focus
8669          * Fires when this field receives input focus.
8670          * @param {Roo.form.Field} this
8671          */
8672         focus : true,
8673         /**
8674          * @event blur
8675          * Fires when this field loses input focus.
8676          * @param {Roo.form.Field} this
8677          */
8678         blur : true,
8679         /**
8680          * @event specialkey
8681          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8682          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8683          * @param {Roo.form.Field} this
8684          * @param {Roo.EventObject} e The event object
8685          */
8686         specialkey : true,
8687         /**
8688          * @event change
8689          * Fires just before the field blurs if the field value has changed.
8690          * @param {Roo.form.Field} this
8691          * @param {Mixed} newValue The new value
8692          * @param {Mixed} oldValue The original value
8693          */
8694         change : true,
8695         /**
8696          * @event invalid
8697          * Fires after the field has been marked as invalid.
8698          * @param {Roo.form.Field} this
8699          * @param {String} msg The validation message
8700          */
8701         invalid : true,
8702         /**
8703          * @event valid
8704          * Fires after the field has been validated with no errors.
8705          * @param {Roo.form.Field} this
8706          */
8707         valid : true,
8708          /**
8709          * @event keyup
8710          * Fires after the key up
8711          * @param {Roo.form.Field} this
8712          * @param {Roo.EventObject}  e The event Object
8713          */
8714         keyup : true
8715     });
8716 };
8717
8718 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8719      /**
8720      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8721       automatic validation (defaults to "keyup").
8722      */
8723     validationEvent : "keyup",
8724      /**
8725      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8726      */
8727     validateOnBlur : true,
8728     /**
8729      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8730      */
8731     validationDelay : 250,
8732      /**
8733      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8734      */
8735     focusClass : "x-form-focus",  // not needed???
8736     
8737        
8738     /**
8739      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8740      */
8741     invalidClass : "has-warning",
8742     
8743     /**
8744      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8745      */
8746     validClass : "has-success",
8747     
8748     /**
8749      * @cfg {Boolean} hasFeedback (true|false) default true
8750      */
8751     hasFeedback : true,
8752     
8753     /**
8754      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8755      */
8756     invalidFeedbackClass : "glyphicon-warning-sign",
8757     
8758     /**
8759      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8760      */
8761     validFeedbackClass : "glyphicon-ok",
8762     
8763     /**
8764      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8765      */
8766     selectOnFocus : false,
8767     
8768      /**
8769      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8770      */
8771     maskRe : null,
8772        /**
8773      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8774      */
8775     vtype : null,
8776     
8777       /**
8778      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8779      */
8780     disableKeyFilter : false,
8781     
8782        /**
8783      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8784      */
8785     disabled : false,
8786      /**
8787      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8788      */
8789     allowBlank : true,
8790     /**
8791      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8792      */
8793     blankText : "Please complete this mandatory field",
8794     
8795      /**
8796      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8797      */
8798     minLength : 0,
8799     /**
8800      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8801      */
8802     maxLength : Number.MAX_VALUE,
8803     /**
8804      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8805      */
8806     minLengthText : "The minimum length for this field is {0}",
8807     /**
8808      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8809      */
8810     maxLengthText : "The maximum length for this field is {0}",
8811   
8812     
8813     /**
8814      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8815      * If available, this function will be called only after the basic validators all return true, and will be passed the
8816      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8817      */
8818     validator : null,
8819     /**
8820      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8821      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8822      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8823      */
8824     regex : null,
8825     /**
8826      * @cfg {String} regexText -- Depricated - use Invalid Text
8827      */
8828     regexText : "",
8829     
8830     /**
8831      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8832      */
8833     invalidText : "",
8834     
8835     
8836     
8837     autocomplete: false,
8838     
8839     
8840     fieldLabel : '',
8841     inputType : 'text',
8842     
8843     name : false,
8844     placeholder: false,
8845     before : false,
8846     after : false,
8847     size : false,
8848     hasFocus : false,
8849     preventMark: false,
8850     isFormField : true,
8851     value : '',
8852     labelWidth : 2,
8853     labelAlign : false,
8854     readOnly : false,
8855     align : false,
8856     formatedValue : false,
8857     forceFeedback : false,
8858     
8859     indicatorpos : 'left',
8860     
8861     labellg : 0,
8862     labelmd : 0,
8863     labelsm : 0,
8864     labelxs : 0,
8865     
8866     capture : '',
8867     accept : '',
8868     
8869     parentLabelAlign : function()
8870     {
8871         var parent = this;
8872         while (parent.parent()) {
8873             parent = parent.parent();
8874             if (typeof(parent.labelAlign) !='undefined') {
8875                 return parent.labelAlign;
8876             }
8877         }
8878         return 'left';
8879         
8880     },
8881     
8882     getAutoCreate : function()
8883     {
8884         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8885         
8886         var id = Roo.id();
8887         
8888         var cfg = {};
8889         
8890         if(this.inputType != 'hidden'){
8891             cfg.cls = 'form-group' //input-group
8892         }
8893         
8894         var input =  {
8895             tag: 'input',
8896             id : id,
8897             type : this.inputType,
8898             value : this.value,
8899             cls : 'form-control',
8900             placeholder : this.placeholder || '',
8901             autocomplete : this.autocomplete || 'new-password'
8902         };
8903         
8904         if(this.capture.length){
8905             input.capture = this.capture;
8906         }
8907         
8908         if(this.accept.length){
8909             input.accept = this.accept + "/*";
8910         }
8911         
8912         if(this.align){
8913             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8914         }
8915         
8916         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8917             input.maxLength = this.maxLength;
8918         }
8919         
8920         if (this.disabled) {
8921             input.disabled=true;
8922         }
8923         
8924         if (this.readOnly) {
8925             input.readonly=true;
8926         }
8927         
8928         if (this.name) {
8929             input.name = this.name;
8930         }
8931         
8932         if (this.size) {
8933             input.cls += ' input-' + this.size;
8934         }
8935         
8936         var settings=this;
8937         ['xs','sm','md','lg'].map(function(size){
8938             if (settings[size]) {
8939                 cfg.cls += ' col-' + size + '-' + settings[size];
8940             }
8941         });
8942         
8943         var inputblock = input;
8944         
8945         var feedback = {
8946             tag: 'span',
8947             cls: 'glyphicon form-control-feedback'
8948         };
8949             
8950         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8951             
8952             inputblock = {
8953                 cls : 'has-feedback',
8954                 cn :  [
8955                     input,
8956                     feedback
8957                 ] 
8958             };  
8959         }
8960         
8961         if (this.before || this.after) {
8962             
8963             inputblock = {
8964                 cls : 'input-group',
8965                 cn :  [] 
8966             };
8967             
8968             if (this.before && typeof(this.before) == 'string') {
8969                 
8970                 inputblock.cn.push({
8971                     tag :'span',
8972                     cls : 'roo-input-before input-group-addon',
8973                     html : this.before
8974                 });
8975             }
8976             if (this.before && typeof(this.before) == 'object') {
8977                 this.before = Roo.factory(this.before);
8978                 
8979                 inputblock.cn.push({
8980                     tag :'span',
8981                     cls : 'roo-input-before input-group-' +
8982                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8983                 });
8984             }
8985             
8986             inputblock.cn.push(input);
8987             
8988             if (this.after && typeof(this.after) == 'string') {
8989                 inputblock.cn.push({
8990                     tag :'span',
8991                     cls : 'roo-input-after input-group-addon',
8992                     html : this.after
8993                 });
8994             }
8995             if (this.after && typeof(this.after) == 'object') {
8996                 this.after = Roo.factory(this.after);
8997                 
8998                 inputblock.cn.push({
8999                     tag :'span',
9000                     cls : 'roo-input-after input-group-' +
9001                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9002                 });
9003             }
9004             
9005             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9006                 inputblock.cls += ' has-feedback';
9007                 inputblock.cn.push(feedback);
9008             }
9009         };
9010         
9011         if (align ==='left' && this.fieldLabel.length) {
9012             
9013             cfg.cls += ' roo-form-group-label-left';
9014             
9015             cfg.cn = [
9016                 {
9017                     tag : 'i',
9018                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9019                     tooltip : 'This field is required'
9020                 },
9021                 {
9022                     tag: 'label',
9023                     'for' :  id,
9024                     cls : 'control-label',
9025                     html : this.fieldLabel
9026
9027                 },
9028                 {
9029                     cls : "", 
9030                     cn: [
9031                         inputblock
9032                     ]
9033                 }
9034             ];
9035             
9036             var labelCfg = cfg.cn[1];
9037             var contentCfg = cfg.cn[2];
9038             
9039             if(this.indicatorpos == 'right'){
9040                 cfg.cn = [
9041                     {
9042                         tag: 'label',
9043                         'for' :  id,
9044                         cls : 'control-label',
9045                         cn : [
9046                             {
9047                                 tag : 'span',
9048                                 html : this.fieldLabel
9049                             },
9050                             {
9051                                 tag : 'i',
9052                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9053                                 tooltip : 'This field is required'
9054                             }
9055                         ]
9056                     },
9057                     {
9058                         cls : "",
9059                         cn: [
9060                             inputblock
9061                         ]
9062                     }
9063
9064                 ];
9065                 
9066                 labelCfg = cfg.cn[0];
9067                 contentCfg = cfg.cn[1];
9068             
9069             }
9070             
9071             if(this.labelWidth > 12){
9072                 labelCfg.style = "width: " + this.labelWidth + 'px';
9073             }
9074             
9075             if(this.labelWidth < 13 && this.labelmd == 0){
9076                 this.labelmd = this.labelWidth;
9077             }
9078             
9079             if(this.labellg > 0){
9080                 labelCfg.cls += ' col-lg-' + this.labellg;
9081                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9082             }
9083             
9084             if(this.labelmd > 0){
9085                 labelCfg.cls += ' col-md-' + this.labelmd;
9086                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9087             }
9088             
9089             if(this.labelsm > 0){
9090                 labelCfg.cls += ' col-sm-' + this.labelsm;
9091                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9092             }
9093             
9094             if(this.labelxs > 0){
9095                 labelCfg.cls += ' col-xs-' + this.labelxs;
9096                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9097             }
9098             
9099             
9100         } else if ( this.fieldLabel.length) {
9101                 
9102             cfg.cn = [
9103                 {
9104                     tag : 'i',
9105                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9106                     tooltip : 'This field is required'
9107                 },
9108                 {
9109                     tag: 'label',
9110                    //cls : 'input-group-addon',
9111                     html : this.fieldLabel
9112
9113                 },
9114
9115                inputblock
9116
9117            ];
9118            
9119            if(this.indicatorpos == 'right'){
9120                 
9121                 cfg.cn = [
9122                     {
9123                         tag: 'label',
9124                        //cls : 'input-group-addon',
9125                         html : this.fieldLabel
9126
9127                     },
9128                     {
9129                         tag : 'i',
9130                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9131                         tooltip : 'This field is required'
9132                     },
9133
9134                    inputblock
9135
9136                ];
9137
9138             }
9139
9140         } else {
9141             
9142             cfg.cn = [
9143
9144                     inputblock
9145
9146             ];
9147                 
9148                 
9149         };
9150         
9151         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9152            cfg.cls += ' navbar-form';
9153         }
9154         
9155         if (this.parentType === 'NavGroup') {
9156            cfg.cls += ' navbar-form';
9157            cfg.tag = 'li';
9158         }
9159         
9160         return cfg;
9161         
9162     },
9163     /**
9164      * return the real input element.
9165      */
9166     inputEl: function ()
9167     {
9168         return this.el.select('input.form-control',true).first();
9169     },
9170     
9171     tooltipEl : function()
9172     {
9173         return this.inputEl();
9174     },
9175     
9176     indicatorEl : function()
9177     {
9178         var indicator = this.el.select('i.roo-required-indicator',true).first();
9179         
9180         if(!indicator){
9181             return false;
9182         }
9183         
9184         return indicator;
9185         
9186     },
9187     
9188     setDisabled : function(v)
9189     {
9190         var i  = this.inputEl().dom;
9191         if (!v) {
9192             i.removeAttribute('disabled');
9193             return;
9194             
9195         }
9196         i.setAttribute('disabled','true');
9197     },
9198     initEvents : function()
9199     {
9200           
9201         this.inputEl().on("keydown" , this.fireKey,  this);
9202         this.inputEl().on("focus", this.onFocus,  this);
9203         this.inputEl().on("blur", this.onBlur,  this);
9204         
9205         this.inputEl().relayEvent('keyup', this);
9206         
9207         this.indicator = this.indicatorEl();
9208         
9209         if(this.indicator){
9210             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9211         }
9212  
9213         // reference to original value for reset
9214         this.originalValue = this.getValue();
9215         //Roo.form.TextField.superclass.initEvents.call(this);
9216         if(this.validationEvent == 'keyup'){
9217             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9218             this.inputEl().on('keyup', this.filterValidation, this);
9219         }
9220         else if(this.validationEvent !== false){
9221             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9222         }
9223         
9224         if(this.selectOnFocus){
9225             this.on("focus", this.preFocus, this);
9226             
9227         }
9228         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9229             this.inputEl().on("keypress", this.filterKeys, this);
9230         } else {
9231             this.inputEl().relayEvent('keypress', this);
9232         }
9233        /* if(this.grow){
9234             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9235             this.el.on("click", this.autoSize,  this);
9236         }
9237         */
9238         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9239             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9240         }
9241         
9242         if (typeof(this.before) == 'object') {
9243             this.before.render(this.el.select('.roo-input-before',true).first());
9244         }
9245         if (typeof(this.after) == 'object') {
9246             this.after.render(this.el.select('.roo-input-after',true).first());
9247         }
9248         
9249         this.inputEl().on('change', this.onChange, this);
9250         
9251     },
9252     filterValidation : function(e){
9253         if(!e.isNavKeyPress()){
9254             this.validationTask.delay(this.validationDelay);
9255         }
9256     },
9257      /**
9258      * Validates the field value
9259      * @return {Boolean} True if the value is valid, else false
9260      */
9261     validate : function(){
9262         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9263         if(this.disabled || this.validateValue(this.getRawValue())){
9264             this.markValid();
9265             return true;
9266         }
9267         
9268         this.markInvalid();
9269         return false;
9270     },
9271     
9272     
9273     /**
9274      * Validates a value according to the field's validation rules and marks the field as invalid
9275      * if the validation fails
9276      * @param {Mixed} value The value to validate
9277      * @return {Boolean} True if the value is valid, else false
9278      */
9279     validateValue : function(value)
9280     {
9281         if(this.getVisibilityEl().hasClass('hidden')){
9282             return true;
9283         }
9284         
9285         if(value.length < 1)  { // if it's blank
9286             if(this.allowBlank){
9287                 return true;
9288             }
9289             return false;
9290         }
9291         
9292         if(value.length < this.minLength){
9293             return false;
9294         }
9295         if(value.length > this.maxLength){
9296             return false;
9297         }
9298         if(this.vtype){
9299             var vt = Roo.form.VTypes;
9300             if(!vt[this.vtype](value, this)){
9301                 return false;
9302             }
9303         }
9304         if(typeof this.validator == "function"){
9305             var msg = this.validator(value);
9306             if(msg !== true){
9307                 return false;
9308             }
9309             if (typeof(msg) == 'string') {
9310                 this.invalidText = msg;
9311             }
9312         }
9313         
9314         if(this.regex && !this.regex.test(value)){
9315             return false;
9316         }
9317         
9318         return true;
9319     },
9320     
9321      // private
9322     fireKey : function(e){
9323         //Roo.log('field ' + e.getKey());
9324         if(e.isNavKeyPress()){
9325             this.fireEvent("specialkey", this, e);
9326         }
9327     },
9328     focus : function (selectText){
9329         if(this.rendered){
9330             this.inputEl().focus();
9331             if(selectText === true){
9332                 this.inputEl().dom.select();
9333             }
9334         }
9335         return this;
9336     } ,
9337     
9338     onFocus : function(){
9339         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9340            // this.el.addClass(this.focusClass);
9341         }
9342         if(!this.hasFocus){
9343             this.hasFocus = true;
9344             this.startValue = this.getValue();
9345             this.fireEvent("focus", this);
9346         }
9347     },
9348     
9349     beforeBlur : Roo.emptyFn,
9350
9351     
9352     // private
9353     onBlur : function(){
9354         this.beforeBlur();
9355         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9356             //this.el.removeClass(this.focusClass);
9357         }
9358         this.hasFocus = false;
9359         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9360             this.validate();
9361         }
9362         var v = this.getValue();
9363         if(String(v) !== String(this.startValue)){
9364             this.fireEvent('change', this, v, this.startValue);
9365         }
9366         this.fireEvent("blur", this);
9367     },
9368     
9369     onChange : function(e)
9370     {
9371         var v = this.getValue();
9372         if(String(v) !== String(this.startValue)){
9373             this.fireEvent('change', this, v, this.startValue);
9374         }
9375         
9376     },
9377     
9378     /**
9379      * Resets the current field value to the originally loaded value and clears any validation messages
9380      */
9381     reset : function(){
9382         this.setValue(this.originalValue);
9383         this.validate();
9384     },
9385      /**
9386      * Returns the name of the field
9387      * @return {Mixed} name The name field
9388      */
9389     getName: function(){
9390         return this.name;
9391     },
9392      /**
9393      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9394      * @return {Mixed} value The field value
9395      */
9396     getValue : function(){
9397         
9398         var v = this.inputEl().getValue();
9399         
9400         return v;
9401     },
9402     /**
9403      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9404      * @return {Mixed} value The field value
9405      */
9406     getRawValue : function(){
9407         var v = this.inputEl().getValue();
9408         
9409         return v;
9410     },
9411     
9412     /**
9413      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9414      * @param {Mixed} value The value to set
9415      */
9416     setRawValue : function(v){
9417         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9418     },
9419     
9420     selectText : function(start, end){
9421         var v = this.getRawValue();
9422         if(v.length > 0){
9423             start = start === undefined ? 0 : start;
9424             end = end === undefined ? v.length : end;
9425             var d = this.inputEl().dom;
9426             if(d.setSelectionRange){
9427                 d.setSelectionRange(start, end);
9428             }else if(d.createTextRange){
9429                 var range = d.createTextRange();
9430                 range.moveStart("character", start);
9431                 range.moveEnd("character", v.length-end);
9432                 range.select();
9433             }
9434         }
9435     },
9436     
9437     /**
9438      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9439      * @param {Mixed} value The value to set
9440      */
9441     setValue : function(v){
9442         this.value = v;
9443         if(this.rendered){
9444             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9445             this.validate();
9446         }
9447     },
9448     
9449     /*
9450     processValue : function(value){
9451         if(this.stripCharsRe){
9452             var newValue = value.replace(this.stripCharsRe, '');
9453             if(newValue !== value){
9454                 this.setRawValue(newValue);
9455                 return newValue;
9456             }
9457         }
9458         return value;
9459     },
9460   */
9461     preFocus : function(){
9462         
9463         if(this.selectOnFocus){
9464             this.inputEl().dom.select();
9465         }
9466     },
9467     filterKeys : function(e){
9468         var k = e.getKey();
9469         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9470             return;
9471         }
9472         var c = e.getCharCode(), cc = String.fromCharCode(c);
9473         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9474             return;
9475         }
9476         if(!this.maskRe.test(cc)){
9477             e.stopEvent();
9478         }
9479     },
9480      /**
9481      * Clear any invalid styles/messages for this field
9482      */
9483     clearInvalid : function(){
9484         
9485         if(!this.el || this.preventMark){ // not rendered
9486             return;
9487         }
9488         
9489      
9490         this.el.removeClass(this.invalidClass);
9491         
9492         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9493             
9494             var feedback = this.el.select('.form-control-feedback', true).first();
9495             
9496             if(feedback){
9497                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9498             }
9499             
9500         }
9501         
9502         if(this.indicator){
9503             this.indicator.removeClass('visible');
9504             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9505         }
9506         
9507         this.fireEvent('valid', this);
9508     },
9509     
9510      /**
9511      * Mark this field as valid
9512      */
9513     markValid : function()
9514     {
9515         if(!this.el  || this.preventMark){ // not rendered...
9516             return;
9517         }
9518         
9519         this.el.removeClass([this.invalidClass, this.validClass]);
9520         
9521         var feedback = this.el.select('.form-control-feedback', true).first();
9522             
9523         if(feedback){
9524             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9525         }
9526         
9527         if(this.indicator){
9528             this.indicator.removeClass('visible');
9529             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9530         }
9531         
9532         if(this.disabled){
9533             return;
9534         }
9535         
9536         if(this.allowBlank && !this.getRawValue().length){
9537             return;
9538         }
9539         
9540         this.el.addClass(this.validClass);
9541         
9542         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9543             
9544             var feedback = this.el.select('.form-control-feedback', true).first();
9545             
9546             if(feedback){
9547                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9548                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9549             }
9550             
9551         }
9552         
9553         this.fireEvent('valid', this);
9554     },
9555     
9556      /**
9557      * Mark this field as invalid
9558      * @param {String} msg The validation message
9559      */
9560     markInvalid : function(msg)
9561     {
9562         if(!this.el  || this.preventMark){ // not rendered
9563             return;
9564         }
9565         
9566         this.el.removeClass([this.invalidClass, this.validClass]);
9567         
9568         var feedback = this.el.select('.form-control-feedback', true).first();
9569             
9570         if(feedback){
9571             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9572         }
9573
9574         if(this.disabled){
9575             return;
9576         }
9577         
9578         if(this.allowBlank && !this.getRawValue().length){
9579             return;
9580         }
9581         
9582         if(this.indicator){
9583             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9584             this.indicator.addClass('visible');
9585         }
9586         
9587         this.el.addClass(this.invalidClass);
9588         
9589         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9590             
9591             var feedback = this.el.select('.form-control-feedback', true).first();
9592             
9593             if(feedback){
9594                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9595                 
9596                 if(this.getValue().length || this.forceFeedback){
9597                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9598                 }
9599                 
9600             }
9601             
9602         }
9603         
9604         this.fireEvent('invalid', this, msg);
9605     },
9606     // private
9607     SafariOnKeyDown : function(event)
9608     {
9609         // this is a workaround for a password hang bug on chrome/ webkit.
9610         if (this.inputEl().dom.type != 'password') {
9611             return;
9612         }
9613         
9614         var isSelectAll = false;
9615         
9616         if(this.inputEl().dom.selectionEnd > 0){
9617             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9618         }
9619         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9620             event.preventDefault();
9621             this.setValue('');
9622             return;
9623         }
9624         
9625         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9626             
9627             event.preventDefault();
9628             // this is very hacky as keydown always get's upper case.
9629             //
9630             var cc = String.fromCharCode(event.getCharCode());
9631             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9632             
9633         }
9634     },
9635     adjustWidth : function(tag, w){
9636         tag = tag.toLowerCase();
9637         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9638             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9639                 if(tag == 'input'){
9640                     return w + 2;
9641                 }
9642                 if(tag == 'textarea'){
9643                     return w-2;
9644                 }
9645             }else if(Roo.isOpera){
9646                 if(tag == 'input'){
9647                     return w + 2;
9648                 }
9649                 if(tag == 'textarea'){
9650                     return w-2;
9651                 }
9652             }
9653         }
9654         return w;
9655     },
9656     
9657     setFieldLabel : function(v)
9658     {
9659         if(!this.rendered){
9660             return;
9661         }
9662         
9663         if(this.indicator){
9664             var ar = this.el.select('label > span',true);
9665             
9666             if (ar.elements.length) {
9667                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9668                 this.fieldLabel = v;
9669                 return;
9670             }
9671             
9672             var br = this.el.select('label',true);
9673             
9674             if(br.elements.length) {
9675                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9676                 this.fieldLabel = v;
9677                 return;
9678             }
9679             
9680             Roo.log('Cannot Found any of label > span || label in input');
9681             return;
9682         }
9683         
9684         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9685         this.fieldLabel = v;
9686         
9687         
9688     }
9689 });
9690
9691  
9692 /*
9693  * - LGPL
9694  *
9695  * Input
9696  * 
9697  */
9698
9699 /**
9700  * @class Roo.bootstrap.TextArea
9701  * @extends Roo.bootstrap.Input
9702  * Bootstrap TextArea class
9703  * @cfg {Number} cols Specifies the visible width of a text area
9704  * @cfg {Number} rows Specifies the visible number of lines in a text area
9705  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9706  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9707  * @cfg {string} html text
9708  * 
9709  * @constructor
9710  * Create a new TextArea
9711  * @param {Object} config The config object
9712  */
9713
9714 Roo.bootstrap.TextArea = function(config){
9715     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9716    
9717 };
9718
9719 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9720      
9721     cols : false,
9722     rows : 5,
9723     readOnly : false,
9724     warp : 'soft',
9725     resize : false,
9726     value: false,
9727     html: false,
9728     
9729     getAutoCreate : function(){
9730         
9731         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9732         
9733         var id = Roo.id();
9734         
9735         var cfg = {};
9736         
9737         if(this.inputType != 'hidden'){
9738             cfg.cls = 'form-group' //input-group
9739         }
9740         
9741         var input =  {
9742             tag: 'textarea',
9743             id : id,
9744             warp : this.warp,
9745             rows : this.rows,
9746             value : this.value || '',
9747             html: this.html || '',
9748             cls : 'form-control',
9749             placeholder : this.placeholder || '' 
9750             
9751         };
9752         
9753         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9754             input.maxLength = this.maxLength;
9755         }
9756         
9757         if(this.resize){
9758             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9759         }
9760         
9761         if(this.cols){
9762             input.cols = this.cols;
9763         }
9764         
9765         if (this.readOnly) {
9766             input.readonly = true;
9767         }
9768         
9769         if (this.name) {
9770             input.name = this.name;
9771         }
9772         
9773         if (this.size) {
9774             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9775         }
9776         
9777         var settings=this;
9778         ['xs','sm','md','lg'].map(function(size){
9779             if (settings[size]) {
9780                 cfg.cls += ' col-' + size + '-' + settings[size];
9781             }
9782         });
9783         
9784         var inputblock = input;
9785         
9786         if(this.hasFeedback && !this.allowBlank){
9787             
9788             var feedback = {
9789                 tag: 'span',
9790                 cls: 'glyphicon form-control-feedback'
9791             };
9792
9793             inputblock = {
9794                 cls : 'has-feedback',
9795                 cn :  [
9796                     input,
9797                     feedback
9798                 ] 
9799             };  
9800         }
9801         
9802         
9803         if (this.before || this.after) {
9804             
9805             inputblock = {
9806                 cls : 'input-group',
9807                 cn :  [] 
9808             };
9809             if (this.before) {
9810                 inputblock.cn.push({
9811                     tag :'span',
9812                     cls : 'input-group-addon',
9813                     html : this.before
9814                 });
9815             }
9816             
9817             inputblock.cn.push(input);
9818             
9819             if(this.hasFeedback && !this.allowBlank){
9820                 inputblock.cls += ' has-feedback';
9821                 inputblock.cn.push(feedback);
9822             }
9823             
9824             if (this.after) {
9825                 inputblock.cn.push({
9826                     tag :'span',
9827                     cls : 'input-group-addon',
9828                     html : this.after
9829                 });
9830             }
9831             
9832         }
9833         
9834         if (align ==='left' && this.fieldLabel.length) {
9835             cfg.cn = [
9836                 {
9837                     tag: 'label',
9838                     'for' :  id,
9839                     cls : 'control-label',
9840                     html : this.fieldLabel
9841                 },
9842                 {
9843                     cls : "",
9844                     cn: [
9845                         inputblock
9846                     ]
9847                 }
9848
9849             ];
9850             
9851             if(this.labelWidth > 12){
9852                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9853             }
9854
9855             if(this.labelWidth < 13 && this.labelmd == 0){
9856                 this.labelmd = this.labelWidth;
9857             }
9858
9859             if(this.labellg > 0){
9860                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9861                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9862             }
9863
9864             if(this.labelmd > 0){
9865                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9866                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9867             }
9868
9869             if(this.labelsm > 0){
9870                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9871                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9872             }
9873
9874             if(this.labelxs > 0){
9875                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9876                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9877             }
9878             
9879         } else if ( this.fieldLabel.length) {
9880             cfg.cn = [
9881
9882                {
9883                    tag: 'label',
9884                    //cls : 'input-group-addon',
9885                    html : this.fieldLabel
9886
9887                },
9888
9889                inputblock
9890
9891            ];
9892
9893         } else {
9894
9895             cfg.cn = [
9896
9897                 inputblock
9898
9899             ];
9900                 
9901         }
9902         
9903         if (this.disabled) {
9904             input.disabled=true;
9905         }
9906         
9907         return cfg;
9908         
9909     },
9910     /**
9911      * return the real textarea element.
9912      */
9913     inputEl: function ()
9914     {
9915         return this.el.select('textarea.form-control',true).first();
9916     },
9917     
9918     /**
9919      * Clear any invalid styles/messages for this field
9920      */
9921     clearInvalid : function()
9922     {
9923         
9924         if(!this.el || this.preventMark){ // not rendered
9925             return;
9926         }
9927         
9928         var label = this.el.select('label', true).first();
9929         var icon = this.el.select('i.fa-star', true).first();
9930         
9931         if(label && icon){
9932             icon.remove();
9933         }
9934         
9935         this.el.removeClass(this.invalidClass);
9936         
9937         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9938             
9939             var feedback = this.el.select('.form-control-feedback', true).first();
9940             
9941             if(feedback){
9942                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9943             }
9944             
9945         }
9946         
9947         this.fireEvent('valid', this);
9948     },
9949     
9950      /**
9951      * Mark this field as valid
9952      */
9953     markValid : function()
9954     {
9955         if(!this.el  || this.preventMark){ // not rendered
9956             return;
9957         }
9958         
9959         this.el.removeClass([this.invalidClass, this.validClass]);
9960         
9961         var feedback = this.el.select('.form-control-feedback', true).first();
9962             
9963         if(feedback){
9964             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9965         }
9966
9967         if(this.disabled || this.allowBlank){
9968             return;
9969         }
9970         
9971         var label = this.el.select('label', true).first();
9972         var icon = this.el.select('i.fa-star', true).first();
9973         
9974         if(label && icon){
9975             icon.remove();
9976         }
9977         
9978         this.el.addClass(this.validClass);
9979         
9980         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9981             
9982             var feedback = this.el.select('.form-control-feedback', true).first();
9983             
9984             if(feedback){
9985                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9986                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9987             }
9988             
9989         }
9990         
9991         this.fireEvent('valid', this);
9992     },
9993     
9994      /**
9995      * Mark this field as invalid
9996      * @param {String} msg The validation message
9997      */
9998     markInvalid : function(msg)
9999     {
10000         if(!this.el  || this.preventMark){ // not rendered
10001             return;
10002         }
10003         
10004         this.el.removeClass([this.invalidClass, this.validClass]);
10005         
10006         var feedback = this.el.select('.form-control-feedback', true).first();
10007             
10008         if(feedback){
10009             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10010         }
10011
10012         if(this.disabled || this.allowBlank){
10013             return;
10014         }
10015         
10016         var label = this.el.select('label', true).first();
10017         var icon = this.el.select('i.fa-star', true).first();
10018         
10019         if(!this.getValue().length && label && !icon){
10020             this.el.createChild({
10021                 tag : 'i',
10022                 cls : 'text-danger fa fa-lg fa-star',
10023                 tooltip : 'This field is required',
10024                 style : 'margin-right:5px;'
10025             }, label, true);
10026         }
10027
10028         this.el.addClass(this.invalidClass);
10029         
10030         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10031             
10032             var feedback = this.el.select('.form-control-feedback', true).first();
10033             
10034             if(feedback){
10035                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10036                 
10037                 if(this.getValue().length || this.forceFeedback){
10038                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10039                 }
10040                 
10041             }
10042             
10043         }
10044         
10045         this.fireEvent('invalid', this, msg);
10046     }
10047 });
10048
10049  
10050 /*
10051  * - LGPL
10052  *
10053  * trigger field - base class for combo..
10054  * 
10055  */
10056  
10057 /**
10058  * @class Roo.bootstrap.TriggerField
10059  * @extends Roo.bootstrap.Input
10060  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10061  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10062  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10063  * for which you can provide a custom implementation.  For example:
10064  * <pre><code>
10065 var trigger = new Roo.bootstrap.TriggerField();
10066 trigger.onTriggerClick = myTriggerFn;
10067 trigger.applyTo('my-field');
10068 </code></pre>
10069  *
10070  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10071  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10072  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10073  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10074  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10075
10076  * @constructor
10077  * Create a new TriggerField.
10078  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10079  * to the base TextField)
10080  */
10081 Roo.bootstrap.TriggerField = function(config){
10082     this.mimicing = false;
10083     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10084 };
10085
10086 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10087     /**
10088      * @cfg {String} triggerClass A CSS class to apply to the trigger
10089      */
10090      /**
10091      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10092      */
10093     hideTrigger:false,
10094
10095     /**
10096      * @cfg {Boolean} removable (true|false) special filter default false
10097      */
10098     removable : false,
10099     
10100     /** @cfg {Boolean} grow @hide */
10101     /** @cfg {Number} growMin @hide */
10102     /** @cfg {Number} growMax @hide */
10103
10104     /**
10105      * @hide 
10106      * @method
10107      */
10108     autoSize: Roo.emptyFn,
10109     // private
10110     monitorTab : true,
10111     // private
10112     deferHeight : true,
10113
10114     
10115     actionMode : 'wrap',
10116     
10117     caret : false,
10118     
10119     
10120     getAutoCreate : function(){
10121        
10122         var align = this.labelAlign || this.parentLabelAlign();
10123         
10124         var id = Roo.id();
10125         
10126         var cfg = {
10127             cls: 'form-group' //input-group
10128         };
10129         
10130         
10131         var input =  {
10132             tag: 'input',
10133             id : id,
10134             type : this.inputType,
10135             cls : 'form-control',
10136             autocomplete: 'new-password',
10137             placeholder : this.placeholder || '' 
10138             
10139         };
10140         if (this.name) {
10141             input.name = this.name;
10142         }
10143         if (this.size) {
10144             input.cls += ' input-' + this.size;
10145         }
10146         
10147         if (this.disabled) {
10148             input.disabled=true;
10149         }
10150         
10151         var inputblock = input;
10152         
10153         if(this.hasFeedback && !this.allowBlank){
10154             
10155             var feedback = {
10156                 tag: 'span',
10157                 cls: 'glyphicon form-control-feedback'
10158             };
10159             
10160             if(this.removable && !this.editable && !this.tickable){
10161                 inputblock = {
10162                     cls : 'has-feedback',
10163                     cn :  [
10164                         inputblock,
10165                         {
10166                             tag: 'button',
10167                             html : 'x',
10168                             cls : 'roo-combo-removable-btn close'
10169                         },
10170                         feedback
10171                     ] 
10172                 };
10173             } else {
10174                 inputblock = {
10175                     cls : 'has-feedback',
10176                     cn :  [
10177                         inputblock,
10178                         feedback
10179                     ] 
10180                 };
10181             }
10182
10183         } else {
10184             if(this.removable && !this.editable && !this.tickable){
10185                 inputblock = {
10186                     cls : 'roo-removable',
10187                     cn :  [
10188                         inputblock,
10189                         {
10190                             tag: 'button',
10191                             html : 'x',
10192                             cls : 'roo-combo-removable-btn close'
10193                         }
10194                     ] 
10195                 };
10196             }
10197         }
10198         
10199         if (this.before || this.after) {
10200             
10201             inputblock = {
10202                 cls : 'input-group',
10203                 cn :  [] 
10204             };
10205             if (this.before) {
10206                 inputblock.cn.push({
10207                     tag :'span',
10208                     cls : 'input-group-addon',
10209                     html : this.before
10210                 });
10211             }
10212             
10213             inputblock.cn.push(input);
10214             
10215             if(this.hasFeedback && !this.allowBlank){
10216                 inputblock.cls += ' has-feedback';
10217                 inputblock.cn.push(feedback);
10218             }
10219             
10220             if (this.after) {
10221                 inputblock.cn.push({
10222                     tag :'span',
10223                     cls : 'input-group-addon',
10224                     html : this.after
10225                 });
10226             }
10227             
10228         };
10229         
10230         var box = {
10231             tag: 'div',
10232             cn: [
10233                 {
10234                     tag: 'input',
10235                     type : 'hidden',
10236                     cls: 'form-hidden-field'
10237                 },
10238                 inputblock
10239             ]
10240             
10241         };
10242         
10243         if(this.multiple){
10244             box = {
10245                 tag: 'div',
10246                 cn: [
10247                     {
10248                         tag: 'input',
10249                         type : 'hidden',
10250                         cls: 'form-hidden-field'
10251                     },
10252                     {
10253                         tag: 'ul',
10254                         cls: 'roo-select2-choices',
10255                         cn:[
10256                             {
10257                                 tag: 'li',
10258                                 cls: 'roo-select2-search-field',
10259                                 cn: [
10260
10261                                     inputblock
10262                                 ]
10263                             }
10264                         ]
10265                     }
10266                 ]
10267             }
10268         };
10269         
10270         var combobox = {
10271             cls: 'roo-select2-container input-group',
10272             cn: [
10273                 box
10274 //                {
10275 //                    tag: 'ul',
10276 //                    cls: 'typeahead typeahead-long dropdown-menu',
10277 //                    style: 'display:none'
10278 //                }
10279             ]
10280         };
10281         
10282         if(!this.multiple && this.showToggleBtn){
10283             
10284             var caret = {
10285                         tag: 'span',
10286                         cls: 'caret'
10287              };
10288             if (this.caret != false) {
10289                 caret = {
10290                      tag: 'i',
10291                      cls: 'fa fa-' + this.caret
10292                 };
10293                 
10294             }
10295             
10296             combobox.cn.push({
10297                 tag :'span',
10298                 cls : 'input-group-addon btn dropdown-toggle',
10299                 cn : [
10300                     caret,
10301                     {
10302                         tag: 'span',
10303                         cls: 'combobox-clear',
10304                         cn  : [
10305                             {
10306                                 tag : 'i',
10307                                 cls: 'icon-remove'
10308                             }
10309                         ]
10310                     }
10311                 ]
10312
10313             })
10314         }
10315         
10316         if(this.multiple){
10317             combobox.cls += ' roo-select2-container-multi';
10318         }
10319         
10320         if (align ==='left' && this.fieldLabel.length) {
10321             
10322             cfg.cls += ' roo-form-group-label-left';
10323
10324             cfg.cn = [
10325                 {
10326                     tag : 'i',
10327                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10328                     tooltip : 'This field is required'
10329                 },
10330                 {
10331                     tag: 'label',
10332                     'for' :  id,
10333                     cls : 'control-label',
10334                     html : this.fieldLabel
10335
10336                 },
10337                 {
10338                     cls : "", 
10339                     cn: [
10340                         combobox
10341                     ]
10342                 }
10343
10344             ];
10345             
10346             var labelCfg = cfg.cn[1];
10347             var contentCfg = cfg.cn[2];
10348             
10349             if(this.indicatorpos == 'right'){
10350                 cfg.cn = [
10351                     {
10352                         tag: 'label',
10353                         'for' :  id,
10354                         cls : 'control-label',
10355                         cn : [
10356                             {
10357                                 tag : 'span',
10358                                 html : this.fieldLabel
10359                             },
10360                             {
10361                                 tag : 'i',
10362                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10363                                 tooltip : 'This field is required'
10364                             }
10365                         ]
10366                     },
10367                     {
10368                         cls : "", 
10369                         cn: [
10370                             combobox
10371                         ]
10372                     }
10373
10374                 ];
10375                 
10376                 labelCfg = cfg.cn[0];
10377                 contentCfg = cfg.cn[1];
10378             }
10379             
10380             if(this.labelWidth > 12){
10381                 labelCfg.style = "width: " + this.labelWidth + 'px';
10382             }
10383             
10384             if(this.labelWidth < 13 && this.labelmd == 0){
10385                 this.labelmd = this.labelWidth;
10386             }
10387             
10388             if(this.labellg > 0){
10389                 labelCfg.cls += ' col-lg-' + this.labellg;
10390                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10391             }
10392             
10393             if(this.labelmd > 0){
10394                 labelCfg.cls += ' col-md-' + this.labelmd;
10395                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10396             }
10397             
10398             if(this.labelsm > 0){
10399                 labelCfg.cls += ' col-sm-' + this.labelsm;
10400                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10401             }
10402             
10403             if(this.labelxs > 0){
10404                 labelCfg.cls += ' col-xs-' + this.labelxs;
10405                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10406             }
10407             
10408         } else if ( this.fieldLabel.length) {
10409 //                Roo.log(" label");
10410             cfg.cn = [
10411                 {
10412                    tag : 'i',
10413                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10414                    tooltip : 'This field is required'
10415                },
10416                {
10417                    tag: 'label',
10418                    //cls : 'input-group-addon',
10419                    html : this.fieldLabel
10420
10421                },
10422
10423                combobox
10424
10425             ];
10426             
10427             if(this.indicatorpos == 'right'){
10428                 
10429                 cfg.cn = [
10430                     {
10431                        tag: 'label',
10432                        cn : [
10433                            {
10434                                tag : 'span',
10435                                html : this.fieldLabel
10436                            },
10437                            {
10438                               tag : 'i',
10439                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10440                               tooltip : 'This field is required'
10441                            }
10442                        ]
10443
10444                     },
10445                     combobox
10446
10447                 ];
10448
10449             }
10450
10451         } else {
10452             
10453 //                Roo.log(" no label && no align");
10454                 cfg = combobox
10455                      
10456                 
10457         }
10458         
10459         var settings=this;
10460         ['xs','sm','md','lg'].map(function(size){
10461             if (settings[size]) {
10462                 cfg.cls += ' col-' + size + '-' + settings[size];
10463             }
10464         });
10465         
10466         return cfg;
10467         
10468     },
10469     
10470     
10471     
10472     // private
10473     onResize : function(w, h){
10474 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10475 //        if(typeof w == 'number'){
10476 //            var x = w - this.trigger.getWidth();
10477 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10478 //            this.trigger.setStyle('left', x+'px');
10479 //        }
10480     },
10481
10482     // private
10483     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10484
10485     // private
10486     getResizeEl : function(){
10487         return this.inputEl();
10488     },
10489
10490     // private
10491     getPositionEl : function(){
10492         return this.inputEl();
10493     },
10494
10495     // private
10496     alignErrorIcon : function(){
10497         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10498     },
10499
10500     // private
10501     initEvents : function(){
10502         
10503         this.createList();
10504         
10505         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10506         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10507         if(!this.multiple && this.showToggleBtn){
10508             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10509             if(this.hideTrigger){
10510                 this.trigger.setDisplayed(false);
10511             }
10512             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10513         }
10514         
10515         if(this.multiple){
10516             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10517         }
10518         
10519         if(this.removable && !this.editable && !this.tickable){
10520             var close = this.closeTriggerEl();
10521             
10522             if(close){
10523                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10524                 close.on('click', this.removeBtnClick, this, close);
10525             }
10526         }
10527         
10528         //this.trigger.addClassOnOver('x-form-trigger-over');
10529         //this.trigger.addClassOnClick('x-form-trigger-click');
10530         
10531         //if(!this.width){
10532         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10533         //}
10534     },
10535     
10536     closeTriggerEl : function()
10537     {
10538         var close = this.el.select('.roo-combo-removable-btn', true).first();
10539         return close ? close : false;
10540     },
10541     
10542     removeBtnClick : function(e, h, el)
10543     {
10544         e.preventDefault();
10545         
10546         if(this.fireEvent("remove", this) !== false){
10547             this.reset();
10548             this.fireEvent("afterremove", this)
10549         }
10550     },
10551     
10552     createList : function()
10553     {
10554         this.list = Roo.get(document.body).createChild({
10555             tag: 'ul',
10556             cls: 'typeahead typeahead-long dropdown-menu',
10557             style: 'display:none'
10558         });
10559         
10560         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10561         
10562     },
10563
10564     // private
10565     initTrigger : function(){
10566        
10567     },
10568
10569     // private
10570     onDestroy : function(){
10571         if(this.trigger){
10572             this.trigger.removeAllListeners();
10573           //  this.trigger.remove();
10574         }
10575         //if(this.wrap){
10576         //    this.wrap.remove();
10577         //}
10578         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10579     },
10580
10581     // private
10582     onFocus : function(){
10583         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10584         /*
10585         if(!this.mimicing){
10586             this.wrap.addClass('x-trigger-wrap-focus');
10587             this.mimicing = true;
10588             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10589             if(this.monitorTab){
10590                 this.el.on("keydown", this.checkTab, this);
10591             }
10592         }
10593         */
10594     },
10595
10596     // private
10597     checkTab : function(e){
10598         if(e.getKey() == e.TAB){
10599             this.triggerBlur();
10600         }
10601     },
10602
10603     // private
10604     onBlur : function(){
10605         // do nothing
10606     },
10607
10608     // private
10609     mimicBlur : function(e, t){
10610         /*
10611         if(!this.wrap.contains(t) && this.validateBlur()){
10612             this.triggerBlur();
10613         }
10614         */
10615     },
10616
10617     // private
10618     triggerBlur : function(){
10619         this.mimicing = false;
10620         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10621         if(this.monitorTab){
10622             this.el.un("keydown", this.checkTab, this);
10623         }
10624         //this.wrap.removeClass('x-trigger-wrap-focus');
10625         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10626     },
10627
10628     // private
10629     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10630     validateBlur : function(e, t){
10631         return true;
10632     },
10633
10634     // private
10635     onDisable : function(){
10636         this.inputEl().dom.disabled = true;
10637         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10638         //if(this.wrap){
10639         //    this.wrap.addClass('x-item-disabled');
10640         //}
10641     },
10642
10643     // private
10644     onEnable : function(){
10645         this.inputEl().dom.disabled = false;
10646         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10647         //if(this.wrap){
10648         //    this.el.removeClass('x-item-disabled');
10649         //}
10650     },
10651
10652     // private
10653     onShow : function(){
10654         var ae = this.getActionEl();
10655         
10656         if(ae){
10657             ae.dom.style.display = '';
10658             ae.dom.style.visibility = 'visible';
10659         }
10660     },
10661
10662     // private
10663     
10664     onHide : function(){
10665         var ae = this.getActionEl();
10666         ae.dom.style.display = 'none';
10667     },
10668
10669     /**
10670      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10671      * by an implementing function.
10672      * @method
10673      * @param {EventObject} e
10674      */
10675     onTriggerClick : Roo.emptyFn
10676 });
10677  /*
10678  * Based on:
10679  * Ext JS Library 1.1.1
10680  * Copyright(c) 2006-2007, Ext JS, LLC.
10681  *
10682  * Originally Released Under LGPL - original licence link has changed is not relivant.
10683  *
10684  * Fork - LGPL
10685  * <script type="text/javascript">
10686  */
10687
10688
10689 /**
10690  * @class Roo.data.SortTypes
10691  * @singleton
10692  * Defines the default sorting (casting?) comparison functions used when sorting data.
10693  */
10694 Roo.data.SortTypes = {
10695     /**
10696      * Default sort that does nothing
10697      * @param {Mixed} s The value being converted
10698      * @return {Mixed} The comparison value
10699      */
10700     none : function(s){
10701         return s;
10702     },
10703     
10704     /**
10705      * The regular expression used to strip tags
10706      * @type {RegExp}
10707      * @property
10708      */
10709     stripTagsRE : /<\/?[^>]+>/gi,
10710     
10711     /**
10712      * Strips all HTML tags to sort on text only
10713      * @param {Mixed} s The value being converted
10714      * @return {String} The comparison value
10715      */
10716     asText : function(s){
10717         return String(s).replace(this.stripTagsRE, "");
10718     },
10719     
10720     /**
10721      * Strips all HTML tags to sort on text only - Case insensitive
10722      * @param {Mixed} s The value being converted
10723      * @return {String} The comparison value
10724      */
10725     asUCText : function(s){
10726         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10727     },
10728     
10729     /**
10730      * Case insensitive string
10731      * @param {Mixed} s The value being converted
10732      * @return {String} The comparison value
10733      */
10734     asUCString : function(s) {
10735         return String(s).toUpperCase();
10736     },
10737     
10738     /**
10739      * Date sorting
10740      * @param {Mixed} s The value being converted
10741      * @return {Number} The comparison value
10742      */
10743     asDate : function(s) {
10744         if(!s){
10745             return 0;
10746         }
10747         if(s instanceof Date){
10748             return s.getTime();
10749         }
10750         return Date.parse(String(s));
10751     },
10752     
10753     /**
10754      * Float sorting
10755      * @param {Mixed} s The value being converted
10756      * @return {Float} The comparison value
10757      */
10758     asFloat : function(s) {
10759         var val = parseFloat(String(s).replace(/,/g, ""));
10760         if(isNaN(val)) {
10761             val = 0;
10762         }
10763         return val;
10764     },
10765     
10766     /**
10767      * Integer sorting
10768      * @param {Mixed} s The value being converted
10769      * @return {Number} The comparison value
10770      */
10771     asInt : function(s) {
10772         var val = parseInt(String(s).replace(/,/g, ""));
10773         if(isNaN(val)) {
10774             val = 0;
10775         }
10776         return val;
10777     }
10778 };/*
10779  * Based on:
10780  * Ext JS Library 1.1.1
10781  * Copyright(c) 2006-2007, Ext JS, LLC.
10782  *
10783  * Originally Released Under LGPL - original licence link has changed is not relivant.
10784  *
10785  * Fork - LGPL
10786  * <script type="text/javascript">
10787  */
10788
10789 /**
10790 * @class Roo.data.Record
10791  * Instances of this class encapsulate both record <em>definition</em> information, and record
10792  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10793  * to access Records cached in an {@link Roo.data.Store} object.<br>
10794  * <p>
10795  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10796  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10797  * objects.<br>
10798  * <p>
10799  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10800  * @constructor
10801  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10802  * {@link #create}. The parameters are the same.
10803  * @param {Array} data An associative Array of data values keyed by the field name.
10804  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10805  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10806  * not specified an integer id is generated.
10807  */
10808 Roo.data.Record = function(data, id){
10809     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10810     this.data = data;
10811 };
10812
10813 /**
10814  * Generate a constructor for a specific record layout.
10815  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10816  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10817  * Each field definition object may contain the following properties: <ul>
10818  * <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,
10819  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10820  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10821  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10822  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10823  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10824  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10825  * this may be omitted.</p></li>
10826  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10827  * <ul><li>auto (Default, implies no conversion)</li>
10828  * <li>string</li>
10829  * <li>int</li>
10830  * <li>float</li>
10831  * <li>boolean</li>
10832  * <li>date</li></ul></p></li>
10833  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10834  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10835  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10836  * by the Reader into an object that will be stored in the Record. It is passed the
10837  * following parameters:<ul>
10838  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10839  * </ul></p></li>
10840  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10841  * </ul>
10842  * <br>usage:<br><pre><code>
10843 var TopicRecord = Roo.data.Record.create(
10844     {name: 'title', mapping: 'topic_title'},
10845     {name: 'author', mapping: 'username'},
10846     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10847     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10848     {name: 'lastPoster', mapping: 'user2'},
10849     {name: 'excerpt', mapping: 'post_text'}
10850 );
10851
10852 var myNewRecord = new TopicRecord({
10853     title: 'Do my job please',
10854     author: 'noobie',
10855     totalPosts: 1,
10856     lastPost: new Date(),
10857     lastPoster: 'Animal',
10858     excerpt: 'No way dude!'
10859 });
10860 myStore.add(myNewRecord);
10861 </code></pre>
10862  * @method create
10863  * @static
10864  */
10865 Roo.data.Record.create = function(o){
10866     var f = function(){
10867         f.superclass.constructor.apply(this, arguments);
10868     };
10869     Roo.extend(f, Roo.data.Record);
10870     var p = f.prototype;
10871     p.fields = new Roo.util.MixedCollection(false, function(field){
10872         return field.name;
10873     });
10874     for(var i = 0, len = o.length; i < len; i++){
10875         p.fields.add(new Roo.data.Field(o[i]));
10876     }
10877     f.getField = function(name){
10878         return p.fields.get(name);  
10879     };
10880     return f;
10881 };
10882
10883 Roo.data.Record.AUTO_ID = 1000;
10884 Roo.data.Record.EDIT = 'edit';
10885 Roo.data.Record.REJECT = 'reject';
10886 Roo.data.Record.COMMIT = 'commit';
10887
10888 Roo.data.Record.prototype = {
10889     /**
10890      * Readonly flag - true if this record has been modified.
10891      * @type Boolean
10892      */
10893     dirty : false,
10894     editing : false,
10895     error: null,
10896     modified: null,
10897
10898     // private
10899     join : function(store){
10900         this.store = store;
10901     },
10902
10903     /**
10904      * Set the named field to the specified value.
10905      * @param {String} name The name of the field to set.
10906      * @param {Object} value The value to set the field to.
10907      */
10908     set : function(name, value){
10909         if(this.data[name] == value){
10910             return;
10911         }
10912         this.dirty = true;
10913         if(!this.modified){
10914             this.modified = {};
10915         }
10916         if(typeof this.modified[name] == 'undefined'){
10917             this.modified[name] = this.data[name];
10918         }
10919         this.data[name] = value;
10920         if(!this.editing && this.store){
10921             this.store.afterEdit(this);
10922         }       
10923     },
10924
10925     /**
10926      * Get the value of the named field.
10927      * @param {String} name The name of the field to get the value of.
10928      * @return {Object} The value of the field.
10929      */
10930     get : function(name){
10931         return this.data[name]; 
10932     },
10933
10934     // private
10935     beginEdit : function(){
10936         this.editing = true;
10937         this.modified = {}; 
10938     },
10939
10940     // private
10941     cancelEdit : function(){
10942         this.editing = false;
10943         delete this.modified;
10944     },
10945
10946     // private
10947     endEdit : function(){
10948         this.editing = false;
10949         if(this.dirty && this.store){
10950             this.store.afterEdit(this);
10951         }
10952     },
10953
10954     /**
10955      * Usually called by the {@link Roo.data.Store} which owns the Record.
10956      * Rejects all changes made to the Record since either creation, or the last commit operation.
10957      * Modified fields are reverted to their original values.
10958      * <p>
10959      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10960      * of reject operations.
10961      */
10962     reject : function(){
10963         var m = this.modified;
10964         for(var n in m){
10965             if(typeof m[n] != "function"){
10966                 this.data[n] = m[n];
10967             }
10968         }
10969         this.dirty = false;
10970         delete this.modified;
10971         this.editing = false;
10972         if(this.store){
10973             this.store.afterReject(this);
10974         }
10975     },
10976
10977     /**
10978      * Usually called by the {@link Roo.data.Store} which owns the Record.
10979      * Commits all changes made to the Record since either creation, or the last commit operation.
10980      * <p>
10981      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10982      * of commit operations.
10983      */
10984     commit : function(){
10985         this.dirty = false;
10986         delete this.modified;
10987         this.editing = false;
10988         if(this.store){
10989             this.store.afterCommit(this);
10990         }
10991     },
10992
10993     // private
10994     hasError : function(){
10995         return this.error != null;
10996     },
10997
10998     // private
10999     clearError : function(){
11000         this.error = null;
11001     },
11002
11003     /**
11004      * Creates a copy of this record.
11005      * @param {String} id (optional) A new record id if you don't want to use this record's id
11006      * @return {Record}
11007      */
11008     copy : function(newId) {
11009         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11010     }
11011 };/*
11012  * Based on:
11013  * Ext JS Library 1.1.1
11014  * Copyright(c) 2006-2007, Ext JS, LLC.
11015  *
11016  * Originally Released Under LGPL - original licence link has changed is not relivant.
11017  *
11018  * Fork - LGPL
11019  * <script type="text/javascript">
11020  */
11021
11022
11023
11024 /**
11025  * @class Roo.data.Store
11026  * @extends Roo.util.Observable
11027  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11028  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11029  * <p>
11030  * 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
11031  * has no knowledge of the format of the data returned by the Proxy.<br>
11032  * <p>
11033  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11034  * instances from the data object. These records are cached and made available through accessor functions.
11035  * @constructor
11036  * Creates a new Store.
11037  * @param {Object} config A config object containing the objects needed for the Store to access data,
11038  * and read the data into Records.
11039  */
11040 Roo.data.Store = function(config){
11041     this.data = new Roo.util.MixedCollection(false);
11042     this.data.getKey = function(o){
11043         return o.id;
11044     };
11045     this.baseParams = {};
11046     // private
11047     this.paramNames = {
11048         "start" : "start",
11049         "limit" : "limit",
11050         "sort" : "sort",
11051         "dir" : "dir",
11052         "multisort" : "_multisort"
11053     };
11054
11055     if(config && config.data){
11056         this.inlineData = config.data;
11057         delete config.data;
11058     }
11059
11060     Roo.apply(this, config);
11061     
11062     if(this.reader){ // reader passed
11063         this.reader = Roo.factory(this.reader, Roo.data);
11064         this.reader.xmodule = this.xmodule || false;
11065         if(!this.recordType){
11066             this.recordType = this.reader.recordType;
11067         }
11068         if(this.reader.onMetaChange){
11069             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11070         }
11071     }
11072
11073     if(this.recordType){
11074         this.fields = this.recordType.prototype.fields;
11075     }
11076     this.modified = [];
11077
11078     this.addEvents({
11079         /**
11080          * @event datachanged
11081          * Fires when the data cache has changed, and a widget which is using this Store
11082          * as a Record cache should refresh its view.
11083          * @param {Store} this
11084          */
11085         datachanged : true,
11086         /**
11087          * @event metachange
11088          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11089          * @param {Store} this
11090          * @param {Object} meta The JSON metadata
11091          */
11092         metachange : true,
11093         /**
11094          * @event add
11095          * Fires when Records have been added to the Store
11096          * @param {Store} this
11097          * @param {Roo.data.Record[]} records The array of Records added
11098          * @param {Number} index The index at which the record(s) were added
11099          */
11100         add : true,
11101         /**
11102          * @event remove
11103          * Fires when a Record has been removed from the Store
11104          * @param {Store} this
11105          * @param {Roo.data.Record} record The Record that was removed
11106          * @param {Number} index The index at which the record was removed
11107          */
11108         remove : true,
11109         /**
11110          * @event update
11111          * Fires when a Record has been updated
11112          * @param {Store} this
11113          * @param {Roo.data.Record} record The Record that was updated
11114          * @param {String} operation The update operation being performed.  Value may be one of:
11115          * <pre><code>
11116  Roo.data.Record.EDIT
11117  Roo.data.Record.REJECT
11118  Roo.data.Record.COMMIT
11119          * </code></pre>
11120          */
11121         update : true,
11122         /**
11123          * @event clear
11124          * Fires when the data cache has been cleared.
11125          * @param {Store} this
11126          */
11127         clear : true,
11128         /**
11129          * @event beforeload
11130          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11131          * the load action will be canceled.
11132          * @param {Store} this
11133          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11134          */
11135         beforeload : true,
11136         /**
11137          * @event beforeloadadd
11138          * Fires after a new set of Records has been loaded.
11139          * @param {Store} this
11140          * @param {Roo.data.Record[]} records The Records that were loaded
11141          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11142          */
11143         beforeloadadd : true,
11144         /**
11145          * @event load
11146          * Fires after a new set of Records has been loaded, before they are added to the store.
11147          * @param {Store} this
11148          * @param {Roo.data.Record[]} records The Records that were loaded
11149          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11150          * @params {Object} return from reader
11151          */
11152         load : true,
11153         /**
11154          * @event loadexception
11155          * Fires if an exception occurs in the Proxy during loading.
11156          * Called with the signature of the Proxy's "loadexception" event.
11157          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11158          * 
11159          * @param {Proxy} 
11160          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11161          * @param {Object} load options 
11162          * @param {Object} jsonData from your request (normally this contains the Exception)
11163          */
11164         loadexception : true
11165     });
11166     
11167     if(this.proxy){
11168         this.proxy = Roo.factory(this.proxy, Roo.data);
11169         this.proxy.xmodule = this.xmodule || false;
11170         this.relayEvents(this.proxy,  ["loadexception"]);
11171     }
11172     this.sortToggle = {};
11173     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11174
11175     Roo.data.Store.superclass.constructor.call(this);
11176
11177     if(this.inlineData){
11178         this.loadData(this.inlineData);
11179         delete this.inlineData;
11180     }
11181 };
11182
11183 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11184      /**
11185     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11186     * without a remote query - used by combo/forms at present.
11187     */
11188     
11189     /**
11190     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11191     */
11192     /**
11193     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11194     */
11195     /**
11196     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11197     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11198     */
11199     /**
11200     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11201     * on any HTTP request
11202     */
11203     /**
11204     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11205     */
11206     /**
11207     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11208     */
11209     multiSort: false,
11210     /**
11211     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11212     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11213     */
11214     remoteSort : false,
11215
11216     /**
11217     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11218      * loaded or when a record is removed. (defaults to false).
11219     */
11220     pruneModifiedRecords : false,
11221
11222     // private
11223     lastOptions : null,
11224
11225     /**
11226      * Add Records to the Store and fires the add event.
11227      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11228      */
11229     add : function(records){
11230         records = [].concat(records);
11231         for(var i = 0, len = records.length; i < len; i++){
11232             records[i].join(this);
11233         }
11234         var index = this.data.length;
11235         this.data.addAll(records);
11236         this.fireEvent("add", this, records, index);
11237     },
11238
11239     /**
11240      * Remove a Record from the Store and fires the remove event.
11241      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11242      */
11243     remove : function(record){
11244         var index = this.data.indexOf(record);
11245         this.data.removeAt(index);
11246  
11247         if(this.pruneModifiedRecords){
11248             this.modified.remove(record);
11249         }
11250         this.fireEvent("remove", this, record, index);
11251     },
11252
11253     /**
11254      * Remove all Records from the Store and fires the clear event.
11255      */
11256     removeAll : function(){
11257         this.data.clear();
11258         if(this.pruneModifiedRecords){
11259             this.modified = [];
11260         }
11261         this.fireEvent("clear", this);
11262     },
11263
11264     /**
11265      * Inserts Records to the Store at the given index and fires the add event.
11266      * @param {Number} index The start index at which to insert the passed Records.
11267      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11268      */
11269     insert : function(index, records){
11270         records = [].concat(records);
11271         for(var i = 0, len = records.length; i < len; i++){
11272             this.data.insert(index, records[i]);
11273             records[i].join(this);
11274         }
11275         this.fireEvent("add", this, records, index);
11276     },
11277
11278     /**
11279      * Get the index within the cache of the passed Record.
11280      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11281      * @return {Number} The index of the passed Record. Returns -1 if not found.
11282      */
11283     indexOf : function(record){
11284         return this.data.indexOf(record);
11285     },
11286
11287     /**
11288      * Get the index within the cache of the Record with the passed id.
11289      * @param {String} id The id of the Record to find.
11290      * @return {Number} The index of the Record. Returns -1 if not found.
11291      */
11292     indexOfId : function(id){
11293         return this.data.indexOfKey(id);
11294     },
11295
11296     /**
11297      * Get the Record with the specified id.
11298      * @param {String} id The id of the Record to find.
11299      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11300      */
11301     getById : function(id){
11302         return this.data.key(id);
11303     },
11304
11305     /**
11306      * Get the Record at the specified index.
11307      * @param {Number} index The index of the Record to find.
11308      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11309      */
11310     getAt : function(index){
11311         return this.data.itemAt(index);
11312     },
11313
11314     /**
11315      * Returns a range of Records between specified indices.
11316      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11317      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11318      * @return {Roo.data.Record[]} An array of Records
11319      */
11320     getRange : function(start, end){
11321         return this.data.getRange(start, end);
11322     },
11323
11324     // private
11325     storeOptions : function(o){
11326         o = Roo.apply({}, o);
11327         delete o.callback;
11328         delete o.scope;
11329         this.lastOptions = o;
11330     },
11331
11332     /**
11333      * Loads the Record cache from the configured Proxy using the configured Reader.
11334      * <p>
11335      * If using remote paging, then the first load call must specify the <em>start</em>
11336      * and <em>limit</em> properties in the options.params property to establish the initial
11337      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11338      * <p>
11339      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11340      * and this call will return before the new data has been loaded. Perform any post-processing
11341      * in a callback function, or in a "load" event handler.</strong>
11342      * <p>
11343      * @param {Object} options An object containing properties which control loading options:<ul>
11344      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11345      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11346      * passed the following arguments:<ul>
11347      * <li>r : Roo.data.Record[]</li>
11348      * <li>options: Options object from the load call</li>
11349      * <li>success: Boolean success indicator</li></ul></li>
11350      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11351      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11352      * </ul>
11353      */
11354     load : function(options){
11355         options = options || {};
11356         if(this.fireEvent("beforeload", this, options) !== false){
11357             this.storeOptions(options);
11358             var p = Roo.apply(options.params || {}, this.baseParams);
11359             // if meta was not loaded from remote source.. try requesting it.
11360             if (!this.reader.metaFromRemote) {
11361                 p._requestMeta = 1;
11362             }
11363             if(this.sortInfo && this.remoteSort){
11364                 var pn = this.paramNames;
11365                 p[pn["sort"]] = this.sortInfo.field;
11366                 p[pn["dir"]] = this.sortInfo.direction;
11367             }
11368             if (this.multiSort) {
11369                 var pn = this.paramNames;
11370                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11371             }
11372             
11373             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11374         }
11375     },
11376
11377     /**
11378      * Reloads the Record cache from the configured Proxy using the configured Reader and
11379      * the options from the last load operation performed.
11380      * @param {Object} options (optional) An object containing properties which may override the options
11381      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11382      * the most recently used options are reused).
11383      */
11384     reload : function(options){
11385         this.load(Roo.applyIf(options||{}, this.lastOptions));
11386     },
11387
11388     // private
11389     // Called as a callback by the Reader during a load operation.
11390     loadRecords : function(o, options, success){
11391         if(!o || success === false){
11392             if(success !== false){
11393                 this.fireEvent("load", this, [], options, o);
11394             }
11395             if(options.callback){
11396                 options.callback.call(options.scope || this, [], options, false);
11397             }
11398             return;
11399         }
11400         // if data returned failure - throw an exception.
11401         if (o.success === false) {
11402             // show a message if no listener is registered.
11403             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11404                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11405             }
11406             // loadmask wil be hooked into this..
11407             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11408             return;
11409         }
11410         var r = o.records, t = o.totalRecords || r.length;
11411         
11412         this.fireEvent("beforeloadadd", this, r, options, o);
11413         
11414         if(!options || options.add !== true){
11415             if(this.pruneModifiedRecords){
11416                 this.modified = [];
11417             }
11418             for(var i = 0, len = r.length; i < len; i++){
11419                 r[i].join(this);
11420             }
11421             if(this.snapshot){
11422                 this.data = this.snapshot;
11423                 delete this.snapshot;
11424             }
11425             this.data.clear();
11426             this.data.addAll(r);
11427             this.totalLength = t;
11428             this.applySort();
11429             this.fireEvent("datachanged", this);
11430         }else{
11431             this.totalLength = Math.max(t, this.data.length+r.length);
11432             this.add(r);
11433         }
11434         
11435         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11436                 
11437             var e = new Roo.data.Record({});
11438
11439             e.set(this.parent.displayField, this.parent.emptyTitle);
11440             e.set(this.parent.valueField, '');
11441
11442             this.insert(0, e);
11443         }
11444             
11445         this.fireEvent("load", this, r, options, o);
11446         if(options.callback){
11447             options.callback.call(options.scope || this, r, options, true);
11448         }
11449     },
11450
11451
11452     /**
11453      * Loads data from a passed data block. A Reader which understands the format of the data
11454      * must have been configured in the constructor.
11455      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11456      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11457      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11458      */
11459     loadData : function(o, append){
11460         var r = this.reader.readRecords(o);
11461         this.loadRecords(r, {add: append}, true);
11462     },
11463
11464     /**
11465      * Gets the number of cached records.
11466      * <p>
11467      * <em>If using paging, this may not be the total size of the dataset. If the data object
11468      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11469      * the data set size</em>
11470      */
11471     getCount : function(){
11472         return this.data.length || 0;
11473     },
11474
11475     /**
11476      * Gets the total number of records in the dataset as returned by the server.
11477      * <p>
11478      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11479      * the dataset size</em>
11480      */
11481     getTotalCount : function(){
11482         return this.totalLength || 0;
11483     },
11484
11485     /**
11486      * Returns the sort state of the Store as an object with two properties:
11487      * <pre><code>
11488  field {String} The name of the field by which the Records are sorted
11489  direction {String} The sort order, "ASC" or "DESC"
11490      * </code></pre>
11491      */
11492     getSortState : function(){
11493         return this.sortInfo;
11494     },
11495
11496     // private
11497     applySort : function(){
11498         if(this.sortInfo && !this.remoteSort){
11499             var s = this.sortInfo, f = s.field;
11500             var st = this.fields.get(f).sortType;
11501             var fn = function(r1, r2){
11502                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11503                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11504             };
11505             this.data.sort(s.direction, fn);
11506             if(this.snapshot && this.snapshot != this.data){
11507                 this.snapshot.sort(s.direction, fn);
11508             }
11509         }
11510     },
11511
11512     /**
11513      * Sets the default sort column and order to be used by the next load operation.
11514      * @param {String} fieldName The name of the field to sort by.
11515      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11516      */
11517     setDefaultSort : function(field, dir){
11518         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11519     },
11520
11521     /**
11522      * Sort the Records.
11523      * If remote sorting is used, the sort is performed on the server, and the cache is
11524      * reloaded. If local sorting is used, the cache is sorted internally.
11525      * @param {String} fieldName The name of the field to sort by.
11526      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11527      */
11528     sort : function(fieldName, dir){
11529         var f = this.fields.get(fieldName);
11530         if(!dir){
11531             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11532             
11533             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11534                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11535             }else{
11536                 dir = f.sortDir;
11537             }
11538         }
11539         this.sortToggle[f.name] = dir;
11540         this.sortInfo = {field: f.name, direction: dir};
11541         if(!this.remoteSort){
11542             this.applySort();
11543             this.fireEvent("datachanged", this);
11544         }else{
11545             this.load(this.lastOptions);
11546         }
11547     },
11548
11549     /**
11550      * Calls the specified function for each of the Records in the cache.
11551      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11552      * Returning <em>false</em> aborts and exits the iteration.
11553      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11554      */
11555     each : function(fn, scope){
11556         this.data.each(fn, scope);
11557     },
11558
11559     /**
11560      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11561      * (e.g., during paging).
11562      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11563      */
11564     getModifiedRecords : function(){
11565         return this.modified;
11566     },
11567
11568     // private
11569     createFilterFn : function(property, value, anyMatch){
11570         if(!value.exec){ // not a regex
11571             value = String(value);
11572             if(value.length == 0){
11573                 return false;
11574             }
11575             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11576         }
11577         return function(r){
11578             return value.test(r.data[property]);
11579         };
11580     },
11581
11582     /**
11583      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11584      * @param {String} property A field on your records
11585      * @param {Number} start The record index to start at (defaults to 0)
11586      * @param {Number} end The last record index to include (defaults to length - 1)
11587      * @return {Number} The sum
11588      */
11589     sum : function(property, start, end){
11590         var rs = this.data.items, v = 0;
11591         start = start || 0;
11592         end = (end || end === 0) ? end : rs.length-1;
11593
11594         for(var i = start; i <= end; i++){
11595             v += (rs[i].data[property] || 0);
11596         }
11597         return v;
11598     },
11599
11600     /**
11601      * Filter the records by a specified property.
11602      * @param {String} field A field on your records
11603      * @param {String/RegExp} value Either a string that the field
11604      * should start with or a RegExp to test against the field
11605      * @param {Boolean} anyMatch True to match any part not just the beginning
11606      */
11607     filter : function(property, value, anyMatch){
11608         var fn = this.createFilterFn(property, value, anyMatch);
11609         return fn ? this.filterBy(fn) : this.clearFilter();
11610     },
11611
11612     /**
11613      * Filter by a function. The specified function will be called with each
11614      * record in this data source. If the function returns true the record is included,
11615      * otherwise it is filtered.
11616      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11617      * @param {Object} scope (optional) The scope of the function (defaults to this)
11618      */
11619     filterBy : function(fn, scope){
11620         this.snapshot = this.snapshot || this.data;
11621         this.data = this.queryBy(fn, scope||this);
11622         this.fireEvent("datachanged", this);
11623     },
11624
11625     /**
11626      * Query the records by a specified property.
11627      * @param {String} field A field on your records
11628      * @param {String/RegExp} value Either a string that the field
11629      * should start with or a RegExp to test against the field
11630      * @param {Boolean} anyMatch True to match any part not just the beginning
11631      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11632      */
11633     query : function(property, value, anyMatch){
11634         var fn = this.createFilterFn(property, value, anyMatch);
11635         return fn ? this.queryBy(fn) : this.data.clone();
11636     },
11637
11638     /**
11639      * Query by a function. The specified function will be called with each
11640      * record in this data source. If the function returns true the record is included
11641      * in the results.
11642      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11643      * @param {Object} scope (optional) The scope of the function (defaults to this)
11644       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11645      **/
11646     queryBy : function(fn, scope){
11647         var data = this.snapshot || this.data;
11648         return data.filterBy(fn, scope||this);
11649     },
11650
11651     /**
11652      * Collects unique values for a particular dataIndex from this store.
11653      * @param {String} dataIndex The property to collect
11654      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11655      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11656      * @return {Array} An array of the unique values
11657      **/
11658     collect : function(dataIndex, allowNull, bypassFilter){
11659         var d = (bypassFilter === true && this.snapshot) ?
11660                 this.snapshot.items : this.data.items;
11661         var v, sv, r = [], l = {};
11662         for(var i = 0, len = d.length; i < len; i++){
11663             v = d[i].data[dataIndex];
11664             sv = String(v);
11665             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11666                 l[sv] = true;
11667                 r[r.length] = v;
11668             }
11669         }
11670         return r;
11671     },
11672
11673     /**
11674      * Revert to a view of the Record cache with no filtering applied.
11675      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11676      */
11677     clearFilter : function(suppressEvent){
11678         if(this.snapshot && this.snapshot != this.data){
11679             this.data = this.snapshot;
11680             delete this.snapshot;
11681             if(suppressEvent !== true){
11682                 this.fireEvent("datachanged", this);
11683             }
11684         }
11685     },
11686
11687     // private
11688     afterEdit : function(record){
11689         if(this.modified.indexOf(record) == -1){
11690             this.modified.push(record);
11691         }
11692         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11693     },
11694     
11695     // private
11696     afterReject : function(record){
11697         this.modified.remove(record);
11698         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11699     },
11700
11701     // private
11702     afterCommit : function(record){
11703         this.modified.remove(record);
11704         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11705     },
11706
11707     /**
11708      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11709      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11710      */
11711     commitChanges : function(){
11712         var m = this.modified.slice(0);
11713         this.modified = [];
11714         for(var i = 0, len = m.length; i < len; i++){
11715             m[i].commit();
11716         }
11717     },
11718
11719     /**
11720      * Cancel outstanding changes on all changed records.
11721      */
11722     rejectChanges : function(){
11723         var m = this.modified.slice(0);
11724         this.modified = [];
11725         for(var i = 0, len = m.length; i < len; i++){
11726             m[i].reject();
11727         }
11728     },
11729
11730     onMetaChange : function(meta, rtype, o){
11731         this.recordType = rtype;
11732         this.fields = rtype.prototype.fields;
11733         delete this.snapshot;
11734         this.sortInfo = meta.sortInfo || this.sortInfo;
11735         this.modified = [];
11736         this.fireEvent('metachange', this, this.reader.meta);
11737     },
11738     
11739     moveIndex : function(data, type)
11740     {
11741         var index = this.indexOf(data);
11742         
11743         var newIndex = index + type;
11744         
11745         this.remove(data);
11746         
11747         this.insert(newIndex, data);
11748         
11749     }
11750 });/*
11751  * Based on:
11752  * Ext JS Library 1.1.1
11753  * Copyright(c) 2006-2007, Ext JS, LLC.
11754  *
11755  * Originally Released Under LGPL - original licence link has changed is not relivant.
11756  *
11757  * Fork - LGPL
11758  * <script type="text/javascript">
11759  */
11760
11761 /**
11762  * @class Roo.data.SimpleStore
11763  * @extends Roo.data.Store
11764  * Small helper class to make creating Stores from Array data easier.
11765  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11766  * @cfg {Array} fields An array of field definition objects, or field name strings.
11767  * @cfg {Array} data The multi-dimensional array of data
11768  * @constructor
11769  * @param {Object} config
11770  */
11771 Roo.data.SimpleStore = function(config){
11772     Roo.data.SimpleStore.superclass.constructor.call(this, {
11773         isLocal : true,
11774         reader: new Roo.data.ArrayReader({
11775                 id: config.id
11776             },
11777             Roo.data.Record.create(config.fields)
11778         ),
11779         proxy : new Roo.data.MemoryProxy(config.data)
11780     });
11781     this.load();
11782 };
11783 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11784  * Based on:
11785  * Ext JS Library 1.1.1
11786  * Copyright(c) 2006-2007, Ext JS, LLC.
11787  *
11788  * Originally Released Under LGPL - original licence link has changed is not relivant.
11789  *
11790  * Fork - LGPL
11791  * <script type="text/javascript">
11792  */
11793
11794 /**
11795 /**
11796  * @extends Roo.data.Store
11797  * @class Roo.data.JsonStore
11798  * Small helper class to make creating Stores for JSON data easier. <br/>
11799 <pre><code>
11800 var store = new Roo.data.JsonStore({
11801     url: 'get-images.php',
11802     root: 'images',
11803     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11804 });
11805 </code></pre>
11806  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11807  * JsonReader and HttpProxy (unless inline data is provided).</b>
11808  * @cfg {Array} fields An array of field definition objects, or field name strings.
11809  * @constructor
11810  * @param {Object} config
11811  */
11812 Roo.data.JsonStore = function(c){
11813     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11814         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11815         reader: new Roo.data.JsonReader(c, c.fields)
11816     }));
11817 };
11818 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11819  * Based on:
11820  * Ext JS Library 1.1.1
11821  * Copyright(c) 2006-2007, Ext JS, LLC.
11822  *
11823  * Originally Released Under LGPL - original licence link has changed is not relivant.
11824  *
11825  * Fork - LGPL
11826  * <script type="text/javascript">
11827  */
11828
11829  
11830 Roo.data.Field = function(config){
11831     if(typeof config == "string"){
11832         config = {name: config};
11833     }
11834     Roo.apply(this, config);
11835     
11836     if(!this.type){
11837         this.type = "auto";
11838     }
11839     
11840     var st = Roo.data.SortTypes;
11841     // named sortTypes are supported, here we look them up
11842     if(typeof this.sortType == "string"){
11843         this.sortType = st[this.sortType];
11844     }
11845     
11846     // set default sortType for strings and dates
11847     if(!this.sortType){
11848         switch(this.type){
11849             case "string":
11850                 this.sortType = st.asUCString;
11851                 break;
11852             case "date":
11853                 this.sortType = st.asDate;
11854                 break;
11855             default:
11856                 this.sortType = st.none;
11857         }
11858     }
11859
11860     // define once
11861     var stripRe = /[\$,%]/g;
11862
11863     // prebuilt conversion function for this field, instead of
11864     // switching every time we're reading a value
11865     if(!this.convert){
11866         var cv, dateFormat = this.dateFormat;
11867         switch(this.type){
11868             case "":
11869             case "auto":
11870             case undefined:
11871                 cv = function(v){ return v; };
11872                 break;
11873             case "string":
11874                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11875                 break;
11876             case "int":
11877                 cv = function(v){
11878                     return v !== undefined && v !== null && v !== '' ?
11879                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11880                     };
11881                 break;
11882             case "float":
11883                 cv = function(v){
11884                     return v !== undefined && v !== null && v !== '' ?
11885                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11886                     };
11887                 break;
11888             case "bool":
11889             case "boolean":
11890                 cv = function(v){ return v === true || v === "true" || v == 1; };
11891                 break;
11892             case "date":
11893                 cv = function(v){
11894                     if(!v){
11895                         return '';
11896                     }
11897                     if(v instanceof Date){
11898                         return v;
11899                     }
11900                     if(dateFormat){
11901                         if(dateFormat == "timestamp"){
11902                             return new Date(v*1000);
11903                         }
11904                         return Date.parseDate(v, dateFormat);
11905                     }
11906                     var parsed = Date.parse(v);
11907                     return parsed ? new Date(parsed) : null;
11908                 };
11909              break;
11910             
11911         }
11912         this.convert = cv;
11913     }
11914 };
11915
11916 Roo.data.Field.prototype = {
11917     dateFormat: null,
11918     defaultValue: "",
11919     mapping: null,
11920     sortType : null,
11921     sortDir : "ASC"
11922 };/*
11923  * Based on:
11924  * Ext JS Library 1.1.1
11925  * Copyright(c) 2006-2007, Ext JS, LLC.
11926  *
11927  * Originally Released Under LGPL - original licence link has changed is not relivant.
11928  *
11929  * Fork - LGPL
11930  * <script type="text/javascript">
11931  */
11932  
11933 // Base class for reading structured data from a data source.  This class is intended to be
11934 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11935
11936 /**
11937  * @class Roo.data.DataReader
11938  * Base class for reading structured data from a data source.  This class is intended to be
11939  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11940  */
11941
11942 Roo.data.DataReader = function(meta, recordType){
11943     
11944     this.meta = meta;
11945     
11946     this.recordType = recordType instanceof Array ? 
11947         Roo.data.Record.create(recordType) : recordType;
11948 };
11949
11950 Roo.data.DataReader.prototype = {
11951      /**
11952      * Create an empty record
11953      * @param {Object} data (optional) - overlay some values
11954      * @return {Roo.data.Record} record created.
11955      */
11956     newRow :  function(d) {
11957         var da =  {};
11958         this.recordType.prototype.fields.each(function(c) {
11959             switch( c.type) {
11960                 case 'int' : da[c.name] = 0; break;
11961                 case 'date' : da[c.name] = new Date(); break;
11962                 case 'float' : da[c.name] = 0.0; break;
11963                 case 'boolean' : da[c.name] = false; break;
11964                 default : da[c.name] = ""; break;
11965             }
11966             
11967         });
11968         return new this.recordType(Roo.apply(da, d));
11969     }
11970     
11971 };/*
11972  * Based on:
11973  * Ext JS Library 1.1.1
11974  * Copyright(c) 2006-2007, Ext JS, LLC.
11975  *
11976  * Originally Released Under LGPL - original licence link has changed is not relivant.
11977  *
11978  * Fork - LGPL
11979  * <script type="text/javascript">
11980  */
11981
11982 /**
11983  * @class Roo.data.DataProxy
11984  * @extends Roo.data.Observable
11985  * This class is an abstract base class for implementations which provide retrieval of
11986  * unformatted data objects.<br>
11987  * <p>
11988  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11989  * (of the appropriate type which knows how to parse the data object) to provide a block of
11990  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11991  * <p>
11992  * Custom implementations must implement the load method as described in
11993  * {@link Roo.data.HttpProxy#load}.
11994  */
11995 Roo.data.DataProxy = function(){
11996     this.addEvents({
11997         /**
11998          * @event beforeload
11999          * Fires before a network request is made to retrieve a data object.
12000          * @param {Object} This DataProxy object.
12001          * @param {Object} params The params parameter to the load function.
12002          */
12003         beforeload : true,
12004         /**
12005          * @event load
12006          * Fires before the load method's callback is called.
12007          * @param {Object} This DataProxy object.
12008          * @param {Object} o The data object.
12009          * @param {Object} arg The callback argument object passed to the load function.
12010          */
12011         load : true,
12012         /**
12013          * @event loadexception
12014          * Fires if an Exception occurs during data retrieval.
12015          * @param {Object} This DataProxy object.
12016          * @param {Object} o The data object.
12017          * @param {Object} arg The callback argument object passed to the load function.
12018          * @param {Object} e The Exception.
12019          */
12020         loadexception : true
12021     });
12022     Roo.data.DataProxy.superclass.constructor.call(this);
12023 };
12024
12025 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12026
12027     /**
12028      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12029      */
12030 /*
12031  * Based on:
12032  * Ext JS Library 1.1.1
12033  * Copyright(c) 2006-2007, Ext JS, LLC.
12034  *
12035  * Originally Released Under LGPL - original licence link has changed is not relivant.
12036  *
12037  * Fork - LGPL
12038  * <script type="text/javascript">
12039  */
12040 /**
12041  * @class Roo.data.MemoryProxy
12042  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12043  * to the Reader when its load method is called.
12044  * @constructor
12045  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12046  */
12047 Roo.data.MemoryProxy = function(data){
12048     if (data.data) {
12049         data = data.data;
12050     }
12051     Roo.data.MemoryProxy.superclass.constructor.call(this);
12052     this.data = data;
12053 };
12054
12055 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12056     
12057     /**
12058      * Load data from the requested source (in this case an in-memory
12059      * data object passed to the constructor), read the data object into
12060      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12061      * process that block using the passed callback.
12062      * @param {Object} params This parameter is not used by the MemoryProxy class.
12063      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12064      * object into a block of Roo.data.Records.
12065      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12066      * The function must be passed <ul>
12067      * <li>The Record block object</li>
12068      * <li>The "arg" argument from the load function</li>
12069      * <li>A boolean success indicator</li>
12070      * </ul>
12071      * @param {Object} scope The scope in which to call the callback
12072      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12073      */
12074     load : function(params, reader, callback, scope, arg){
12075         params = params || {};
12076         var result;
12077         try {
12078             result = reader.readRecords(this.data);
12079         }catch(e){
12080             this.fireEvent("loadexception", this, arg, null, e);
12081             callback.call(scope, null, arg, false);
12082             return;
12083         }
12084         callback.call(scope, result, arg, true);
12085     },
12086     
12087     // private
12088     update : function(params, records){
12089         
12090     }
12091 });/*
12092  * Based on:
12093  * Ext JS Library 1.1.1
12094  * Copyright(c) 2006-2007, Ext JS, LLC.
12095  *
12096  * Originally Released Under LGPL - original licence link has changed is not relivant.
12097  *
12098  * Fork - LGPL
12099  * <script type="text/javascript">
12100  */
12101 /**
12102  * @class Roo.data.HttpProxy
12103  * @extends Roo.data.DataProxy
12104  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12105  * configured to reference a certain URL.<br><br>
12106  * <p>
12107  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12108  * from which the running page was served.<br><br>
12109  * <p>
12110  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12111  * <p>
12112  * Be aware that to enable the browser to parse an XML document, the server must set
12113  * the Content-Type header in the HTTP response to "text/xml".
12114  * @constructor
12115  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12116  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12117  * will be used to make the request.
12118  */
12119 Roo.data.HttpProxy = function(conn){
12120     Roo.data.HttpProxy.superclass.constructor.call(this);
12121     // is conn a conn config or a real conn?
12122     this.conn = conn;
12123     this.useAjax = !conn || !conn.events;
12124   
12125 };
12126
12127 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12128     // thse are take from connection...
12129     
12130     /**
12131      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12132      */
12133     /**
12134      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12135      * extra parameters to each request made by this object. (defaults to undefined)
12136      */
12137     /**
12138      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12139      *  to each request made by this object. (defaults to undefined)
12140      */
12141     /**
12142      * @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)
12143      */
12144     /**
12145      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12146      */
12147      /**
12148      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12149      * @type Boolean
12150      */
12151   
12152
12153     /**
12154      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12155      * @type Boolean
12156      */
12157     /**
12158      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12159      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12160      * a finer-grained basis than the DataProxy events.
12161      */
12162     getConnection : function(){
12163         return this.useAjax ? Roo.Ajax : this.conn;
12164     },
12165
12166     /**
12167      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12168      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12169      * process that block using the passed callback.
12170      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12171      * for the request to the remote server.
12172      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12173      * object into a block of Roo.data.Records.
12174      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12175      * The function must be passed <ul>
12176      * <li>The Record block object</li>
12177      * <li>The "arg" argument from the load function</li>
12178      * <li>A boolean success indicator</li>
12179      * </ul>
12180      * @param {Object} scope The scope in which to call the callback
12181      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12182      */
12183     load : function(params, reader, callback, scope, arg){
12184         if(this.fireEvent("beforeload", this, params) !== false){
12185             var  o = {
12186                 params : params || {},
12187                 request: {
12188                     callback : callback,
12189                     scope : scope,
12190                     arg : arg
12191                 },
12192                 reader: reader,
12193                 callback : this.loadResponse,
12194                 scope: this
12195             };
12196             if(this.useAjax){
12197                 Roo.applyIf(o, this.conn);
12198                 if(this.activeRequest){
12199                     Roo.Ajax.abort(this.activeRequest);
12200                 }
12201                 this.activeRequest = Roo.Ajax.request(o);
12202             }else{
12203                 this.conn.request(o);
12204             }
12205         }else{
12206             callback.call(scope||this, null, arg, false);
12207         }
12208     },
12209
12210     // private
12211     loadResponse : function(o, success, response){
12212         delete this.activeRequest;
12213         if(!success){
12214             this.fireEvent("loadexception", this, o, response);
12215             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12216             return;
12217         }
12218         var result;
12219         try {
12220             result = o.reader.read(response);
12221         }catch(e){
12222             this.fireEvent("loadexception", this, o, response, e);
12223             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12224             return;
12225         }
12226         
12227         this.fireEvent("load", this, o, o.request.arg);
12228         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12229     },
12230
12231     // private
12232     update : function(dataSet){
12233
12234     },
12235
12236     // private
12237     updateResponse : function(dataSet){
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 /**
12252  * @class Roo.data.ScriptTagProxy
12253  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12254  * other than the originating domain of the running page.<br><br>
12255  * <p>
12256  * <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
12257  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12258  * <p>
12259  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12260  * source code that is used as the source inside a &lt;script> tag.<br><br>
12261  * <p>
12262  * In order for the browser to process the returned data, the server must wrap the data object
12263  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12264  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12265  * depending on whether the callback name was passed:
12266  * <p>
12267  * <pre><code>
12268 boolean scriptTag = false;
12269 String cb = request.getParameter("callback");
12270 if (cb != null) {
12271     scriptTag = true;
12272     response.setContentType("text/javascript");
12273 } else {
12274     response.setContentType("application/x-json");
12275 }
12276 Writer out = response.getWriter();
12277 if (scriptTag) {
12278     out.write(cb + "(");
12279 }
12280 out.print(dataBlock.toJsonString());
12281 if (scriptTag) {
12282     out.write(");");
12283 }
12284 </pre></code>
12285  *
12286  * @constructor
12287  * @param {Object} config A configuration object.
12288  */
12289 Roo.data.ScriptTagProxy = function(config){
12290     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12291     Roo.apply(this, config);
12292     this.head = document.getElementsByTagName("head")[0];
12293 };
12294
12295 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12296
12297 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12298     /**
12299      * @cfg {String} url The URL from which to request the data object.
12300      */
12301     /**
12302      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12303      */
12304     timeout : 30000,
12305     /**
12306      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12307      * the server the name of the callback function set up by the load call to process the returned data object.
12308      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12309      * javascript output which calls this named function passing the data object as its only parameter.
12310      */
12311     callbackParam : "callback",
12312     /**
12313      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12314      * name to the request.
12315      */
12316     nocache : true,
12317
12318     /**
12319      * Load data from the configured URL, read the data object into
12320      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12321      * process that block using the passed callback.
12322      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12323      * for the request to the remote server.
12324      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12325      * object into a block of Roo.data.Records.
12326      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12327      * The function must be passed <ul>
12328      * <li>The Record block object</li>
12329      * <li>The "arg" argument from the load function</li>
12330      * <li>A boolean success indicator</li>
12331      * </ul>
12332      * @param {Object} scope The scope in which to call the callback
12333      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12334      */
12335     load : function(params, reader, callback, scope, arg){
12336         if(this.fireEvent("beforeload", this, params) !== false){
12337
12338             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12339
12340             var url = this.url;
12341             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12342             if(this.nocache){
12343                 url += "&_dc=" + (new Date().getTime());
12344             }
12345             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12346             var trans = {
12347                 id : transId,
12348                 cb : "stcCallback"+transId,
12349                 scriptId : "stcScript"+transId,
12350                 params : params,
12351                 arg : arg,
12352                 url : url,
12353                 callback : callback,
12354                 scope : scope,
12355                 reader : reader
12356             };
12357             var conn = this;
12358
12359             window[trans.cb] = function(o){
12360                 conn.handleResponse(o, trans);
12361             };
12362
12363             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12364
12365             if(this.autoAbort !== false){
12366                 this.abort();
12367             }
12368
12369             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12370
12371             var script = document.createElement("script");
12372             script.setAttribute("src", url);
12373             script.setAttribute("type", "text/javascript");
12374             script.setAttribute("id", trans.scriptId);
12375             this.head.appendChild(script);
12376
12377             this.trans = trans;
12378         }else{
12379             callback.call(scope||this, null, arg, false);
12380         }
12381     },
12382
12383     // private
12384     isLoading : function(){
12385         return this.trans ? true : false;
12386     },
12387
12388     /**
12389      * Abort the current server request.
12390      */
12391     abort : function(){
12392         if(this.isLoading()){
12393             this.destroyTrans(this.trans);
12394         }
12395     },
12396
12397     // private
12398     destroyTrans : function(trans, isLoaded){
12399         this.head.removeChild(document.getElementById(trans.scriptId));
12400         clearTimeout(trans.timeoutId);
12401         if(isLoaded){
12402             window[trans.cb] = undefined;
12403             try{
12404                 delete window[trans.cb];
12405             }catch(e){}
12406         }else{
12407             // if hasn't been loaded, wait for load to remove it to prevent script error
12408             window[trans.cb] = function(){
12409                 window[trans.cb] = undefined;
12410                 try{
12411                     delete window[trans.cb];
12412                 }catch(e){}
12413             };
12414         }
12415     },
12416
12417     // private
12418     handleResponse : function(o, trans){
12419         this.trans = false;
12420         this.destroyTrans(trans, true);
12421         var result;
12422         try {
12423             result = trans.reader.readRecords(o);
12424         }catch(e){
12425             this.fireEvent("loadexception", this, o, trans.arg, e);
12426             trans.callback.call(trans.scope||window, null, trans.arg, false);
12427             return;
12428         }
12429         this.fireEvent("load", this, o, trans.arg);
12430         trans.callback.call(trans.scope||window, result, trans.arg, true);
12431     },
12432
12433     // private
12434     handleFailure : function(trans){
12435         this.trans = false;
12436         this.destroyTrans(trans, false);
12437         this.fireEvent("loadexception", this, null, trans.arg);
12438         trans.callback.call(trans.scope||window, null, trans.arg, false);
12439     }
12440 });/*
12441  * Based on:
12442  * Ext JS Library 1.1.1
12443  * Copyright(c) 2006-2007, Ext JS, LLC.
12444  *
12445  * Originally Released Under LGPL - original licence link has changed is not relivant.
12446  *
12447  * Fork - LGPL
12448  * <script type="text/javascript">
12449  */
12450
12451 /**
12452  * @class Roo.data.JsonReader
12453  * @extends Roo.data.DataReader
12454  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12455  * based on mappings in a provided Roo.data.Record constructor.
12456  * 
12457  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12458  * in the reply previously. 
12459  * 
12460  * <p>
12461  * Example code:
12462  * <pre><code>
12463 var RecordDef = Roo.data.Record.create([
12464     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12465     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12466 ]);
12467 var myReader = new Roo.data.JsonReader({
12468     totalProperty: "results",    // The property which contains the total dataset size (optional)
12469     root: "rows",                // The property which contains an Array of row objects
12470     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12471 }, RecordDef);
12472 </code></pre>
12473  * <p>
12474  * This would consume a JSON file like this:
12475  * <pre><code>
12476 { 'results': 2, 'rows': [
12477     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12478     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12479 }
12480 </code></pre>
12481  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12482  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12483  * paged from the remote server.
12484  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12485  * @cfg {String} root name of the property which contains the Array of row objects.
12486  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12487  * @cfg {Array} fields Array of field definition objects
12488  * @constructor
12489  * Create a new JsonReader
12490  * @param {Object} meta Metadata configuration options
12491  * @param {Object} recordType Either an Array of field definition objects,
12492  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12493  */
12494 Roo.data.JsonReader = function(meta, recordType){
12495     
12496     meta = meta || {};
12497     // set some defaults:
12498     Roo.applyIf(meta, {
12499         totalProperty: 'total',
12500         successProperty : 'success',
12501         root : 'data',
12502         id : 'id'
12503     });
12504     
12505     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12506 };
12507 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12508     
12509     /**
12510      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12511      * Used by Store query builder to append _requestMeta to params.
12512      * 
12513      */
12514     metaFromRemote : false,
12515     /**
12516      * This method is only used by a DataProxy which has retrieved data from a remote server.
12517      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12518      * @return {Object} data A data block which is used by an Roo.data.Store object as
12519      * a cache of Roo.data.Records.
12520      */
12521     read : function(response){
12522         var json = response.responseText;
12523        
12524         var o = /* eval:var:o */ eval("("+json+")");
12525         if(!o) {
12526             throw {message: "JsonReader.read: Json object not found"};
12527         }
12528         
12529         if(o.metaData){
12530             
12531             delete this.ef;
12532             this.metaFromRemote = true;
12533             this.meta = o.metaData;
12534             this.recordType = Roo.data.Record.create(o.metaData.fields);
12535             this.onMetaChange(this.meta, this.recordType, o);
12536         }
12537         return this.readRecords(o);
12538     },
12539
12540     // private function a store will implement
12541     onMetaChange : function(meta, recordType, o){
12542
12543     },
12544
12545     /**
12546          * @ignore
12547          */
12548     simpleAccess: function(obj, subsc) {
12549         return obj[subsc];
12550     },
12551
12552         /**
12553          * @ignore
12554          */
12555     getJsonAccessor: function(){
12556         var re = /[\[\.]/;
12557         return function(expr) {
12558             try {
12559                 return(re.test(expr))
12560                     ? new Function("obj", "return obj." + expr)
12561                     : function(obj){
12562                         return obj[expr];
12563                     };
12564             } catch(e){}
12565             return Roo.emptyFn;
12566         };
12567     }(),
12568
12569     /**
12570      * Create a data block containing Roo.data.Records from an XML document.
12571      * @param {Object} o An object which contains an Array of row objects in the property specified
12572      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12573      * which contains the total size of the dataset.
12574      * @return {Object} data A data block which is used by an Roo.data.Store object as
12575      * a cache of Roo.data.Records.
12576      */
12577     readRecords : function(o){
12578         /**
12579          * After any data loads, the raw JSON data is available for further custom processing.
12580          * @type Object
12581          */
12582         this.o = o;
12583         var s = this.meta, Record = this.recordType,
12584             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12585
12586 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12587         if (!this.ef) {
12588             if(s.totalProperty) {
12589                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12590                 }
12591                 if(s.successProperty) {
12592                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12593                 }
12594                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12595                 if (s.id) {
12596                         var g = this.getJsonAccessor(s.id);
12597                         this.getId = function(rec) {
12598                                 var r = g(rec);  
12599                                 return (r === undefined || r === "") ? null : r;
12600                         };
12601                 } else {
12602                         this.getId = function(){return null;};
12603                 }
12604             this.ef = [];
12605             for(var jj = 0; jj < fl; jj++){
12606                 f = fi[jj];
12607                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12608                 this.ef[jj] = this.getJsonAccessor(map);
12609             }
12610         }
12611
12612         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12613         if(s.totalProperty){
12614             var vt = parseInt(this.getTotal(o), 10);
12615             if(!isNaN(vt)){
12616                 totalRecords = vt;
12617             }
12618         }
12619         if(s.successProperty){
12620             var vs = this.getSuccess(o);
12621             if(vs === false || vs === 'false'){
12622                 success = false;
12623             }
12624         }
12625         var records = [];
12626         for(var i = 0; i < c; i++){
12627                 var n = root[i];
12628             var values = {};
12629             var id = this.getId(n);
12630             for(var j = 0; j < fl; j++){
12631                 f = fi[j];
12632             var v = this.ef[j](n);
12633             if (!f.convert) {
12634                 Roo.log('missing convert for ' + f.name);
12635                 Roo.log(f);
12636                 continue;
12637             }
12638             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12639             }
12640             var record = new Record(values, id);
12641             record.json = n;
12642             records[i] = record;
12643         }
12644         return {
12645             raw : o,
12646             success : success,
12647             records : records,
12648             totalRecords : totalRecords
12649         };
12650     }
12651 });/*
12652  * Based on:
12653  * Ext JS Library 1.1.1
12654  * Copyright(c) 2006-2007, Ext JS, LLC.
12655  *
12656  * Originally Released Under LGPL - original licence link has changed is not relivant.
12657  *
12658  * Fork - LGPL
12659  * <script type="text/javascript">
12660  */
12661
12662 /**
12663  * @class Roo.data.ArrayReader
12664  * @extends Roo.data.DataReader
12665  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12666  * Each element of that Array represents a row of data fields. The
12667  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12668  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12669  * <p>
12670  * Example code:.
12671  * <pre><code>
12672 var RecordDef = Roo.data.Record.create([
12673     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12674     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12675 ]);
12676 var myReader = new Roo.data.ArrayReader({
12677     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12678 }, RecordDef);
12679 </code></pre>
12680  * <p>
12681  * This would consume an Array like this:
12682  * <pre><code>
12683 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12684   </code></pre>
12685  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12686  * @constructor
12687  * Create a new JsonReader
12688  * @param {Object} meta Metadata configuration options.
12689  * @param {Object} recordType Either an Array of field definition objects
12690  * as specified to {@link Roo.data.Record#create},
12691  * or an {@link Roo.data.Record} object
12692  * created using {@link Roo.data.Record#create}.
12693  */
12694 Roo.data.ArrayReader = function(meta, recordType){
12695     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12696 };
12697
12698 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12699     /**
12700      * Create a data block containing Roo.data.Records from an XML document.
12701      * @param {Object} o An Array of row objects which represents the dataset.
12702      * @return {Object} data A data block which is used by an Roo.data.Store object as
12703      * a cache of Roo.data.Records.
12704      */
12705     readRecords : function(o){
12706         var sid = this.meta ? this.meta.id : null;
12707         var recordType = this.recordType, fields = recordType.prototype.fields;
12708         var records = [];
12709         var root = o;
12710             for(var i = 0; i < root.length; i++){
12711                     var n = root[i];
12712                 var values = {};
12713                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12714                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12715                 var f = fields.items[j];
12716                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12717                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12718                 v = f.convert(v);
12719                 values[f.name] = v;
12720             }
12721                 var record = new recordType(values, id);
12722                 record.json = n;
12723                 records[records.length] = record;
12724             }
12725             return {
12726                 records : records,
12727                 totalRecords : records.length
12728             };
12729     }
12730 });/*
12731  * - LGPL
12732  * * 
12733  */
12734
12735 /**
12736  * @class Roo.bootstrap.ComboBox
12737  * @extends Roo.bootstrap.TriggerField
12738  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12739  * @cfg {Boolean} append (true|false) default false
12740  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12741  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12742  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12743  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12744  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12745  * @cfg {Boolean} animate default true
12746  * @cfg {Boolean} emptyResultText only for touch device
12747  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12748  * @cfg {String} emptyTitle default ''
12749  * @constructor
12750  * Create a new ComboBox.
12751  * @param {Object} config Configuration options
12752  */
12753 Roo.bootstrap.ComboBox = function(config){
12754     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12755     this.addEvents({
12756         /**
12757          * @event expand
12758          * Fires when the dropdown list is expanded
12759         * @param {Roo.bootstrap.ComboBox} combo This combo box
12760         */
12761         'expand' : true,
12762         /**
12763          * @event collapse
12764          * Fires when the dropdown list is collapsed
12765         * @param {Roo.bootstrap.ComboBox} combo This combo box
12766         */
12767         'collapse' : true,
12768         /**
12769          * @event beforeselect
12770          * Fires before a list item is selected. Return false to cancel the selection.
12771         * @param {Roo.bootstrap.ComboBox} combo This combo box
12772         * @param {Roo.data.Record} record The data record returned from the underlying store
12773         * @param {Number} index The index of the selected item in the dropdown list
12774         */
12775         'beforeselect' : true,
12776         /**
12777          * @event select
12778          * Fires when a list item is selected
12779         * @param {Roo.bootstrap.ComboBox} combo This combo box
12780         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12781         * @param {Number} index The index of the selected item in the dropdown list
12782         */
12783         'select' : true,
12784         /**
12785          * @event beforequery
12786          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12787          * The event object passed has these properties:
12788         * @param {Roo.bootstrap.ComboBox} combo This combo box
12789         * @param {String} query The query
12790         * @param {Boolean} forceAll true to force "all" query
12791         * @param {Boolean} cancel true to cancel the query
12792         * @param {Object} e The query event object
12793         */
12794         'beforequery': true,
12795          /**
12796          * @event add
12797          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12798         * @param {Roo.bootstrap.ComboBox} combo This combo box
12799         */
12800         'add' : true,
12801         /**
12802          * @event edit
12803          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12804         * @param {Roo.bootstrap.ComboBox} combo This combo box
12805         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12806         */
12807         'edit' : true,
12808         /**
12809          * @event remove
12810          * Fires when the remove value from the combobox array
12811         * @param {Roo.bootstrap.ComboBox} combo This combo box
12812         */
12813         'remove' : true,
12814         /**
12815          * @event afterremove
12816          * Fires when the remove value from the combobox array
12817         * @param {Roo.bootstrap.ComboBox} combo This combo box
12818         */
12819         'afterremove' : true,
12820         /**
12821          * @event specialfilter
12822          * Fires when specialfilter
12823             * @param {Roo.bootstrap.ComboBox} combo This combo box
12824             */
12825         'specialfilter' : true,
12826         /**
12827          * @event tick
12828          * Fires when tick the element
12829             * @param {Roo.bootstrap.ComboBox} combo This combo box
12830             */
12831         'tick' : true,
12832         /**
12833          * @event touchviewdisplay
12834          * Fires when touch view require special display (default is using displayField)
12835             * @param {Roo.bootstrap.ComboBox} combo This combo box
12836             * @param {Object} cfg set html .
12837             */
12838         'touchviewdisplay' : true
12839         
12840     });
12841     
12842     this.item = [];
12843     this.tickItems = [];
12844     
12845     this.selectedIndex = -1;
12846     if(this.mode == 'local'){
12847         if(config.queryDelay === undefined){
12848             this.queryDelay = 10;
12849         }
12850         if(config.minChars === undefined){
12851             this.minChars = 0;
12852         }
12853     }
12854 };
12855
12856 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12857      
12858     /**
12859      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12860      * rendering into an Roo.Editor, defaults to false)
12861      */
12862     /**
12863      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12864      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12865      */
12866     /**
12867      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12868      */
12869     /**
12870      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12871      * the dropdown list (defaults to undefined, with no header element)
12872      */
12873
12874      /**
12875      * @cfg {String/Roo.Template} tpl The template to use to render the output
12876      */
12877      
12878      /**
12879      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12880      */
12881     listWidth: undefined,
12882     /**
12883      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12884      * mode = 'remote' or 'text' if mode = 'local')
12885      */
12886     displayField: undefined,
12887     
12888     /**
12889      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12890      * mode = 'remote' or 'value' if mode = 'local'). 
12891      * Note: use of a valueField requires the user make a selection
12892      * in order for a value to be mapped.
12893      */
12894     valueField: undefined,
12895     /**
12896      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12897      */
12898     modalTitle : '',
12899     
12900     /**
12901      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12902      * field's data value (defaults to the underlying DOM element's name)
12903      */
12904     hiddenName: undefined,
12905     /**
12906      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12907      */
12908     listClass: '',
12909     /**
12910      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12911      */
12912     selectedClass: 'active',
12913     
12914     /**
12915      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12916      */
12917     shadow:'sides',
12918     /**
12919      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12920      * anchor positions (defaults to 'tl-bl')
12921      */
12922     listAlign: 'tl-bl?',
12923     /**
12924      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12925      */
12926     maxHeight: 300,
12927     /**
12928      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12929      * query specified by the allQuery config option (defaults to 'query')
12930      */
12931     triggerAction: 'query',
12932     /**
12933      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12934      * (defaults to 4, does not apply if editable = false)
12935      */
12936     minChars : 4,
12937     /**
12938      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12939      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12940      */
12941     typeAhead: false,
12942     /**
12943      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12944      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12945      */
12946     queryDelay: 500,
12947     /**
12948      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12949      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12950      */
12951     pageSize: 0,
12952     /**
12953      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12954      * when editable = true (defaults to false)
12955      */
12956     selectOnFocus:false,
12957     /**
12958      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12959      */
12960     queryParam: 'query',
12961     /**
12962      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12963      * when mode = 'remote' (defaults to 'Loading...')
12964      */
12965     loadingText: 'Loading...',
12966     /**
12967      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12968      */
12969     resizable: false,
12970     /**
12971      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12972      */
12973     handleHeight : 8,
12974     /**
12975      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12976      * traditional select (defaults to true)
12977      */
12978     editable: true,
12979     /**
12980      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12981      */
12982     allQuery: '',
12983     /**
12984      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12985      */
12986     mode: 'remote',
12987     /**
12988      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12989      * listWidth has a higher value)
12990      */
12991     minListWidth : 70,
12992     /**
12993      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12994      * allow the user to set arbitrary text into the field (defaults to false)
12995      */
12996     forceSelection:false,
12997     /**
12998      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12999      * if typeAhead = true (defaults to 250)
13000      */
13001     typeAheadDelay : 250,
13002     /**
13003      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13004      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13005      */
13006     valueNotFoundText : undefined,
13007     /**
13008      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13009      */
13010     blockFocus : false,
13011     
13012     /**
13013      * @cfg {Boolean} disableClear Disable showing of clear button.
13014      */
13015     disableClear : false,
13016     /**
13017      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13018      */
13019     alwaysQuery : false,
13020     
13021     /**
13022      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13023      */
13024     multiple : false,
13025     
13026     /**
13027      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13028      */
13029     invalidClass : "has-warning",
13030     
13031     /**
13032      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13033      */
13034     validClass : "has-success",
13035     
13036     /**
13037      * @cfg {Boolean} specialFilter (true|false) special filter default false
13038      */
13039     specialFilter : false,
13040     
13041     /**
13042      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13043      */
13044     mobileTouchView : true,
13045     
13046     /**
13047      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13048      */
13049     useNativeIOS : false,
13050     
13051     /**
13052      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13053      */
13054     mobile_restrict_height : false,
13055     
13056     ios_options : false,
13057     
13058     //private
13059     addicon : false,
13060     editicon: false,
13061     
13062     page: 0,
13063     hasQuery: false,
13064     append: false,
13065     loadNext: false,
13066     autoFocus : true,
13067     tickable : false,
13068     btnPosition : 'right',
13069     triggerList : true,
13070     showToggleBtn : true,
13071     animate : true,
13072     emptyResultText: 'Empty',
13073     triggerText : 'Select',
13074     emptyTitle : '',
13075     
13076     // element that contains real text value.. (when hidden is used..)
13077     
13078     getAutoCreate : function()
13079     {   
13080         var cfg = false;
13081         //render
13082         /*
13083          * Render classic select for iso
13084          */
13085         
13086         if(Roo.isIOS && this.useNativeIOS){
13087             cfg = this.getAutoCreateNativeIOS();
13088             return cfg;
13089         }
13090         
13091         /*
13092          * Touch Devices
13093          */
13094         
13095         if(Roo.isTouch && this.mobileTouchView){
13096             cfg = this.getAutoCreateTouchView();
13097             return cfg;;
13098         }
13099         
13100         /*
13101          *  Normal ComboBox
13102          */
13103         if(!this.tickable){
13104             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13105             return cfg;
13106         }
13107         
13108         /*
13109          *  ComboBox with tickable selections
13110          */
13111              
13112         var align = this.labelAlign || this.parentLabelAlign();
13113         
13114         cfg = {
13115             cls : 'form-group roo-combobox-tickable' //input-group
13116         };
13117         
13118         var btn_text_select = '';
13119         var btn_text_done = '';
13120         var btn_text_cancel = '';
13121         
13122         if (this.btn_text_show) {
13123             btn_text_select = 'Select';
13124             btn_text_done = 'Done';
13125             btn_text_cancel = 'Cancel'; 
13126         }
13127         
13128         var buttons = {
13129             tag : 'div',
13130             cls : 'tickable-buttons',
13131             cn : [
13132                 {
13133                     tag : 'button',
13134                     type : 'button',
13135                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13136                     //html : this.triggerText
13137                     html: btn_text_select
13138                 },
13139                 {
13140                     tag : 'button',
13141                     type : 'button',
13142                     name : 'ok',
13143                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13144                     //html : 'Done'
13145                     html: btn_text_done
13146                 },
13147                 {
13148                     tag : 'button',
13149                     type : 'button',
13150                     name : 'cancel',
13151                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13152                     //html : 'Cancel'
13153                     html: btn_text_cancel
13154                 }
13155             ]
13156         };
13157         
13158         if(this.editable){
13159             buttons.cn.unshift({
13160                 tag: 'input',
13161                 cls: 'roo-select2-search-field-input'
13162             });
13163         }
13164         
13165         var _this = this;
13166         
13167         Roo.each(buttons.cn, function(c){
13168             if (_this.size) {
13169                 c.cls += ' btn-' + _this.size;
13170             }
13171
13172             if (_this.disabled) {
13173                 c.disabled = true;
13174             }
13175         });
13176         
13177         var box = {
13178             tag: 'div',
13179             cn: [
13180                 {
13181                     tag: 'input',
13182                     type : 'hidden',
13183                     cls: 'form-hidden-field'
13184                 },
13185                 {
13186                     tag: 'ul',
13187                     cls: 'roo-select2-choices',
13188                     cn:[
13189                         {
13190                             tag: 'li',
13191                             cls: 'roo-select2-search-field',
13192                             cn: [
13193                                 buttons
13194                             ]
13195                         }
13196                     ]
13197                 }
13198             ]
13199         };
13200         
13201         var combobox = {
13202             cls: 'roo-select2-container input-group roo-select2-container-multi',
13203             cn: [
13204                 box
13205 //                {
13206 //                    tag: 'ul',
13207 //                    cls: 'typeahead typeahead-long dropdown-menu',
13208 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13209 //                }
13210             ]
13211         };
13212         
13213         if(this.hasFeedback && !this.allowBlank){
13214             
13215             var feedback = {
13216                 tag: 'span',
13217                 cls: 'glyphicon form-control-feedback'
13218             };
13219
13220             combobox.cn.push(feedback);
13221         }
13222         
13223         
13224         if (align ==='left' && this.fieldLabel.length) {
13225             
13226             cfg.cls += ' roo-form-group-label-left';
13227             
13228             cfg.cn = [
13229                 {
13230                     tag : 'i',
13231                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13232                     tooltip : 'This field is required'
13233                 },
13234                 {
13235                     tag: 'label',
13236                     'for' :  id,
13237                     cls : 'control-label',
13238                     html : this.fieldLabel
13239
13240                 },
13241                 {
13242                     cls : "", 
13243                     cn: [
13244                         combobox
13245                     ]
13246                 }
13247
13248             ];
13249             
13250             var labelCfg = cfg.cn[1];
13251             var contentCfg = cfg.cn[2];
13252             
13253
13254             if(this.indicatorpos == 'right'){
13255                 
13256                 cfg.cn = [
13257                     {
13258                         tag: 'label',
13259                         'for' :  id,
13260                         cls : 'control-label',
13261                         cn : [
13262                             {
13263                                 tag : 'span',
13264                                 html : this.fieldLabel
13265                             },
13266                             {
13267                                 tag : 'i',
13268                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13269                                 tooltip : 'This field is required'
13270                             }
13271                         ]
13272                     },
13273                     {
13274                         cls : "",
13275                         cn: [
13276                             combobox
13277                         ]
13278                     }
13279
13280                 ];
13281                 
13282                 
13283                 
13284                 labelCfg = cfg.cn[0];
13285                 contentCfg = cfg.cn[1];
13286             
13287             }
13288             
13289             if(this.labelWidth > 12){
13290                 labelCfg.style = "width: " + this.labelWidth + 'px';
13291             }
13292             
13293             if(this.labelWidth < 13 && this.labelmd == 0){
13294                 this.labelmd = this.labelWidth;
13295             }
13296             
13297             if(this.labellg > 0){
13298                 labelCfg.cls += ' col-lg-' + this.labellg;
13299                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13300             }
13301             
13302             if(this.labelmd > 0){
13303                 labelCfg.cls += ' col-md-' + this.labelmd;
13304                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13305             }
13306             
13307             if(this.labelsm > 0){
13308                 labelCfg.cls += ' col-sm-' + this.labelsm;
13309                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13310             }
13311             
13312             if(this.labelxs > 0){
13313                 labelCfg.cls += ' col-xs-' + this.labelxs;
13314                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13315             }
13316                 
13317                 
13318         } else if ( this.fieldLabel.length) {
13319 //                Roo.log(" label");
13320                  cfg.cn = [
13321                     {
13322                         tag : 'i',
13323                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13324                         tooltip : 'This field is required'
13325                     },
13326                     {
13327                         tag: 'label',
13328                         //cls : 'input-group-addon',
13329                         html : this.fieldLabel
13330                     },
13331                     combobox
13332                 ];
13333                 
13334                 if(this.indicatorpos == 'right'){
13335                     cfg.cn = [
13336                         {
13337                             tag: 'label',
13338                             //cls : 'input-group-addon',
13339                             html : this.fieldLabel
13340                         },
13341                         {
13342                             tag : 'i',
13343                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13344                             tooltip : 'This field is required'
13345                         },
13346                         combobox
13347                     ];
13348                     
13349                 }
13350
13351         } else {
13352             
13353 //                Roo.log(" no label && no align");
13354                 cfg = combobox
13355                      
13356                 
13357         }
13358          
13359         var settings=this;
13360         ['xs','sm','md','lg'].map(function(size){
13361             if (settings[size]) {
13362                 cfg.cls += ' col-' + size + '-' + settings[size];
13363             }
13364         });
13365         
13366         return cfg;
13367         
13368     },
13369     
13370     _initEventsCalled : false,
13371     
13372     // private
13373     initEvents: function()
13374     {   
13375         if (this._initEventsCalled) { // as we call render... prevent looping...
13376             return;
13377         }
13378         this._initEventsCalled = true;
13379         
13380         if (!this.store) {
13381             throw "can not find store for combo";
13382         }
13383         
13384         this.indicator = this.indicatorEl();
13385         
13386         this.store = Roo.factory(this.store, Roo.data);
13387         this.store.parent = this;
13388         
13389         // if we are building from html. then this element is so complex, that we can not really
13390         // use the rendered HTML.
13391         // so we have to trash and replace the previous code.
13392         if (Roo.XComponent.build_from_html) {
13393             // remove this element....
13394             var e = this.el.dom, k=0;
13395             while (e ) { e = e.previousSibling;  ++k;}
13396
13397             this.el.remove();
13398             
13399             this.el=false;
13400             this.rendered = false;
13401             
13402             this.render(this.parent().getChildContainer(true), k);
13403         }
13404         
13405         if(Roo.isIOS && this.useNativeIOS){
13406             this.initIOSView();
13407             return;
13408         }
13409         
13410         /*
13411          * Touch Devices
13412          */
13413         
13414         if(Roo.isTouch && this.mobileTouchView){
13415             this.initTouchView();
13416             return;
13417         }
13418         
13419         if(this.tickable){
13420             this.initTickableEvents();
13421             return;
13422         }
13423         
13424         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13425         
13426         if(this.hiddenName){
13427             
13428             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13429             
13430             this.hiddenField.dom.value =
13431                 this.hiddenValue !== undefined ? this.hiddenValue :
13432                 this.value !== undefined ? this.value : '';
13433
13434             // prevent input submission
13435             this.el.dom.removeAttribute('name');
13436             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13437              
13438              
13439         }
13440         //if(Roo.isGecko){
13441         //    this.el.dom.setAttribute('autocomplete', 'off');
13442         //}
13443         
13444         var cls = 'x-combo-list';
13445         
13446         //this.list = new Roo.Layer({
13447         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13448         //});
13449         
13450         var _this = this;
13451         
13452         (function(){
13453             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13454             _this.list.setWidth(lw);
13455         }).defer(100);
13456         
13457         this.list.on('mouseover', this.onViewOver, this);
13458         this.list.on('mousemove', this.onViewMove, this);
13459         this.list.on('scroll', this.onViewScroll, this);
13460         
13461         /*
13462         this.list.swallowEvent('mousewheel');
13463         this.assetHeight = 0;
13464
13465         if(this.title){
13466             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13467             this.assetHeight += this.header.getHeight();
13468         }
13469
13470         this.innerList = this.list.createChild({cls:cls+'-inner'});
13471         this.innerList.on('mouseover', this.onViewOver, this);
13472         this.innerList.on('mousemove', this.onViewMove, this);
13473         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13474         
13475         if(this.allowBlank && !this.pageSize && !this.disableClear){
13476             this.footer = this.list.createChild({cls:cls+'-ft'});
13477             this.pageTb = new Roo.Toolbar(this.footer);
13478            
13479         }
13480         if(this.pageSize){
13481             this.footer = this.list.createChild({cls:cls+'-ft'});
13482             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13483                     {pageSize: this.pageSize});
13484             
13485         }
13486         
13487         if (this.pageTb && this.allowBlank && !this.disableClear) {
13488             var _this = this;
13489             this.pageTb.add(new Roo.Toolbar.Fill(), {
13490                 cls: 'x-btn-icon x-btn-clear',
13491                 text: '&#160;',
13492                 handler: function()
13493                 {
13494                     _this.collapse();
13495                     _this.clearValue();
13496                     _this.onSelect(false, -1);
13497                 }
13498             });
13499         }
13500         if (this.footer) {
13501             this.assetHeight += this.footer.getHeight();
13502         }
13503         */
13504             
13505         if(!this.tpl){
13506             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13507         }
13508
13509         this.view = new Roo.View(this.list, this.tpl, {
13510             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13511         });
13512         //this.view.wrapEl.setDisplayed(false);
13513         this.view.on('click', this.onViewClick, this);
13514         
13515         
13516         this.store.on('beforeload', this.onBeforeLoad, this);
13517         this.store.on('load', this.onLoad, this);
13518         this.store.on('loadexception', this.onLoadException, this);
13519         /*
13520         if(this.resizable){
13521             this.resizer = new Roo.Resizable(this.list,  {
13522                pinned:true, handles:'se'
13523             });
13524             this.resizer.on('resize', function(r, w, h){
13525                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13526                 this.listWidth = w;
13527                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13528                 this.restrictHeight();
13529             }, this);
13530             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13531         }
13532         */
13533         if(!this.editable){
13534             this.editable = true;
13535             this.setEditable(false);
13536         }
13537         
13538         /*
13539         
13540         if (typeof(this.events.add.listeners) != 'undefined') {
13541             
13542             this.addicon = this.wrap.createChild(
13543                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13544        
13545             this.addicon.on('click', function(e) {
13546                 this.fireEvent('add', this);
13547             }, this);
13548         }
13549         if (typeof(this.events.edit.listeners) != 'undefined') {
13550             
13551             this.editicon = this.wrap.createChild(
13552                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13553             if (this.addicon) {
13554                 this.editicon.setStyle('margin-left', '40px');
13555             }
13556             this.editicon.on('click', function(e) {
13557                 
13558                 // we fire even  if inothing is selected..
13559                 this.fireEvent('edit', this, this.lastData );
13560                 
13561             }, this);
13562         }
13563         */
13564         
13565         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13566             "up" : function(e){
13567                 this.inKeyMode = true;
13568                 this.selectPrev();
13569             },
13570
13571             "down" : function(e){
13572                 if(!this.isExpanded()){
13573                     this.onTriggerClick();
13574                 }else{
13575                     this.inKeyMode = true;
13576                     this.selectNext();
13577                 }
13578             },
13579
13580             "enter" : function(e){
13581 //                this.onViewClick();
13582                 //return true;
13583                 this.collapse();
13584                 
13585                 if(this.fireEvent("specialkey", this, e)){
13586                     this.onViewClick(false);
13587                 }
13588                 
13589                 return true;
13590             },
13591
13592             "esc" : function(e){
13593                 this.collapse();
13594             },
13595
13596             "tab" : function(e){
13597                 this.collapse();
13598                 
13599                 if(this.fireEvent("specialkey", this, e)){
13600                     this.onViewClick(false);
13601                 }
13602                 
13603                 return true;
13604             },
13605
13606             scope : this,
13607
13608             doRelay : function(foo, bar, hname){
13609                 if(hname == 'down' || this.scope.isExpanded()){
13610                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13611                 }
13612                 return true;
13613             },
13614
13615             forceKeyDown: true
13616         });
13617         
13618         
13619         this.queryDelay = Math.max(this.queryDelay || 10,
13620                 this.mode == 'local' ? 10 : 250);
13621         
13622         
13623         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13624         
13625         if(this.typeAhead){
13626             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13627         }
13628         if(this.editable !== false){
13629             this.inputEl().on("keyup", this.onKeyUp, this);
13630         }
13631         if(this.forceSelection){
13632             this.inputEl().on('blur', this.doForce, this);
13633         }
13634         
13635         if(this.multiple){
13636             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13637             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13638         }
13639     },
13640     
13641     initTickableEvents: function()
13642     {   
13643         this.createList();
13644         
13645         if(this.hiddenName){
13646             
13647             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13648             
13649             this.hiddenField.dom.value =
13650                 this.hiddenValue !== undefined ? this.hiddenValue :
13651                 this.value !== undefined ? this.value : '';
13652
13653             // prevent input submission
13654             this.el.dom.removeAttribute('name');
13655             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13656              
13657              
13658         }
13659         
13660 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13661         
13662         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13663         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13664         if(this.triggerList){
13665             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13666         }
13667          
13668         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13669         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13670         
13671         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13672         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13673         
13674         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13675         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13676         
13677         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13678         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13679         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13680         
13681         this.okBtn.hide();
13682         this.cancelBtn.hide();
13683         
13684         var _this = this;
13685         
13686         (function(){
13687             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13688             _this.list.setWidth(lw);
13689         }).defer(100);
13690         
13691         this.list.on('mouseover', this.onViewOver, this);
13692         this.list.on('mousemove', this.onViewMove, this);
13693         
13694         this.list.on('scroll', this.onViewScroll, this);
13695         
13696         if(!this.tpl){
13697             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13698                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13699         }
13700
13701         this.view = new Roo.View(this.list, this.tpl, {
13702             singleSelect:true,
13703             tickable:true,
13704             parent:this,
13705             store: this.store,
13706             selectedClass: this.selectedClass
13707         });
13708         
13709         //this.view.wrapEl.setDisplayed(false);
13710         this.view.on('click', this.onViewClick, this);
13711         
13712         
13713         
13714         this.store.on('beforeload', this.onBeforeLoad, this);
13715         this.store.on('load', this.onLoad, this);
13716         this.store.on('loadexception', this.onLoadException, this);
13717         
13718         if(this.editable){
13719             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13720                 "up" : function(e){
13721                     this.inKeyMode = true;
13722                     this.selectPrev();
13723                 },
13724
13725                 "down" : function(e){
13726                     this.inKeyMode = true;
13727                     this.selectNext();
13728                 },
13729
13730                 "enter" : function(e){
13731                     if(this.fireEvent("specialkey", this, e)){
13732                         this.onViewClick(false);
13733                     }
13734                     
13735                     return true;
13736                 },
13737
13738                 "esc" : function(e){
13739                     this.onTickableFooterButtonClick(e, false, false);
13740                 },
13741
13742                 "tab" : function(e){
13743                     this.fireEvent("specialkey", this, e);
13744                     
13745                     this.onTickableFooterButtonClick(e, false, false);
13746                     
13747                     return true;
13748                 },
13749
13750                 scope : this,
13751
13752                 doRelay : function(e, fn, key){
13753                     if(this.scope.isExpanded()){
13754                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13755                     }
13756                     return true;
13757                 },
13758
13759                 forceKeyDown: true
13760             });
13761         }
13762         
13763         this.queryDelay = Math.max(this.queryDelay || 10,
13764                 this.mode == 'local' ? 10 : 250);
13765         
13766         
13767         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13768         
13769         if(this.typeAhead){
13770             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13771         }
13772         
13773         if(this.editable !== false){
13774             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13775         }
13776         
13777         this.indicator = this.indicatorEl();
13778         
13779         if(this.indicator){
13780             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13781             this.indicator.hide();
13782         }
13783         
13784     },
13785
13786     onDestroy : function(){
13787         if(this.view){
13788             this.view.setStore(null);
13789             this.view.el.removeAllListeners();
13790             this.view.el.remove();
13791             this.view.purgeListeners();
13792         }
13793         if(this.list){
13794             this.list.dom.innerHTML  = '';
13795         }
13796         
13797         if(this.store){
13798             this.store.un('beforeload', this.onBeforeLoad, this);
13799             this.store.un('load', this.onLoad, this);
13800             this.store.un('loadexception', this.onLoadException, this);
13801         }
13802         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13803     },
13804
13805     // private
13806     fireKey : function(e){
13807         if(e.isNavKeyPress() && !this.list.isVisible()){
13808             this.fireEvent("specialkey", this, e);
13809         }
13810     },
13811
13812     // private
13813     onResize: function(w, h){
13814 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13815 //        
13816 //        if(typeof w != 'number'){
13817 //            // we do not handle it!?!?
13818 //            return;
13819 //        }
13820 //        var tw = this.trigger.getWidth();
13821 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13822 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13823 //        var x = w - tw;
13824 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13825 //            
13826 //        //this.trigger.setStyle('left', x+'px');
13827 //        
13828 //        if(this.list && this.listWidth === undefined){
13829 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13830 //            this.list.setWidth(lw);
13831 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13832 //        }
13833         
13834     
13835         
13836     },
13837
13838     /**
13839      * Allow or prevent the user from directly editing the field text.  If false is passed,
13840      * the user will only be able to select from the items defined in the dropdown list.  This method
13841      * is the runtime equivalent of setting the 'editable' config option at config time.
13842      * @param {Boolean} value True to allow the user to directly edit the field text
13843      */
13844     setEditable : function(value){
13845         if(value == this.editable){
13846             return;
13847         }
13848         this.editable = value;
13849         if(!value){
13850             this.inputEl().dom.setAttribute('readOnly', true);
13851             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13852             this.inputEl().addClass('x-combo-noedit');
13853         }else{
13854             this.inputEl().dom.setAttribute('readOnly', false);
13855             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13856             this.inputEl().removeClass('x-combo-noedit');
13857         }
13858     },
13859
13860     // private
13861     
13862     onBeforeLoad : function(combo,opts){
13863         if(!this.hasFocus){
13864             return;
13865         }
13866          if (!opts.add) {
13867             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13868          }
13869         this.restrictHeight();
13870         this.selectedIndex = -1;
13871     },
13872
13873     // private
13874     onLoad : function(){
13875         
13876         this.hasQuery = false;
13877         
13878         if(!this.hasFocus){
13879             return;
13880         }
13881         
13882         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13883             this.loading.hide();
13884         }
13885         
13886         if(this.store.getCount() > 0){
13887             
13888             this.expand();
13889             this.restrictHeight();
13890             if(this.lastQuery == this.allQuery){
13891                 if(this.editable && !this.tickable){
13892                     this.inputEl().dom.select();
13893                 }
13894                 
13895                 if(
13896                     !this.selectByValue(this.value, true) &&
13897                     this.autoFocus && 
13898                     (
13899                         !this.store.lastOptions ||
13900                         typeof(this.store.lastOptions.add) == 'undefined' || 
13901                         this.store.lastOptions.add != true
13902                     )
13903                 ){
13904                     this.select(0, true);
13905                 }
13906             }else{
13907                 if(this.autoFocus){
13908                     this.selectNext();
13909                 }
13910                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13911                     this.taTask.delay(this.typeAheadDelay);
13912                 }
13913             }
13914         }else{
13915             this.onEmptyResults();
13916         }
13917         
13918         //this.el.focus();
13919     },
13920     // private
13921     onLoadException : function()
13922     {
13923         this.hasQuery = false;
13924         
13925         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13926             this.loading.hide();
13927         }
13928         
13929         if(this.tickable && this.editable){
13930             return;
13931         }
13932         
13933         this.collapse();
13934         // only causes errors at present
13935         //Roo.log(this.store.reader.jsonData);
13936         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13937             // fixme
13938             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13939         //}
13940         
13941         
13942     },
13943     // private
13944     onTypeAhead : function(){
13945         if(this.store.getCount() > 0){
13946             var r = this.store.getAt(0);
13947             var newValue = r.data[this.displayField];
13948             var len = newValue.length;
13949             var selStart = this.getRawValue().length;
13950             
13951             if(selStart != len){
13952                 this.setRawValue(newValue);
13953                 this.selectText(selStart, newValue.length);
13954             }
13955         }
13956     },
13957
13958     // private
13959     onSelect : function(record, index){
13960         
13961         if(this.fireEvent('beforeselect', this, record, index) !== false){
13962         
13963             this.setFromData(index > -1 ? record.data : false);
13964             
13965             this.collapse();
13966             this.fireEvent('select', this, record, index);
13967         }
13968     },
13969
13970     /**
13971      * Returns the currently selected field value or empty string if no value is set.
13972      * @return {String} value The selected value
13973      */
13974     getValue : function()
13975     {
13976         if(Roo.isIOS && this.useNativeIOS){
13977             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13978         }
13979         
13980         if(this.multiple){
13981             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13982         }
13983         
13984         if(this.valueField){
13985             return typeof this.value != 'undefined' ? this.value : '';
13986         }else{
13987             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13988         }
13989     },
13990     
13991     getRawValue : function()
13992     {
13993         if(Roo.isIOS && this.useNativeIOS){
13994             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13995         }
13996         
13997         var v = this.inputEl().getValue();
13998         
13999         return v;
14000     },
14001
14002     /**
14003      * Clears any text/value currently set in the field
14004      */
14005     clearValue : function(){
14006         
14007         if(this.hiddenField){
14008             this.hiddenField.dom.value = '';
14009         }
14010         this.value = '';
14011         this.setRawValue('');
14012         this.lastSelectionText = '';
14013         this.lastData = false;
14014         
14015         var close = this.closeTriggerEl();
14016         
14017         if(close){
14018             close.hide();
14019         }
14020         
14021         this.validate();
14022         
14023     },
14024
14025     /**
14026      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14027      * will be displayed in the field.  If the value does not match the data value of an existing item,
14028      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14029      * Otherwise the field will be blank (although the value will still be set).
14030      * @param {String} value The value to match
14031      */
14032     setValue : function(v)
14033     {
14034         if(Roo.isIOS && this.useNativeIOS){
14035             this.setIOSValue(v);
14036             return;
14037         }
14038         
14039         if(this.multiple){
14040             this.syncValue();
14041             return;
14042         }
14043         
14044         var text = v;
14045         if(this.valueField){
14046             var r = this.findRecord(this.valueField, v);
14047             if(r){
14048                 text = r.data[this.displayField];
14049             }else if(this.valueNotFoundText !== undefined){
14050                 text = this.valueNotFoundText;
14051             }
14052         }
14053         this.lastSelectionText = text;
14054         if(this.hiddenField){
14055             this.hiddenField.dom.value = v;
14056         }
14057         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14058         this.value = v;
14059         
14060         var close = this.closeTriggerEl();
14061         
14062         if(close){
14063             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14064         }
14065         
14066         this.validate();
14067     },
14068     /**
14069      * @property {Object} the last set data for the element
14070      */
14071     
14072     lastData : false,
14073     /**
14074      * Sets the value of the field based on a object which is related to the record format for the store.
14075      * @param {Object} value the value to set as. or false on reset?
14076      */
14077     setFromData : function(o){
14078         
14079         if(this.multiple){
14080             this.addItem(o);
14081             return;
14082         }
14083             
14084         var dv = ''; // display value
14085         var vv = ''; // value value..
14086         this.lastData = o;
14087         if (this.displayField) {
14088             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14089         } else {
14090             // this is an error condition!!!
14091             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14092         }
14093         
14094         if(this.valueField){
14095             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14096         }
14097         
14098         var close = this.closeTriggerEl();
14099         
14100         if(close){
14101             if(dv.length || vv * 1 > 0){
14102                 close.show() ;
14103                 this.blockFocus=true;
14104             } else {
14105                 close.hide();
14106             }             
14107         }
14108         
14109         if(this.hiddenField){
14110             this.hiddenField.dom.value = vv;
14111             
14112             this.lastSelectionText = dv;
14113             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14114             this.value = vv;
14115             return;
14116         }
14117         // no hidden field.. - we store the value in 'value', but still display
14118         // display field!!!!
14119         this.lastSelectionText = dv;
14120         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14121         this.value = vv;
14122         
14123         
14124         
14125     },
14126     // private
14127     reset : function(){
14128         // overridden so that last data is reset..
14129         
14130         if(this.multiple){
14131             this.clearItem();
14132             return;
14133         }
14134         
14135         this.setValue(this.originalValue);
14136         //this.clearInvalid();
14137         this.lastData = false;
14138         if (this.view) {
14139             this.view.clearSelections();
14140         }
14141         
14142         this.validate();
14143     },
14144     // private
14145     findRecord : function(prop, value){
14146         var record;
14147         if(this.store.getCount() > 0){
14148             this.store.each(function(r){
14149                 if(r.data[prop] == value){
14150                     record = r;
14151                     return false;
14152                 }
14153                 return true;
14154             });
14155         }
14156         return record;
14157     },
14158     
14159     getName: function()
14160     {
14161         // returns hidden if it's set..
14162         if (!this.rendered) {return ''};
14163         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14164         
14165     },
14166     // private
14167     onViewMove : function(e, t){
14168         this.inKeyMode = false;
14169     },
14170
14171     // private
14172     onViewOver : function(e, t){
14173         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14174             return;
14175         }
14176         var item = this.view.findItemFromChild(t);
14177         
14178         if(item){
14179             var index = this.view.indexOf(item);
14180             this.select(index, false);
14181         }
14182     },
14183
14184     // private
14185     onViewClick : function(view, doFocus, el, e)
14186     {
14187         var index = this.view.getSelectedIndexes()[0];
14188         
14189         var r = this.store.getAt(index);
14190         
14191         if(this.tickable){
14192             
14193             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14194                 return;
14195             }
14196             
14197             var rm = false;
14198             var _this = this;
14199             
14200             Roo.each(this.tickItems, function(v,k){
14201                 
14202                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14203                     Roo.log(v);
14204                     _this.tickItems.splice(k, 1);
14205                     
14206                     if(typeof(e) == 'undefined' && view == false){
14207                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14208                     }
14209                     
14210                     rm = true;
14211                     return;
14212                 }
14213             });
14214             
14215             if(rm){
14216                 return;
14217             }
14218             
14219             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14220                 this.tickItems.push(r.data);
14221             }
14222             
14223             if(typeof(e) == 'undefined' && view == false){
14224                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14225             }
14226                     
14227             return;
14228         }
14229         
14230         if(r){
14231             this.onSelect(r, index);
14232         }
14233         if(doFocus !== false && !this.blockFocus){
14234             this.inputEl().focus();
14235         }
14236     },
14237
14238     // private
14239     restrictHeight : function(){
14240         //this.innerList.dom.style.height = '';
14241         //var inner = this.innerList.dom;
14242         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14243         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14244         //this.list.beginUpdate();
14245         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14246         this.list.alignTo(this.inputEl(), this.listAlign);
14247         this.list.alignTo(this.inputEl(), this.listAlign);
14248         //this.list.endUpdate();
14249     },
14250
14251     // private
14252     onEmptyResults : function(){
14253         
14254         if(this.tickable && this.editable){
14255             this.hasFocus = false;
14256             this.restrictHeight();
14257             return;
14258         }
14259         
14260         this.collapse();
14261     },
14262
14263     /**
14264      * Returns true if the dropdown list is expanded, else false.
14265      */
14266     isExpanded : function(){
14267         return this.list.isVisible();
14268     },
14269
14270     /**
14271      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14272      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14273      * @param {String} value The data value of the item to select
14274      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14275      * selected item if it is not currently in view (defaults to true)
14276      * @return {Boolean} True if the value matched an item in the list, else false
14277      */
14278     selectByValue : function(v, scrollIntoView){
14279         if(v !== undefined && v !== null){
14280             var r = this.findRecord(this.valueField || this.displayField, v);
14281             if(r){
14282                 this.select(this.store.indexOf(r), scrollIntoView);
14283                 return true;
14284             }
14285         }
14286         return false;
14287     },
14288
14289     /**
14290      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14291      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14292      * @param {Number} index The zero-based index of the list item to select
14293      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14294      * selected item if it is not currently in view (defaults to true)
14295      */
14296     select : function(index, scrollIntoView){
14297         this.selectedIndex = index;
14298         this.view.select(index);
14299         if(scrollIntoView !== false){
14300             var el = this.view.getNode(index);
14301             /*
14302              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14303              */
14304             if(el){
14305                 this.list.scrollChildIntoView(el, false);
14306             }
14307         }
14308     },
14309
14310     // private
14311     selectNext : function(){
14312         var ct = this.store.getCount();
14313         if(ct > 0){
14314             if(this.selectedIndex == -1){
14315                 this.select(0);
14316             }else if(this.selectedIndex < ct-1){
14317                 this.select(this.selectedIndex+1);
14318             }
14319         }
14320     },
14321
14322     // private
14323     selectPrev : function(){
14324         var ct = this.store.getCount();
14325         if(ct > 0){
14326             if(this.selectedIndex == -1){
14327                 this.select(0);
14328             }else if(this.selectedIndex != 0){
14329                 this.select(this.selectedIndex-1);
14330             }
14331         }
14332     },
14333
14334     // private
14335     onKeyUp : function(e){
14336         if(this.editable !== false && !e.isSpecialKey()){
14337             this.lastKey = e.getKey();
14338             this.dqTask.delay(this.queryDelay);
14339         }
14340     },
14341
14342     // private
14343     validateBlur : function(){
14344         return !this.list || !this.list.isVisible();   
14345     },
14346
14347     // private
14348     initQuery : function(){
14349         
14350         var v = this.getRawValue();
14351         
14352         if(this.tickable && this.editable){
14353             v = this.tickableInputEl().getValue();
14354         }
14355         
14356         this.doQuery(v);
14357     },
14358
14359     // private
14360     doForce : function(){
14361         if(this.inputEl().dom.value.length > 0){
14362             this.inputEl().dom.value =
14363                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14364              
14365         }
14366     },
14367
14368     /**
14369      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14370      * query allowing the query action to be canceled if needed.
14371      * @param {String} query The SQL query to execute
14372      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14373      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14374      * saved in the current store (defaults to false)
14375      */
14376     doQuery : function(q, forceAll){
14377         
14378         if(q === undefined || q === null){
14379             q = '';
14380         }
14381         var qe = {
14382             query: q,
14383             forceAll: forceAll,
14384             combo: this,
14385             cancel:false
14386         };
14387         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14388             return false;
14389         }
14390         q = qe.query;
14391         
14392         forceAll = qe.forceAll;
14393         if(forceAll === true || (q.length >= this.minChars)){
14394             
14395             this.hasQuery = true;
14396             
14397             if(this.lastQuery != q || this.alwaysQuery){
14398                 this.lastQuery = q;
14399                 if(this.mode == 'local'){
14400                     this.selectedIndex = -1;
14401                     if(forceAll){
14402                         this.store.clearFilter();
14403                     }else{
14404                         
14405                         if(this.specialFilter){
14406                             this.fireEvent('specialfilter', this);
14407                             this.onLoad();
14408                             return;
14409                         }
14410                         
14411                         this.store.filter(this.displayField, q);
14412                     }
14413                     
14414                     this.store.fireEvent("datachanged", this.store);
14415                     
14416                     this.onLoad();
14417                     
14418                     
14419                 }else{
14420                     
14421                     this.store.baseParams[this.queryParam] = q;
14422                     
14423                     var options = {params : this.getParams(q)};
14424                     
14425                     if(this.loadNext){
14426                         options.add = true;
14427                         options.params.start = this.page * this.pageSize;
14428                     }
14429                     
14430                     this.store.load(options);
14431                     
14432                     /*
14433                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14434                      *  we should expand the list on onLoad
14435                      *  so command out it
14436                      */
14437 //                    this.expand();
14438                 }
14439             }else{
14440                 this.selectedIndex = -1;
14441                 this.onLoad();   
14442             }
14443         }
14444         
14445         this.loadNext = false;
14446     },
14447     
14448     // private
14449     getParams : function(q){
14450         var p = {};
14451         //p[this.queryParam] = q;
14452         
14453         if(this.pageSize){
14454             p.start = 0;
14455             p.limit = this.pageSize;
14456         }
14457         return p;
14458     },
14459
14460     /**
14461      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14462      */
14463     collapse : function(){
14464         if(!this.isExpanded()){
14465             return;
14466         }
14467         
14468         this.list.hide();
14469         
14470         this.hasFocus = false;
14471         
14472         if(this.tickable){
14473             this.okBtn.hide();
14474             this.cancelBtn.hide();
14475             this.trigger.show();
14476             
14477             if(this.editable){
14478                 this.tickableInputEl().dom.value = '';
14479                 this.tickableInputEl().blur();
14480             }
14481             
14482         }
14483         
14484         Roo.get(document).un('mousedown', this.collapseIf, this);
14485         Roo.get(document).un('mousewheel', this.collapseIf, this);
14486         if (!this.editable) {
14487             Roo.get(document).un('keydown', this.listKeyPress, this);
14488         }
14489         this.fireEvent('collapse', this);
14490         
14491         this.validate();
14492     },
14493
14494     // private
14495     collapseIf : function(e){
14496         var in_combo  = e.within(this.el);
14497         var in_list =  e.within(this.list);
14498         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14499         
14500         if (in_combo || in_list || is_list) {
14501             //e.stopPropagation();
14502             return;
14503         }
14504         
14505         if(this.tickable){
14506             this.onTickableFooterButtonClick(e, false, false);
14507         }
14508
14509         this.collapse();
14510         
14511     },
14512
14513     /**
14514      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14515      */
14516     expand : function(){
14517        
14518         if(this.isExpanded() || !this.hasFocus){
14519             return;
14520         }
14521         
14522         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14523         this.list.setWidth(lw);
14524         
14525         Roo.log('expand');
14526         
14527         this.list.show();
14528         
14529         this.restrictHeight();
14530         
14531         if(this.tickable){
14532             
14533             this.tickItems = Roo.apply([], this.item);
14534             
14535             this.okBtn.show();
14536             this.cancelBtn.show();
14537             this.trigger.hide();
14538             
14539             if(this.editable){
14540                 this.tickableInputEl().focus();
14541             }
14542             
14543         }
14544         
14545         Roo.get(document).on('mousedown', this.collapseIf, this);
14546         Roo.get(document).on('mousewheel', this.collapseIf, this);
14547         if (!this.editable) {
14548             Roo.get(document).on('keydown', this.listKeyPress, this);
14549         }
14550         
14551         this.fireEvent('expand', this);
14552     },
14553
14554     // private
14555     // Implements the default empty TriggerField.onTriggerClick function
14556     onTriggerClick : function(e)
14557     {
14558         Roo.log('trigger click');
14559         
14560         if(this.disabled || !this.triggerList){
14561             return;
14562         }
14563         
14564         this.page = 0;
14565         this.loadNext = false;
14566         
14567         if(this.isExpanded()){
14568             this.collapse();
14569             if (!this.blockFocus) {
14570                 this.inputEl().focus();
14571             }
14572             
14573         }else {
14574             this.hasFocus = true;
14575             if(this.triggerAction == 'all') {
14576                 this.doQuery(this.allQuery, true);
14577             } else {
14578                 this.doQuery(this.getRawValue());
14579             }
14580             if (!this.blockFocus) {
14581                 this.inputEl().focus();
14582             }
14583         }
14584     },
14585     
14586     onTickableTriggerClick : function(e)
14587     {
14588         if(this.disabled){
14589             return;
14590         }
14591         
14592         this.page = 0;
14593         this.loadNext = false;
14594         this.hasFocus = true;
14595         
14596         if(this.triggerAction == 'all') {
14597             this.doQuery(this.allQuery, true);
14598         } else {
14599             this.doQuery(this.getRawValue());
14600         }
14601     },
14602     
14603     onSearchFieldClick : function(e)
14604     {
14605         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14606             this.onTickableFooterButtonClick(e, false, false);
14607             return;
14608         }
14609         
14610         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14611             return;
14612         }
14613         
14614         this.page = 0;
14615         this.loadNext = false;
14616         this.hasFocus = true;
14617         
14618         if(this.triggerAction == 'all') {
14619             this.doQuery(this.allQuery, true);
14620         } else {
14621             this.doQuery(this.getRawValue());
14622         }
14623     },
14624     
14625     listKeyPress : function(e)
14626     {
14627         //Roo.log('listkeypress');
14628         // scroll to first matching element based on key pres..
14629         if (e.isSpecialKey()) {
14630             return false;
14631         }
14632         var k = String.fromCharCode(e.getKey()).toUpperCase();
14633         //Roo.log(k);
14634         var match  = false;
14635         var csel = this.view.getSelectedNodes();
14636         var cselitem = false;
14637         if (csel.length) {
14638             var ix = this.view.indexOf(csel[0]);
14639             cselitem  = this.store.getAt(ix);
14640             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14641                 cselitem = false;
14642             }
14643             
14644         }
14645         
14646         this.store.each(function(v) { 
14647             if (cselitem) {
14648                 // start at existing selection.
14649                 if (cselitem.id == v.id) {
14650                     cselitem = false;
14651                 }
14652                 return true;
14653             }
14654                 
14655             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14656                 match = this.store.indexOf(v);
14657                 return false;
14658             }
14659             return true;
14660         }, this);
14661         
14662         if (match === false) {
14663             return true; // no more action?
14664         }
14665         // scroll to?
14666         this.view.select(match);
14667         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14668         sn.scrollIntoView(sn.dom.parentNode, false);
14669     },
14670     
14671     onViewScroll : function(e, t){
14672         
14673         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){
14674             return;
14675         }
14676         
14677         this.hasQuery = true;
14678         
14679         this.loading = this.list.select('.loading', true).first();
14680         
14681         if(this.loading === null){
14682             this.list.createChild({
14683                 tag: 'div',
14684                 cls: 'loading roo-select2-more-results roo-select2-active',
14685                 html: 'Loading more results...'
14686             });
14687             
14688             this.loading = this.list.select('.loading', true).first();
14689             
14690             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14691             
14692             this.loading.hide();
14693         }
14694         
14695         this.loading.show();
14696         
14697         var _combo = this;
14698         
14699         this.page++;
14700         this.loadNext = true;
14701         
14702         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14703         
14704         return;
14705     },
14706     
14707     addItem : function(o)
14708     {   
14709         var dv = ''; // display value
14710         
14711         if (this.displayField) {
14712             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14713         } else {
14714             // this is an error condition!!!
14715             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14716         }
14717         
14718         if(!dv.length){
14719             return;
14720         }
14721         
14722         var choice = this.choices.createChild({
14723             tag: 'li',
14724             cls: 'roo-select2-search-choice',
14725             cn: [
14726                 {
14727                     tag: 'div',
14728                     html: dv
14729                 },
14730                 {
14731                     tag: 'a',
14732                     href: '#',
14733                     cls: 'roo-select2-search-choice-close fa fa-times',
14734                     tabindex: '-1'
14735                 }
14736             ]
14737             
14738         }, this.searchField);
14739         
14740         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14741         
14742         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14743         
14744         this.item.push(o);
14745         
14746         this.lastData = o;
14747         
14748         this.syncValue();
14749         
14750         this.inputEl().dom.value = '';
14751         
14752         this.validate();
14753     },
14754     
14755     onRemoveItem : function(e, _self, o)
14756     {
14757         e.preventDefault();
14758         
14759         this.lastItem = Roo.apply([], this.item);
14760         
14761         var index = this.item.indexOf(o.data) * 1;
14762         
14763         if( index < 0){
14764             Roo.log('not this item?!');
14765             return;
14766         }
14767         
14768         this.item.splice(index, 1);
14769         o.item.remove();
14770         
14771         this.syncValue();
14772         
14773         this.fireEvent('remove', this, e);
14774         
14775         this.validate();
14776         
14777     },
14778     
14779     syncValue : function()
14780     {
14781         if(!this.item.length){
14782             this.clearValue();
14783             return;
14784         }
14785             
14786         var value = [];
14787         var _this = this;
14788         Roo.each(this.item, function(i){
14789             if(_this.valueField){
14790                 value.push(i[_this.valueField]);
14791                 return;
14792             }
14793
14794             value.push(i);
14795         });
14796
14797         this.value = value.join(',');
14798
14799         if(this.hiddenField){
14800             this.hiddenField.dom.value = this.value;
14801         }
14802         
14803         this.store.fireEvent("datachanged", this.store);
14804         
14805         this.validate();
14806     },
14807     
14808     clearItem : function()
14809     {
14810         if(!this.multiple){
14811             return;
14812         }
14813         
14814         this.item = [];
14815         
14816         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14817            c.remove();
14818         });
14819         
14820         this.syncValue();
14821         
14822         this.validate();
14823         
14824         if(this.tickable && !Roo.isTouch){
14825             this.view.refresh();
14826         }
14827     },
14828     
14829     inputEl: function ()
14830     {
14831         if(Roo.isIOS && this.useNativeIOS){
14832             return this.el.select('select.roo-ios-select', true).first();
14833         }
14834         
14835         if(Roo.isTouch && this.mobileTouchView){
14836             return this.el.select('input.form-control',true).first();
14837         }
14838         
14839         if(this.tickable){
14840             return this.searchField;
14841         }
14842         
14843         return this.el.select('input.form-control',true).first();
14844     },
14845     
14846     onTickableFooterButtonClick : function(e, btn, el)
14847     {
14848         e.preventDefault();
14849         
14850         this.lastItem = Roo.apply([], this.item);
14851         
14852         if(btn && btn.name == 'cancel'){
14853             this.tickItems = Roo.apply([], this.item);
14854             this.collapse();
14855             return;
14856         }
14857         
14858         this.clearItem();
14859         
14860         var _this = this;
14861         
14862         Roo.each(this.tickItems, function(o){
14863             _this.addItem(o);
14864         });
14865         
14866         this.collapse();
14867         
14868     },
14869     
14870     validate : function()
14871     {
14872         if(this.getVisibilityEl().hasClass('hidden')){
14873             return true;
14874         }
14875         
14876         var v = this.getRawValue();
14877         
14878         if(this.multiple){
14879             v = this.getValue();
14880         }
14881         
14882         if(this.disabled || this.allowBlank || v.length){
14883             this.markValid();
14884             return true;
14885         }
14886         
14887         this.markInvalid();
14888         return false;
14889     },
14890     
14891     tickableInputEl : function()
14892     {
14893         if(!this.tickable || !this.editable){
14894             return this.inputEl();
14895         }
14896         
14897         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14898     },
14899     
14900     
14901     getAutoCreateTouchView : function()
14902     {
14903         var id = Roo.id();
14904         
14905         var cfg = {
14906             cls: 'form-group' //input-group
14907         };
14908         
14909         var input =  {
14910             tag: 'input',
14911             id : id,
14912             type : this.inputType,
14913             cls : 'form-control x-combo-noedit',
14914             autocomplete: 'new-password',
14915             placeholder : this.placeholder || '',
14916             readonly : true
14917         };
14918         
14919         if (this.name) {
14920             input.name = this.name;
14921         }
14922         
14923         if (this.size) {
14924             input.cls += ' input-' + this.size;
14925         }
14926         
14927         if (this.disabled) {
14928             input.disabled = true;
14929         }
14930         
14931         var inputblock = {
14932             cls : '',
14933             cn : [
14934                 input
14935             ]
14936         };
14937         
14938         if(this.before){
14939             inputblock.cls += ' input-group';
14940             
14941             inputblock.cn.unshift({
14942                 tag :'span',
14943                 cls : 'input-group-addon',
14944                 html : this.before
14945             });
14946         }
14947         
14948         if(this.removable && !this.multiple){
14949             inputblock.cls += ' roo-removable';
14950             
14951             inputblock.cn.push({
14952                 tag: 'button',
14953                 html : 'x',
14954                 cls : 'roo-combo-removable-btn close'
14955             });
14956         }
14957
14958         if(this.hasFeedback && !this.allowBlank){
14959             
14960             inputblock.cls += ' has-feedback';
14961             
14962             inputblock.cn.push({
14963                 tag: 'span',
14964                 cls: 'glyphicon form-control-feedback'
14965             });
14966             
14967         }
14968         
14969         if (this.after) {
14970             
14971             inputblock.cls += (this.before) ? '' : ' input-group';
14972             
14973             inputblock.cn.push({
14974                 tag :'span',
14975                 cls : 'input-group-addon',
14976                 html : this.after
14977             });
14978         }
14979
14980         var box = {
14981             tag: 'div',
14982             cn: [
14983                 {
14984                     tag: 'input',
14985                     type : 'hidden',
14986                     cls: 'form-hidden-field'
14987                 },
14988                 inputblock
14989             ]
14990             
14991         };
14992         
14993         if(this.multiple){
14994             box = {
14995                 tag: 'div',
14996                 cn: [
14997                     {
14998                         tag: 'input',
14999                         type : 'hidden',
15000                         cls: 'form-hidden-field'
15001                     },
15002                     {
15003                         tag: 'ul',
15004                         cls: 'roo-select2-choices',
15005                         cn:[
15006                             {
15007                                 tag: 'li',
15008                                 cls: 'roo-select2-search-field',
15009                                 cn: [
15010
15011                                     inputblock
15012                                 ]
15013                             }
15014                         ]
15015                     }
15016                 ]
15017             }
15018         };
15019         
15020         var combobox = {
15021             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15022             cn: [
15023                 box
15024             ]
15025         };
15026         
15027         if(!this.multiple && this.showToggleBtn){
15028             
15029             var caret = {
15030                         tag: 'span',
15031                         cls: 'caret'
15032             };
15033             
15034             if (this.caret != false) {
15035                 caret = {
15036                      tag: 'i',
15037                      cls: 'fa fa-' + this.caret
15038                 };
15039                 
15040             }
15041             
15042             combobox.cn.push({
15043                 tag :'span',
15044                 cls : 'input-group-addon btn dropdown-toggle',
15045                 cn : [
15046                     caret,
15047                     {
15048                         tag: 'span',
15049                         cls: 'combobox-clear',
15050                         cn  : [
15051                             {
15052                                 tag : 'i',
15053                                 cls: 'icon-remove'
15054                             }
15055                         ]
15056                     }
15057                 ]
15058
15059             })
15060         }
15061         
15062         if(this.multiple){
15063             combobox.cls += ' roo-select2-container-multi';
15064         }
15065         
15066         var align = this.labelAlign || this.parentLabelAlign();
15067         
15068         if (align ==='left' && this.fieldLabel.length) {
15069
15070             cfg.cn = [
15071                 {
15072                    tag : 'i',
15073                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15074                    tooltip : 'This field is required'
15075                 },
15076                 {
15077                     tag: 'label',
15078                     cls : 'control-label',
15079                     html : this.fieldLabel
15080
15081                 },
15082                 {
15083                     cls : '', 
15084                     cn: [
15085                         combobox
15086                     ]
15087                 }
15088             ];
15089             
15090             var labelCfg = cfg.cn[1];
15091             var contentCfg = cfg.cn[2];
15092             
15093
15094             if(this.indicatorpos == 'right'){
15095                 cfg.cn = [
15096                     {
15097                         tag: 'label',
15098                         'for' :  id,
15099                         cls : 'control-label',
15100                         cn : [
15101                             {
15102                                 tag : 'span',
15103                                 html : this.fieldLabel
15104                             },
15105                             {
15106                                 tag : 'i',
15107                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15108                                 tooltip : 'This field is required'
15109                             }
15110                         ]
15111                     },
15112                     {
15113                         cls : "",
15114                         cn: [
15115                             combobox
15116                         ]
15117                     }
15118
15119                 ];
15120                 
15121                 labelCfg = cfg.cn[0];
15122                 contentCfg = cfg.cn[1];
15123             }
15124             
15125            
15126             
15127             if(this.labelWidth > 12){
15128                 labelCfg.style = "width: " + this.labelWidth + 'px';
15129             }
15130             
15131             if(this.labelWidth < 13 && this.labelmd == 0){
15132                 this.labelmd = this.labelWidth;
15133             }
15134             
15135             if(this.labellg > 0){
15136                 labelCfg.cls += ' col-lg-' + this.labellg;
15137                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15138             }
15139             
15140             if(this.labelmd > 0){
15141                 labelCfg.cls += ' col-md-' + this.labelmd;
15142                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15143             }
15144             
15145             if(this.labelsm > 0){
15146                 labelCfg.cls += ' col-sm-' + this.labelsm;
15147                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15148             }
15149             
15150             if(this.labelxs > 0){
15151                 labelCfg.cls += ' col-xs-' + this.labelxs;
15152                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15153             }
15154                 
15155                 
15156         } else if ( this.fieldLabel.length) {
15157             cfg.cn = [
15158                 {
15159                    tag : 'i',
15160                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15161                    tooltip : 'This field is required'
15162                 },
15163                 {
15164                     tag: 'label',
15165                     cls : 'control-label',
15166                     html : this.fieldLabel
15167
15168                 },
15169                 {
15170                     cls : '', 
15171                     cn: [
15172                         combobox
15173                     ]
15174                 }
15175             ];
15176             
15177             if(this.indicatorpos == 'right'){
15178                 cfg.cn = [
15179                     {
15180                         tag: 'label',
15181                         cls : 'control-label',
15182                         html : this.fieldLabel,
15183                         cn : [
15184                             {
15185                                tag : 'i',
15186                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15187                                tooltip : 'This field is required'
15188                             }
15189                         ]
15190                     },
15191                     {
15192                         cls : '', 
15193                         cn: [
15194                             combobox
15195                         ]
15196                     }
15197                 ];
15198             }
15199         } else {
15200             cfg.cn = combobox;    
15201         }
15202         
15203         
15204         var settings = this;
15205         
15206         ['xs','sm','md','lg'].map(function(size){
15207             if (settings[size]) {
15208                 cfg.cls += ' col-' + size + '-' + settings[size];
15209             }
15210         });
15211         
15212         return cfg;
15213     },
15214     
15215     initTouchView : function()
15216     {
15217         this.renderTouchView();
15218         
15219         this.touchViewEl.on('scroll', function(){
15220             this.el.dom.scrollTop = 0;
15221         }, this);
15222         
15223         this.originalValue = this.getValue();
15224         
15225         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15226         
15227         this.inputEl().on("click", this.showTouchView, this);
15228         if (this.triggerEl) {
15229             this.triggerEl.on("click", this.showTouchView, this);
15230         }
15231         
15232         
15233         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15234         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15235         
15236         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15237         
15238         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15239         this.store.on('load', this.onTouchViewLoad, this);
15240         this.store.on('loadexception', this.onTouchViewLoadException, this);
15241         
15242         if(this.hiddenName){
15243             
15244             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15245             
15246             this.hiddenField.dom.value =
15247                 this.hiddenValue !== undefined ? this.hiddenValue :
15248                 this.value !== undefined ? this.value : '';
15249         
15250             this.el.dom.removeAttribute('name');
15251             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15252         }
15253         
15254         if(this.multiple){
15255             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15256             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15257         }
15258         
15259         if(this.removable && !this.multiple){
15260             var close = this.closeTriggerEl();
15261             if(close){
15262                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15263                 close.on('click', this.removeBtnClick, this, close);
15264             }
15265         }
15266         /*
15267          * fix the bug in Safari iOS8
15268          */
15269         this.inputEl().on("focus", function(e){
15270             document.activeElement.blur();
15271         }, this);
15272         
15273         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15274         
15275         return;
15276         
15277         
15278     },
15279     
15280     renderTouchView : function()
15281     {
15282         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15283         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15284         
15285         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15286         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15287         
15288         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15289         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15290         this.touchViewBodyEl.setStyle('overflow', 'auto');
15291         
15292         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15293         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15294         
15295         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15296         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15297         
15298     },
15299     
15300     showTouchView : function()
15301     {
15302         if(this.disabled){
15303             return;
15304         }
15305         
15306         this.touchViewHeaderEl.hide();
15307
15308         if(this.modalTitle.length){
15309             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15310             this.touchViewHeaderEl.show();
15311         }
15312
15313         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15314         this.touchViewEl.show();
15315
15316         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15317         
15318         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15319         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15320
15321         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15322
15323         if(this.modalTitle.length){
15324             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15325         }
15326         
15327         this.touchViewBodyEl.setHeight(bodyHeight);
15328
15329         if(this.animate){
15330             var _this = this;
15331             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15332         }else{
15333             this.touchViewEl.addClass('in');
15334         }
15335         
15336         if(this._touchViewMask){
15337             Roo.get(document.body).addClass("x-body-masked");
15338             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15339             this._touchViewMask.setStyle('z-index', 10000);
15340             this._touchViewMask.addClass('show');
15341         }
15342         
15343         this.doTouchViewQuery();
15344         
15345     },
15346     
15347     hideTouchView : function()
15348     {
15349         this.touchViewEl.removeClass('in');
15350
15351         if(this.animate){
15352             var _this = this;
15353             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15354         }else{
15355             this.touchViewEl.setStyle('display', 'none');
15356         }
15357         
15358         if(this._touchViewMask){
15359             this._touchViewMask.removeClass('show');
15360             Roo.get(document.body).removeClass("x-body-masked");
15361         }
15362     },
15363     
15364     setTouchViewValue : function()
15365     {
15366         if(this.multiple){
15367             this.clearItem();
15368         
15369             var _this = this;
15370
15371             Roo.each(this.tickItems, function(o){
15372                 this.addItem(o);
15373             }, this);
15374         }
15375         
15376         this.hideTouchView();
15377     },
15378     
15379     doTouchViewQuery : function()
15380     {
15381         var qe = {
15382             query: '',
15383             forceAll: true,
15384             combo: this,
15385             cancel:false
15386         };
15387         
15388         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15389             return false;
15390         }
15391         
15392         if(!this.alwaysQuery || this.mode == 'local'){
15393             this.onTouchViewLoad();
15394             return;
15395         }
15396         
15397         this.store.load();
15398     },
15399     
15400     onTouchViewBeforeLoad : function(combo,opts)
15401     {
15402         return;
15403     },
15404
15405     // private
15406     onTouchViewLoad : function()
15407     {
15408         if(this.store.getCount() < 1){
15409             this.onTouchViewEmptyResults();
15410             return;
15411         }
15412         
15413         this.clearTouchView();
15414         
15415         var rawValue = this.getRawValue();
15416         
15417         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15418         
15419         this.tickItems = [];
15420         
15421         this.store.data.each(function(d, rowIndex){
15422             var row = this.touchViewListGroup.createChild(template);
15423             
15424             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15425                 row.addClass(d.data.cls);
15426             }
15427             
15428             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15429                 var cfg = {
15430                     data : d.data,
15431                     html : d.data[this.displayField]
15432                 };
15433                 
15434                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15435                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15436                 }
15437             }
15438             row.removeClass('selected');
15439             if(!this.multiple && this.valueField &&
15440                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15441             {
15442                 // radio buttons..
15443                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15444                 row.addClass('selected');
15445             }
15446             
15447             if(this.multiple && this.valueField &&
15448                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15449             {
15450                 
15451                 // checkboxes...
15452                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15453                 this.tickItems.push(d.data);
15454             }
15455             
15456             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15457             
15458         }, this);
15459         
15460         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15461         
15462         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15463
15464         if(this.modalTitle.length){
15465             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15466         }
15467
15468         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15469         
15470         if(this.mobile_restrict_height && listHeight < bodyHeight){
15471             this.touchViewBodyEl.setHeight(listHeight);
15472         }
15473         
15474         var _this = this;
15475         
15476         if(firstChecked && listHeight > bodyHeight){
15477             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15478         }
15479         
15480     },
15481     
15482     onTouchViewLoadException : function()
15483     {
15484         this.hideTouchView();
15485     },
15486     
15487     onTouchViewEmptyResults : function()
15488     {
15489         this.clearTouchView();
15490         
15491         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15492         
15493         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15494         
15495     },
15496     
15497     clearTouchView : function()
15498     {
15499         this.touchViewListGroup.dom.innerHTML = '';
15500     },
15501     
15502     onTouchViewClick : function(e, el, o)
15503     {
15504         e.preventDefault();
15505         
15506         var row = o.row;
15507         var rowIndex = o.rowIndex;
15508         
15509         var r = this.store.getAt(rowIndex);
15510         
15511         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15512             
15513             if(!this.multiple){
15514                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15515                     c.dom.removeAttribute('checked');
15516                 }, this);
15517
15518                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15519
15520                 this.setFromData(r.data);
15521
15522                 var close = this.closeTriggerEl();
15523
15524                 if(close){
15525                     close.show();
15526                 }
15527
15528                 this.hideTouchView();
15529
15530                 this.fireEvent('select', this, r, rowIndex);
15531
15532                 return;
15533             }
15534
15535             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15536                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15537                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15538                 return;
15539             }
15540
15541             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15542             this.addItem(r.data);
15543             this.tickItems.push(r.data);
15544         }
15545     },
15546     
15547     getAutoCreateNativeIOS : function()
15548     {
15549         var cfg = {
15550             cls: 'form-group' //input-group,
15551         };
15552         
15553         var combobox =  {
15554             tag: 'select',
15555             cls : 'roo-ios-select'
15556         };
15557         
15558         if (this.name) {
15559             combobox.name = this.name;
15560         }
15561         
15562         if (this.disabled) {
15563             combobox.disabled = true;
15564         }
15565         
15566         var settings = this;
15567         
15568         ['xs','sm','md','lg'].map(function(size){
15569             if (settings[size]) {
15570                 cfg.cls += ' col-' + size + '-' + settings[size];
15571             }
15572         });
15573         
15574         cfg.cn = combobox;
15575         
15576         return cfg;
15577         
15578     },
15579     
15580     initIOSView : function()
15581     {
15582         this.store.on('load', this.onIOSViewLoad, this);
15583         
15584         return;
15585     },
15586     
15587     onIOSViewLoad : function()
15588     {
15589         if(this.store.getCount() < 1){
15590             return;
15591         }
15592         
15593         this.clearIOSView();
15594         
15595         if(this.allowBlank) {
15596             
15597             var default_text = '-- SELECT --';
15598             
15599             if(this.placeholder.length){
15600                 default_text = this.placeholder;
15601             }
15602             
15603             if(this.emptyTitle.length){
15604                 default_text += ' - ' + this.emptyTitle + ' -';
15605             }
15606             
15607             var opt = this.inputEl().createChild({
15608                 tag: 'option',
15609                 value : 0,
15610                 html : default_text
15611             });
15612             
15613             var o = {};
15614             o[this.valueField] = 0;
15615             o[this.displayField] = default_text;
15616             
15617             this.ios_options.push({
15618                 data : o,
15619                 el : opt
15620             });
15621             
15622         }
15623         
15624         this.store.data.each(function(d, rowIndex){
15625             
15626             var html = '';
15627             
15628             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15629                 html = d.data[this.displayField];
15630             }
15631             
15632             var value = '';
15633             
15634             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15635                 value = d.data[this.valueField];
15636             }
15637             
15638             var option = {
15639                 tag: 'option',
15640                 value : value,
15641                 html : html
15642             };
15643             
15644             if(this.value == d.data[this.valueField]){
15645                 option['selected'] = true;
15646             }
15647             
15648             var opt = this.inputEl().createChild(option);
15649             
15650             this.ios_options.push({
15651                 data : d.data,
15652                 el : opt
15653             });
15654             
15655         }, this);
15656         
15657         this.inputEl().on('change', function(){
15658            this.fireEvent('select', this);
15659         }, this);
15660         
15661     },
15662     
15663     clearIOSView: function()
15664     {
15665         this.inputEl().dom.innerHTML = '';
15666         
15667         this.ios_options = [];
15668     },
15669     
15670     setIOSValue: function(v)
15671     {
15672         this.value = v;
15673         
15674         if(!this.ios_options){
15675             return;
15676         }
15677         
15678         Roo.each(this.ios_options, function(opts){
15679            
15680            opts.el.dom.removeAttribute('selected');
15681            
15682            if(opts.data[this.valueField] != v){
15683                return;
15684            }
15685            
15686            opts.el.dom.setAttribute('selected', true);
15687            
15688         }, this);
15689     }
15690
15691     /** 
15692     * @cfg {Boolean} grow 
15693     * @hide 
15694     */
15695     /** 
15696     * @cfg {Number} growMin 
15697     * @hide 
15698     */
15699     /** 
15700     * @cfg {Number} growMax 
15701     * @hide 
15702     */
15703     /**
15704      * @hide
15705      * @method autoSize
15706      */
15707 });
15708
15709 Roo.apply(Roo.bootstrap.ComboBox,  {
15710     
15711     header : {
15712         tag: 'div',
15713         cls: 'modal-header',
15714         cn: [
15715             {
15716                 tag: 'h4',
15717                 cls: 'modal-title'
15718             }
15719         ]
15720     },
15721     
15722     body : {
15723         tag: 'div',
15724         cls: 'modal-body',
15725         cn: [
15726             {
15727                 tag: 'ul',
15728                 cls: 'list-group'
15729             }
15730         ]
15731     },
15732     
15733     listItemRadio : {
15734         tag: 'li',
15735         cls: 'list-group-item',
15736         cn: [
15737             {
15738                 tag: 'span',
15739                 cls: 'roo-combobox-list-group-item-value'
15740             },
15741             {
15742                 tag: 'div',
15743                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15744                 cn: [
15745                     {
15746                         tag: 'input',
15747                         type: 'radio'
15748                     },
15749                     {
15750                         tag: 'label'
15751                     }
15752                 ]
15753             }
15754         ]
15755     },
15756     
15757     listItemCheckbox : {
15758         tag: 'li',
15759         cls: 'list-group-item',
15760         cn: [
15761             {
15762                 tag: 'span',
15763                 cls: 'roo-combobox-list-group-item-value'
15764             },
15765             {
15766                 tag: 'div',
15767                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15768                 cn: [
15769                     {
15770                         tag: 'input',
15771                         type: 'checkbox'
15772                     },
15773                     {
15774                         tag: 'label'
15775                     }
15776                 ]
15777             }
15778         ]
15779     },
15780     
15781     emptyResult : {
15782         tag: 'div',
15783         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15784     },
15785     
15786     footer : {
15787         tag: 'div',
15788         cls: 'modal-footer',
15789         cn: [
15790             {
15791                 tag: 'div',
15792                 cls: 'row',
15793                 cn: [
15794                     {
15795                         tag: 'div',
15796                         cls: 'col-xs-6 text-left',
15797                         cn: {
15798                             tag: 'button',
15799                             cls: 'btn btn-danger roo-touch-view-cancel',
15800                             html: 'Cancel'
15801                         }
15802                     },
15803                     {
15804                         tag: 'div',
15805                         cls: 'col-xs-6 text-right',
15806                         cn: {
15807                             tag: 'button',
15808                             cls: 'btn btn-success roo-touch-view-ok',
15809                             html: 'OK'
15810                         }
15811                     }
15812                 ]
15813             }
15814         ]
15815         
15816     }
15817 });
15818
15819 Roo.apply(Roo.bootstrap.ComboBox,  {
15820     
15821     touchViewTemplate : {
15822         tag: 'div',
15823         cls: 'modal fade roo-combobox-touch-view',
15824         cn: [
15825             {
15826                 tag: 'div',
15827                 cls: 'modal-dialog',
15828                 style : 'position:fixed', // we have to fix position....
15829                 cn: [
15830                     {
15831                         tag: 'div',
15832                         cls: 'modal-content',
15833                         cn: [
15834                             Roo.bootstrap.ComboBox.header,
15835                             Roo.bootstrap.ComboBox.body,
15836                             Roo.bootstrap.ComboBox.footer
15837                         ]
15838                     }
15839                 ]
15840             }
15841         ]
15842     }
15843 });/*
15844  * Based on:
15845  * Ext JS Library 1.1.1
15846  * Copyright(c) 2006-2007, Ext JS, LLC.
15847  *
15848  * Originally Released Under LGPL - original licence link has changed is not relivant.
15849  *
15850  * Fork - LGPL
15851  * <script type="text/javascript">
15852  */
15853
15854 /**
15855  * @class Roo.View
15856  * @extends Roo.util.Observable
15857  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15858  * This class also supports single and multi selection modes. <br>
15859  * Create a data model bound view:
15860  <pre><code>
15861  var store = new Roo.data.Store(...);
15862
15863  var view = new Roo.View({
15864     el : "my-element",
15865     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15866  
15867     singleSelect: true,
15868     selectedClass: "ydataview-selected",
15869     store: store
15870  });
15871
15872  // listen for node click?
15873  view.on("click", function(vw, index, node, e){
15874  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15875  });
15876
15877  // load XML data
15878  dataModel.load("foobar.xml");
15879  </code></pre>
15880  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15881  * <br><br>
15882  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15883  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15884  * 
15885  * Note: old style constructor is still suported (container, template, config)
15886  * 
15887  * @constructor
15888  * Create a new View
15889  * @param {Object} config The config object
15890  * 
15891  */
15892 Roo.View = function(config, depreciated_tpl, depreciated_config){
15893     
15894     this.parent = false;
15895     
15896     if (typeof(depreciated_tpl) == 'undefined') {
15897         // new way.. - universal constructor.
15898         Roo.apply(this, config);
15899         this.el  = Roo.get(this.el);
15900     } else {
15901         // old format..
15902         this.el  = Roo.get(config);
15903         this.tpl = depreciated_tpl;
15904         Roo.apply(this, depreciated_config);
15905     }
15906     this.wrapEl  = this.el.wrap().wrap();
15907     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15908     
15909     
15910     if(typeof(this.tpl) == "string"){
15911         this.tpl = new Roo.Template(this.tpl);
15912     } else {
15913         // support xtype ctors..
15914         this.tpl = new Roo.factory(this.tpl, Roo);
15915     }
15916     
15917     
15918     this.tpl.compile();
15919     
15920     /** @private */
15921     this.addEvents({
15922         /**
15923          * @event beforeclick
15924          * Fires before a click is processed. Returns false to cancel the default action.
15925          * @param {Roo.View} this
15926          * @param {Number} index The index of the target node
15927          * @param {HTMLElement} node The target node
15928          * @param {Roo.EventObject} e The raw event object
15929          */
15930             "beforeclick" : true,
15931         /**
15932          * @event click
15933          * Fires when a template node is clicked.
15934          * @param {Roo.View} this
15935          * @param {Number} index The index of the target node
15936          * @param {HTMLElement} node The target node
15937          * @param {Roo.EventObject} e The raw event object
15938          */
15939             "click" : true,
15940         /**
15941          * @event dblclick
15942          * Fires when a template node is double clicked.
15943          * @param {Roo.View} this
15944          * @param {Number} index The index of the target node
15945          * @param {HTMLElement} node The target node
15946          * @param {Roo.EventObject} e The raw event object
15947          */
15948             "dblclick" : true,
15949         /**
15950          * @event contextmenu
15951          * Fires when a template node is right clicked.
15952          * @param {Roo.View} this
15953          * @param {Number} index The index of the target node
15954          * @param {HTMLElement} node The target node
15955          * @param {Roo.EventObject} e The raw event object
15956          */
15957             "contextmenu" : true,
15958         /**
15959          * @event selectionchange
15960          * Fires when the selected nodes change.
15961          * @param {Roo.View} this
15962          * @param {Array} selections Array of the selected nodes
15963          */
15964             "selectionchange" : true,
15965     
15966         /**
15967          * @event beforeselect
15968          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15969          * @param {Roo.View} this
15970          * @param {HTMLElement} node The node to be selected
15971          * @param {Array} selections Array of currently selected nodes
15972          */
15973             "beforeselect" : true,
15974         /**
15975          * @event preparedata
15976          * Fires on every row to render, to allow you to change the data.
15977          * @param {Roo.View} this
15978          * @param {Object} data to be rendered (change this)
15979          */
15980           "preparedata" : true
15981           
15982           
15983         });
15984
15985
15986
15987     this.el.on({
15988         "click": this.onClick,
15989         "dblclick": this.onDblClick,
15990         "contextmenu": this.onContextMenu,
15991         scope:this
15992     });
15993
15994     this.selections = [];
15995     this.nodes = [];
15996     this.cmp = new Roo.CompositeElementLite([]);
15997     if(this.store){
15998         this.store = Roo.factory(this.store, Roo.data);
15999         this.setStore(this.store, true);
16000     }
16001     
16002     if ( this.footer && this.footer.xtype) {
16003            
16004          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16005         
16006         this.footer.dataSource = this.store;
16007         this.footer.container = fctr;
16008         this.footer = Roo.factory(this.footer, Roo);
16009         fctr.insertFirst(this.el);
16010         
16011         // this is a bit insane - as the paging toolbar seems to detach the el..
16012 //        dom.parentNode.parentNode.parentNode
16013          // they get detached?
16014     }
16015     
16016     
16017     Roo.View.superclass.constructor.call(this);
16018     
16019     
16020 };
16021
16022 Roo.extend(Roo.View, Roo.util.Observable, {
16023     
16024      /**
16025      * @cfg {Roo.data.Store} store Data store to load data from.
16026      */
16027     store : false,
16028     
16029     /**
16030      * @cfg {String|Roo.Element} el The container element.
16031      */
16032     el : '',
16033     
16034     /**
16035      * @cfg {String|Roo.Template} tpl The template used by this View 
16036      */
16037     tpl : false,
16038     /**
16039      * @cfg {String} dataName the named area of the template to use as the data area
16040      *                          Works with domtemplates roo-name="name"
16041      */
16042     dataName: false,
16043     /**
16044      * @cfg {String} selectedClass The css class to add to selected nodes
16045      */
16046     selectedClass : "x-view-selected",
16047      /**
16048      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16049      */
16050     emptyText : "",
16051     
16052     /**
16053      * @cfg {String} text to display on mask (default Loading)
16054      */
16055     mask : false,
16056     /**
16057      * @cfg {Boolean} multiSelect Allow multiple selection
16058      */
16059     multiSelect : false,
16060     /**
16061      * @cfg {Boolean} singleSelect Allow single selection
16062      */
16063     singleSelect:  false,
16064     
16065     /**
16066      * @cfg {Boolean} toggleSelect - selecting 
16067      */
16068     toggleSelect : false,
16069     
16070     /**
16071      * @cfg {Boolean} tickable - selecting 
16072      */
16073     tickable : false,
16074     
16075     /**
16076      * Returns the element this view is bound to.
16077      * @return {Roo.Element}
16078      */
16079     getEl : function(){
16080         return this.wrapEl;
16081     },
16082     
16083     
16084
16085     /**
16086      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16087      */
16088     refresh : function(){
16089         //Roo.log('refresh');
16090         var t = this.tpl;
16091         
16092         // if we are using something like 'domtemplate', then
16093         // the what gets used is:
16094         // t.applySubtemplate(NAME, data, wrapping data..)
16095         // the outer template then get' applied with
16096         //     the store 'extra data'
16097         // and the body get's added to the
16098         //      roo-name="data" node?
16099         //      <span class='roo-tpl-{name}'></span> ?????
16100         
16101         
16102         
16103         this.clearSelections();
16104         this.el.update("");
16105         var html = [];
16106         var records = this.store.getRange();
16107         if(records.length < 1) {
16108             
16109             // is this valid??  = should it render a template??
16110             
16111             this.el.update(this.emptyText);
16112             return;
16113         }
16114         var el = this.el;
16115         if (this.dataName) {
16116             this.el.update(t.apply(this.store.meta)); //????
16117             el = this.el.child('.roo-tpl-' + this.dataName);
16118         }
16119         
16120         for(var i = 0, len = records.length; i < len; i++){
16121             var data = this.prepareData(records[i].data, i, records[i]);
16122             this.fireEvent("preparedata", this, data, i, records[i]);
16123             
16124             var d = Roo.apply({}, data);
16125             
16126             if(this.tickable){
16127                 Roo.apply(d, {'roo-id' : Roo.id()});
16128                 
16129                 var _this = this;
16130             
16131                 Roo.each(this.parent.item, function(item){
16132                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16133                         return;
16134                     }
16135                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16136                 });
16137             }
16138             
16139             html[html.length] = Roo.util.Format.trim(
16140                 this.dataName ?
16141                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16142                     t.apply(d)
16143             );
16144         }
16145         
16146         
16147         
16148         el.update(html.join(""));
16149         this.nodes = el.dom.childNodes;
16150         this.updateIndexes(0);
16151     },
16152     
16153
16154     /**
16155      * Function to override to reformat the data that is sent to
16156      * the template for each node.
16157      * DEPRICATED - use the preparedata event handler.
16158      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16159      * a JSON object for an UpdateManager bound view).
16160      */
16161     prepareData : function(data, index, record)
16162     {
16163         this.fireEvent("preparedata", this, data, index, record);
16164         return data;
16165     },
16166
16167     onUpdate : function(ds, record){
16168         // Roo.log('on update');   
16169         this.clearSelections();
16170         var index = this.store.indexOf(record);
16171         var n = this.nodes[index];
16172         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16173         n.parentNode.removeChild(n);
16174         this.updateIndexes(index, index);
16175     },
16176
16177     
16178     
16179 // --------- FIXME     
16180     onAdd : function(ds, records, index)
16181     {
16182         //Roo.log(['on Add', ds, records, index] );        
16183         this.clearSelections();
16184         if(this.nodes.length == 0){
16185             this.refresh();
16186             return;
16187         }
16188         var n = this.nodes[index];
16189         for(var i = 0, len = records.length; i < len; i++){
16190             var d = this.prepareData(records[i].data, i, records[i]);
16191             if(n){
16192                 this.tpl.insertBefore(n, d);
16193             }else{
16194                 
16195                 this.tpl.append(this.el, d);
16196             }
16197         }
16198         this.updateIndexes(index);
16199     },
16200
16201     onRemove : function(ds, record, index){
16202        // Roo.log('onRemove');
16203         this.clearSelections();
16204         var el = this.dataName  ?
16205             this.el.child('.roo-tpl-' + this.dataName) :
16206             this.el; 
16207         
16208         el.dom.removeChild(this.nodes[index]);
16209         this.updateIndexes(index);
16210     },
16211
16212     /**
16213      * Refresh an individual node.
16214      * @param {Number} index
16215      */
16216     refreshNode : function(index){
16217         this.onUpdate(this.store, this.store.getAt(index));
16218     },
16219
16220     updateIndexes : function(startIndex, endIndex){
16221         var ns = this.nodes;
16222         startIndex = startIndex || 0;
16223         endIndex = endIndex || ns.length - 1;
16224         for(var i = startIndex; i <= endIndex; i++){
16225             ns[i].nodeIndex = i;
16226         }
16227     },
16228
16229     /**
16230      * Changes the data store this view uses and refresh the view.
16231      * @param {Store} store
16232      */
16233     setStore : function(store, initial){
16234         if(!initial && this.store){
16235             this.store.un("datachanged", this.refresh);
16236             this.store.un("add", this.onAdd);
16237             this.store.un("remove", this.onRemove);
16238             this.store.un("update", this.onUpdate);
16239             this.store.un("clear", this.refresh);
16240             this.store.un("beforeload", this.onBeforeLoad);
16241             this.store.un("load", this.onLoad);
16242             this.store.un("loadexception", this.onLoad);
16243         }
16244         if(store){
16245           
16246             store.on("datachanged", this.refresh, this);
16247             store.on("add", this.onAdd, this);
16248             store.on("remove", this.onRemove, this);
16249             store.on("update", this.onUpdate, this);
16250             store.on("clear", this.refresh, this);
16251             store.on("beforeload", this.onBeforeLoad, this);
16252             store.on("load", this.onLoad, this);
16253             store.on("loadexception", this.onLoad, this);
16254         }
16255         
16256         if(store){
16257             this.refresh();
16258         }
16259     },
16260     /**
16261      * onbeforeLoad - masks the loading area.
16262      *
16263      */
16264     onBeforeLoad : function(store,opts)
16265     {
16266          //Roo.log('onBeforeLoad');   
16267         if (!opts.add) {
16268             this.el.update("");
16269         }
16270         this.el.mask(this.mask ? this.mask : "Loading" ); 
16271     },
16272     onLoad : function ()
16273     {
16274         this.el.unmask();
16275     },
16276     
16277
16278     /**
16279      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16280      * @param {HTMLElement} node
16281      * @return {HTMLElement} The template node
16282      */
16283     findItemFromChild : function(node){
16284         var el = this.dataName  ?
16285             this.el.child('.roo-tpl-' + this.dataName,true) :
16286             this.el.dom; 
16287         
16288         if(!node || node.parentNode == el){
16289                     return node;
16290             }
16291             var p = node.parentNode;
16292             while(p && p != el){
16293             if(p.parentNode == el){
16294                 return p;
16295             }
16296             p = p.parentNode;
16297         }
16298             return null;
16299     },
16300
16301     /** @ignore */
16302     onClick : function(e){
16303         var item = this.findItemFromChild(e.getTarget());
16304         if(item){
16305             var index = this.indexOf(item);
16306             if(this.onItemClick(item, index, e) !== false){
16307                 this.fireEvent("click", this, index, item, e);
16308             }
16309         }else{
16310             this.clearSelections();
16311         }
16312     },
16313
16314     /** @ignore */
16315     onContextMenu : function(e){
16316         var item = this.findItemFromChild(e.getTarget());
16317         if(item){
16318             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16319         }
16320     },
16321
16322     /** @ignore */
16323     onDblClick : function(e){
16324         var item = this.findItemFromChild(e.getTarget());
16325         if(item){
16326             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16327         }
16328     },
16329
16330     onItemClick : function(item, index, e)
16331     {
16332         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16333             return false;
16334         }
16335         if (this.toggleSelect) {
16336             var m = this.isSelected(item) ? 'unselect' : 'select';
16337             //Roo.log(m);
16338             var _t = this;
16339             _t[m](item, true, false);
16340             return true;
16341         }
16342         if(this.multiSelect || this.singleSelect){
16343             if(this.multiSelect && e.shiftKey && this.lastSelection){
16344                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16345             }else{
16346                 this.select(item, this.multiSelect && e.ctrlKey);
16347                 this.lastSelection = item;
16348             }
16349             
16350             if(!this.tickable){
16351                 e.preventDefault();
16352             }
16353             
16354         }
16355         return true;
16356     },
16357
16358     /**
16359      * Get the number of selected nodes.
16360      * @return {Number}
16361      */
16362     getSelectionCount : function(){
16363         return this.selections.length;
16364     },
16365
16366     /**
16367      * Get the currently selected nodes.
16368      * @return {Array} An array of HTMLElements
16369      */
16370     getSelectedNodes : function(){
16371         return this.selections;
16372     },
16373
16374     /**
16375      * Get the indexes of the selected nodes.
16376      * @return {Array}
16377      */
16378     getSelectedIndexes : function(){
16379         var indexes = [], s = this.selections;
16380         for(var i = 0, len = s.length; i < len; i++){
16381             indexes.push(s[i].nodeIndex);
16382         }
16383         return indexes;
16384     },
16385
16386     /**
16387      * Clear all selections
16388      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16389      */
16390     clearSelections : function(suppressEvent){
16391         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16392             this.cmp.elements = this.selections;
16393             this.cmp.removeClass(this.selectedClass);
16394             this.selections = [];
16395             if(!suppressEvent){
16396                 this.fireEvent("selectionchange", this, this.selections);
16397             }
16398         }
16399     },
16400
16401     /**
16402      * Returns true if the passed node is selected
16403      * @param {HTMLElement/Number} node The node or node index
16404      * @return {Boolean}
16405      */
16406     isSelected : function(node){
16407         var s = this.selections;
16408         if(s.length < 1){
16409             return false;
16410         }
16411         node = this.getNode(node);
16412         return s.indexOf(node) !== -1;
16413     },
16414
16415     /**
16416      * Selects nodes.
16417      * @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
16418      * @param {Boolean} keepExisting (optional) true to keep existing selections
16419      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16420      */
16421     select : function(nodeInfo, keepExisting, suppressEvent){
16422         if(nodeInfo instanceof Array){
16423             if(!keepExisting){
16424                 this.clearSelections(true);
16425             }
16426             for(var i = 0, len = nodeInfo.length; i < len; i++){
16427                 this.select(nodeInfo[i], true, true);
16428             }
16429             return;
16430         } 
16431         var node = this.getNode(nodeInfo);
16432         if(!node || this.isSelected(node)){
16433             return; // already selected.
16434         }
16435         if(!keepExisting){
16436             this.clearSelections(true);
16437         }
16438         
16439         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16440             Roo.fly(node).addClass(this.selectedClass);
16441             this.selections.push(node);
16442             if(!suppressEvent){
16443                 this.fireEvent("selectionchange", this, this.selections);
16444             }
16445         }
16446         
16447         
16448     },
16449       /**
16450      * Unselects nodes.
16451      * @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
16452      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16453      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16454      */
16455     unselect : function(nodeInfo, keepExisting, suppressEvent)
16456     {
16457         if(nodeInfo instanceof Array){
16458             Roo.each(this.selections, function(s) {
16459                 this.unselect(s, nodeInfo);
16460             }, this);
16461             return;
16462         }
16463         var node = this.getNode(nodeInfo);
16464         if(!node || !this.isSelected(node)){
16465             //Roo.log("not selected");
16466             return; // not selected.
16467         }
16468         // fireevent???
16469         var ns = [];
16470         Roo.each(this.selections, function(s) {
16471             if (s == node ) {
16472                 Roo.fly(node).removeClass(this.selectedClass);
16473
16474                 return;
16475             }
16476             ns.push(s);
16477         },this);
16478         
16479         this.selections= ns;
16480         this.fireEvent("selectionchange", this, this.selections);
16481     },
16482
16483     /**
16484      * Gets a template node.
16485      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16486      * @return {HTMLElement} The node or null if it wasn't found
16487      */
16488     getNode : function(nodeInfo){
16489         if(typeof nodeInfo == "string"){
16490             return document.getElementById(nodeInfo);
16491         }else if(typeof nodeInfo == "number"){
16492             return this.nodes[nodeInfo];
16493         }
16494         return nodeInfo;
16495     },
16496
16497     /**
16498      * Gets a range template nodes.
16499      * @param {Number} startIndex
16500      * @param {Number} endIndex
16501      * @return {Array} An array of nodes
16502      */
16503     getNodes : function(start, end){
16504         var ns = this.nodes;
16505         start = start || 0;
16506         end = typeof end == "undefined" ? ns.length - 1 : end;
16507         var nodes = [];
16508         if(start <= end){
16509             for(var i = start; i <= end; i++){
16510                 nodes.push(ns[i]);
16511             }
16512         } else{
16513             for(var i = start; i >= end; i--){
16514                 nodes.push(ns[i]);
16515             }
16516         }
16517         return nodes;
16518     },
16519
16520     /**
16521      * Finds the index of the passed node
16522      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16523      * @return {Number} The index of the node or -1
16524      */
16525     indexOf : function(node){
16526         node = this.getNode(node);
16527         if(typeof node.nodeIndex == "number"){
16528             return node.nodeIndex;
16529         }
16530         var ns = this.nodes;
16531         for(var i = 0, len = ns.length; i < len; i++){
16532             if(ns[i] == node){
16533                 return i;
16534             }
16535         }
16536         return -1;
16537     }
16538 });
16539 /*
16540  * - LGPL
16541  *
16542  * based on jquery fullcalendar
16543  * 
16544  */
16545
16546 Roo.bootstrap = Roo.bootstrap || {};
16547 /**
16548  * @class Roo.bootstrap.Calendar
16549  * @extends Roo.bootstrap.Component
16550  * Bootstrap Calendar class
16551  * @cfg {Boolean} loadMask (true|false) default false
16552  * @cfg {Object} header generate the user specific header of the calendar, default false
16553
16554  * @constructor
16555  * Create a new Container
16556  * @param {Object} config The config object
16557  */
16558
16559
16560
16561 Roo.bootstrap.Calendar = function(config){
16562     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16563      this.addEvents({
16564         /**
16565              * @event select
16566              * Fires when a date is selected
16567              * @param {DatePicker} this
16568              * @param {Date} date The selected date
16569              */
16570         'select': true,
16571         /**
16572              * @event monthchange
16573              * Fires when the displayed month changes 
16574              * @param {DatePicker} this
16575              * @param {Date} date The selected month
16576              */
16577         'monthchange': true,
16578         /**
16579              * @event evententer
16580              * Fires when mouse over an event
16581              * @param {Calendar} this
16582              * @param {event} Event
16583              */
16584         'evententer': true,
16585         /**
16586              * @event eventleave
16587              * Fires when the mouse leaves an
16588              * @param {Calendar} this
16589              * @param {event}
16590              */
16591         'eventleave': true,
16592         /**
16593              * @event eventclick
16594              * Fires when the mouse click an
16595              * @param {Calendar} this
16596              * @param {event}
16597              */
16598         'eventclick': true
16599         
16600     });
16601
16602 };
16603
16604 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16605     
16606      /**
16607      * @cfg {Number} startDay
16608      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16609      */
16610     startDay : 0,
16611     
16612     loadMask : false,
16613     
16614     header : false,
16615       
16616     getAutoCreate : function(){
16617         
16618         
16619         var fc_button = function(name, corner, style, content ) {
16620             return Roo.apply({},{
16621                 tag : 'span',
16622                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16623                          (corner.length ?
16624                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16625                             ''
16626                         ),
16627                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16628                 unselectable: 'on'
16629             });
16630         };
16631         
16632         var header = {};
16633         
16634         if(!this.header){
16635             header = {
16636                 tag : 'table',
16637                 cls : 'fc-header',
16638                 style : 'width:100%',
16639                 cn : [
16640                     {
16641                         tag: 'tr',
16642                         cn : [
16643                             {
16644                                 tag : 'td',
16645                                 cls : 'fc-header-left',
16646                                 cn : [
16647                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16648                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16649                                     { tag: 'span', cls: 'fc-header-space' },
16650                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16651
16652
16653                                 ]
16654                             },
16655
16656                             {
16657                                 tag : 'td',
16658                                 cls : 'fc-header-center',
16659                                 cn : [
16660                                     {
16661                                         tag: 'span',
16662                                         cls: 'fc-header-title',
16663                                         cn : {
16664                                             tag: 'H2',
16665                                             html : 'month / year'
16666                                         }
16667                                     }
16668
16669                                 ]
16670                             },
16671                             {
16672                                 tag : 'td',
16673                                 cls : 'fc-header-right',
16674                                 cn : [
16675                               /*      fc_button('month', 'left', '', 'month' ),
16676                                     fc_button('week', '', '', 'week' ),
16677                                     fc_button('day', 'right', '', 'day' )
16678                                 */    
16679
16680                                 ]
16681                             }
16682
16683                         ]
16684                     }
16685                 ]
16686             };
16687         }
16688         
16689         header = this.header;
16690         
16691        
16692         var cal_heads = function() {
16693             var ret = [];
16694             // fixme - handle this.
16695             
16696             for (var i =0; i < Date.dayNames.length; i++) {
16697                 var d = Date.dayNames[i];
16698                 ret.push({
16699                     tag: 'th',
16700                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16701                     html : d.substring(0,3)
16702                 });
16703                 
16704             }
16705             ret[0].cls += ' fc-first';
16706             ret[6].cls += ' fc-last';
16707             return ret;
16708         };
16709         var cal_cell = function(n) {
16710             return  {
16711                 tag: 'td',
16712                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16713                 cn : [
16714                     {
16715                         cn : [
16716                             {
16717                                 cls: 'fc-day-number',
16718                                 html: 'D'
16719                             },
16720                             {
16721                                 cls: 'fc-day-content',
16722                              
16723                                 cn : [
16724                                      {
16725                                         style: 'position: relative;' // height: 17px;
16726                                     }
16727                                 ]
16728                             }
16729                             
16730                             
16731                         ]
16732                     }
16733                 ]
16734                 
16735             }
16736         };
16737         var cal_rows = function() {
16738             
16739             var ret = [];
16740             for (var r = 0; r < 6; r++) {
16741                 var row= {
16742                     tag : 'tr',
16743                     cls : 'fc-week',
16744                     cn : []
16745                 };
16746                 
16747                 for (var i =0; i < Date.dayNames.length; i++) {
16748                     var d = Date.dayNames[i];
16749                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16750
16751                 }
16752                 row.cn[0].cls+=' fc-first';
16753                 row.cn[0].cn[0].style = 'min-height:90px';
16754                 row.cn[6].cls+=' fc-last';
16755                 ret.push(row);
16756                 
16757             }
16758             ret[0].cls += ' fc-first';
16759             ret[4].cls += ' fc-prev-last';
16760             ret[5].cls += ' fc-last';
16761             return ret;
16762             
16763         };
16764         
16765         var cal_table = {
16766             tag: 'table',
16767             cls: 'fc-border-separate',
16768             style : 'width:100%',
16769             cellspacing  : 0,
16770             cn : [
16771                 { 
16772                     tag: 'thead',
16773                     cn : [
16774                         { 
16775                             tag: 'tr',
16776                             cls : 'fc-first fc-last',
16777                             cn : cal_heads()
16778                         }
16779                     ]
16780                 },
16781                 { 
16782                     tag: 'tbody',
16783                     cn : cal_rows()
16784                 }
16785                   
16786             ]
16787         };
16788          
16789          var cfg = {
16790             cls : 'fc fc-ltr',
16791             cn : [
16792                 header,
16793                 {
16794                     cls : 'fc-content',
16795                     style : "position: relative;",
16796                     cn : [
16797                         {
16798                             cls : 'fc-view fc-view-month fc-grid',
16799                             style : 'position: relative',
16800                             unselectable : 'on',
16801                             cn : [
16802                                 {
16803                                     cls : 'fc-event-container',
16804                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16805                                 },
16806                                 cal_table
16807                             ]
16808                         }
16809                     ]
16810     
16811                 }
16812            ] 
16813             
16814         };
16815         
16816          
16817         
16818         return cfg;
16819     },
16820     
16821     
16822     initEvents : function()
16823     {
16824         if(!this.store){
16825             throw "can not find store for calendar";
16826         }
16827         
16828         var mark = {
16829             tag: "div",
16830             cls:"x-dlg-mask",
16831             style: "text-align:center",
16832             cn: [
16833                 {
16834                     tag: "div",
16835                     style: "background-color:white;width:50%;margin:250 auto",
16836                     cn: [
16837                         {
16838                             tag: "img",
16839                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16840                         },
16841                         {
16842                             tag: "span",
16843                             html: "Loading"
16844                         }
16845                         
16846                     ]
16847                 }
16848             ]
16849         };
16850         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16851         
16852         var size = this.el.select('.fc-content', true).first().getSize();
16853         this.maskEl.setSize(size.width, size.height);
16854         this.maskEl.enableDisplayMode("block");
16855         if(!this.loadMask){
16856             this.maskEl.hide();
16857         }
16858         
16859         this.store = Roo.factory(this.store, Roo.data);
16860         this.store.on('load', this.onLoad, this);
16861         this.store.on('beforeload', this.onBeforeLoad, this);
16862         
16863         this.resize();
16864         
16865         this.cells = this.el.select('.fc-day',true);
16866         //Roo.log(this.cells);
16867         this.textNodes = this.el.query('.fc-day-number');
16868         this.cells.addClassOnOver('fc-state-hover');
16869         
16870         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16871         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16872         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16873         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16874         
16875         this.on('monthchange', this.onMonthChange, this);
16876         
16877         this.update(new Date().clearTime());
16878     },
16879     
16880     resize : function() {
16881         var sz  = this.el.getSize();
16882         
16883         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16884         this.el.select('.fc-day-content div',true).setHeight(34);
16885     },
16886     
16887     
16888     // private
16889     showPrevMonth : function(e){
16890         this.update(this.activeDate.add("mo", -1));
16891     },
16892     showToday : function(e){
16893         this.update(new Date().clearTime());
16894     },
16895     // private
16896     showNextMonth : function(e){
16897         this.update(this.activeDate.add("mo", 1));
16898     },
16899
16900     // private
16901     showPrevYear : function(){
16902         this.update(this.activeDate.add("y", -1));
16903     },
16904
16905     // private
16906     showNextYear : function(){
16907         this.update(this.activeDate.add("y", 1));
16908     },
16909
16910     
16911    // private
16912     update : function(date)
16913     {
16914         var vd = this.activeDate;
16915         this.activeDate = date;
16916 //        if(vd && this.el){
16917 //            var t = date.getTime();
16918 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16919 //                Roo.log('using add remove');
16920 //                
16921 //                this.fireEvent('monthchange', this, date);
16922 //                
16923 //                this.cells.removeClass("fc-state-highlight");
16924 //                this.cells.each(function(c){
16925 //                   if(c.dateValue == t){
16926 //                       c.addClass("fc-state-highlight");
16927 //                       setTimeout(function(){
16928 //                            try{c.dom.firstChild.focus();}catch(e){}
16929 //                       }, 50);
16930 //                       return false;
16931 //                   }
16932 //                   return true;
16933 //                });
16934 //                return;
16935 //            }
16936 //        }
16937         
16938         var days = date.getDaysInMonth();
16939         
16940         var firstOfMonth = date.getFirstDateOfMonth();
16941         var startingPos = firstOfMonth.getDay()-this.startDay;
16942         
16943         if(startingPos < this.startDay){
16944             startingPos += 7;
16945         }
16946         
16947         var pm = date.add(Date.MONTH, -1);
16948         var prevStart = pm.getDaysInMonth()-startingPos;
16949 //        
16950         this.cells = this.el.select('.fc-day',true);
16951         this.textNodes = this.el.query('.fc-day-number');
16952         this.cells.addClassOnOver('fc-state-hover');
16953         
16954         var cells = this.cells.elements;
16955         var textEls = this.textNodes;
16956         
16957         Roo.each(cells, function(cell){
16958             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16959         });
16960         
16961         days += startingPos;
16962
16963         // convert everything to numbers so it's fast
16964         var day = 86400000;
16965         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16966         //Roo.log(d);
16967         //Roo.log(pm);
16968         //Roo.log(prevStart);
16969         
16970         var today = new Date().clearTime().getTime();
16971         var sel = date.clearTime().getTime();
16972         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16973         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16974         var ddMatch = this.disabledDatesRE;
16975         var ddText = this.disabledDatesText;
16976         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16977         var ddaysText = this.disabledDaysText;
16978         var format = this.format;
16979         
16980         var setCellClass = function(cal, cell){
16981             cell.row = 0;
16982             cell.events = [];
16983             cell.more = [];
16984             //Roo.log('set Cell Class');
16985             cell.title = "";
16986             var t = d.getTime();
16987             
16988             //Roo.log(d);
16989             
16990             cell.dateValue = t;
16991             if(t == today){
16992                 cell.className += " fc-today";
16993                 cell.className += " fc-state-highlight";
16994                 cell.title = cal.todayText;
16995             }
16996             if(t == sel){
16997                 // disable highlight in other month..
16998                 //cell.className += " fc-state-highlight";
16999                 
17000             }
17001             // disabling
17002             if(t < min) {
17003                 cell.className = " fc-state-disabled";
17004                 cell.title = cal.minText;
17005                 return;
17006             }
17007             if(t > max) {
17008                 cell.className = " fc-state-disabled";
17009                 cell.title = cal.maxText;
17010                 return;
17011             }
17012             if(ddays){
17013                 if(ddays.indexOf(d.getDay()) != -1){
17014                     cell.title = ddaysText;
17015                     cell.className = " fc-state-disabled";
17016                 }
17017             }
17018             if(ddMatch && format){
17019                 var fvalue = d.dateFormat(format);
17020                 if(ddMatch.test(fvalue)){
17021                     cell.title = ddText.replace("%0", fvalue);
17022                     cell.className = " fc-state-disabled";
17023                 }
17024             }
17025             
17026             if (!cell.initialClassName) {
17027                 cell.initialClassName = cell.dom.className;
17028             }
17029             
17030             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17031         };
17032
17033         var i = 0;
17034         
17035         for(; i < startingPos; i++) {
17036             textEls[i].innerHTML = (++prevStart);
17037             d.setDate(d.getDate()+1);
17038             
17039             cells[i].className = "fc-past fc-other-month";
17040             setCellClass(this, cells[i]);
17041         }
17042         
17043         var intDay = 0;
17044         
17045         for(; i < days; i++){
17046             intDay = i - startingPos + 1;
17047             textEls[i].innerHTML = (intDay);
17048             d.setDate(d.getDate()+1);
17049             
17050             cells[i].className = ''; // "x-date-active";
17051             setCellClass(this, cells[i]);
17052         }
17053         var extraDays = 0;
17054         
17055         for(; i < 42; i++) {
17056             textEls[i].innerHTML = (++extraDays);
17057             d.setDate(d.getDate()+1);
17058             
17059             cells[i].className = "fc-future fc-other-month";
17060             setCellClass(this, cells[i]);
17061         }
17062         
17063         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17064         
17065         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17066         
17067         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17068         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17069         
17070         if(totalRows != 6){
17071             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17072             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17073         }
17074         
17075         this.fireEvent('monthchange', this, date);
17076         
17077         
17078         /*
17079         if(!this.internalRender){
17080             var main = this.el.dom.firstChild;
17081             var w = main.offsetWidth;
17082             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17083             Roo.fly(main).setWidth(w);
17084             this.internalRender = true;
17085             // opera does not respect the auto grow header center column
17086             // then, after it gets a width opera refuses to recalculate
17087             // without a second pass
17088             if(Roo.isOpera && !this.secondPass){
17089                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17090                 this.secondPass = true;
17091                 this.update.defer(10, this, [date]);
17092             }
17093         }
17094         */
17095         
17096     },
17097     
17098     findCell : function(dt) {
17099         dt = dt.clearTime().getTime();
17100         var ret = false;
17101         this.cells.each(function(c){
17102             //Roo.log("check " +c.dateValue + '?=' + dt);
17103             if(c.dateValue == dt){
17104                 ret = c;
17105                 return false;
17106             }
17107             return true;
17108         });
17109         
17110         return ret;
17111     },
17112     
17113     findCells : function(ev) {
17114         var s = ev.start.clone().clearTime().getTime();
17115        // Roo.log(s);
17116         var e= ev.end.clone().clearTime().getTime();
17117        // Roo.log(e);
17118         var ret = [];
17119         this.cells.each(function(c){
17120              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17121             
17122             if(c.dateValue > e){
17123                 return ;
17124             }
17125             if(c.dateValue < s){
17126                 return ;
17127             }
17128             ret.push(c);
17129         });
17130         
17131         return ret;    
17132     },
17133     
17134 //    findBestRow: function(cells)
17135 //    {
17136 //        var ret = 0;
17137 //        
17138 //        for (var i =0 ; i < cells.length;i++) {
17139 //            ret  = Math.max(cells[i].rows || 0,ret);
17140 //        }
17141 //        return ret;
17142 //        
17143 //    },
17144     
17145     
17146     addItem : function(ev)
17147     {
17148         // look for vertical location slot in
17149         var cells = this.findCells(ev);
17150         
17151 //        ev.row = this.findBestRow(cells);
17152         
17153         // work out the location.
17154         
17155         var crow = false;
17156         var rows = [];
17157         for(var i =0; i < cells.length; i++) {
17158             
17159             cells[i].row = cells[0].row;
17160             
17161             if(i == 0){
17162                 cells[i].row = cells[i].row + 1;
17163             }
17164             
17165             if (!crow) {
17166                 crow = {
17167                     start : cells[i],
17168                     end :  cells[i]
17169                 };
17170                 continue;
17171             }
17172             if (crow.start.getY() == cells[i].getY()) {
17173                 // on same row.
17174                 crow.end = cells[i];
17175                 continue;
17176             }
17177             // different row.
17178             rows.push(crow);
17179             crow = {
17180                 start: cells[i],
17181                 end : cells[i]
17182             };
17183             
17184         }
17185         
17186         rows.push(crow);
17187         ev.els = [];
17188         ev.rows = rows;
17189         ev.cells = cells;
17190         
17191         cells[0].events.push(ev);
17192         
17193         this.calevents.push(ev);
17194     },
17195     
17196     clearEvents: function() {
17197         
17198         if(!this.calevents){
17199             return;
17200         }
17201         
17202         Roo.each(this.cells.elements, function(c){
17203             c.row = 0;
17204             c.events = [];
17205             c.more = [];
17206         });
17207         
17208         Roo.each(this.calevents, function(e) {
17209             Roo.each(e.els, function(el) {
17210                 el.un('mouseenter' ,this.onEventEnter, this);
17211                 el.un('mouseleave' ,this.onEventLeave, this);
17212                 el.remove();
17213             },this);
17214         },this);
17215         
17216         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17217             e.remove();
17218         });
17219         
17220     },
17221     
17222     renderEvents: function()
17223     {   
17224         var _this = this;
17225         
17226         this.cells.each(function(c) {
17227             
17228             if(c.row < 5){
17229                 return;
17230             }
17231             
17232             var ev = c.events;
17233             
17234             var r = 4;
17235             if(c.row != c.events.length){
17236                 r = 4 - (4 - (c.row - c.events.length));
17237             }
17238             
17239             c.events = ev.slice(0, r);
17240             c.more = ev.slice(r);
17241             
17242             if(c.more.length && c.more.length == 1){
17243                 c.events.push(c.more.pop());
17244             }
17245             
17246             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17247             
17248         });
17249             
17250         this.cells.each(function(c) {
17251             
17252             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17253             
17254             
17255             for (var e = 0; e < c.events.length; e++){
17256                 var ev = c.events[e];
17257                 var rows = ev.rows;
17258                 
17259                 for(var i = 0; i < rows.length; i++) {
17260                 
17261                     // how many rows should it span..
17262
17263                     var  cfg = {
17264                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17265                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17266
17267                         unselectable : "on",
17268                         cn : [
17269                             {
17270                                 cls: 'fc-event-inner',
17271                                 cn : [
17272     //                                {
17273     //                                  tag:'span',
17274     //                                  cls: 'fc-event-time',
17275     //                                  html : cells.length > 1 ? '' : ev.time
17276     //                                },
17277                                     {
17278                                       tag:'span',
17279                                       cls: 'fc-event-title',
17280                                       html : String.format('{0}', ev.title)
17281                                     }
17282
17283
17284                                 ]
17285                             },
17286                             {
17287                                 cls: 'ui-resizable-handle ui-resizable-e',
17288                                 html : '&nbsp;&nbsp;&nbsp'
17289                             }
17290
17291                         ]
17292                     };
17293
17294                     if (i == 0) {
17295                         cfg.cls += ' fc-event-start';
17296                     }
17297                     if ((i+1) == rows.length) {
17298                         cfg.cls += ' fc-event-end';
17299                     }
17300
17301                     var ctr = _this.el.select('.fc-event-container',true).first();
17302                     var cg = ctr.createChild(cfg);
17303
17304                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17305                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17306
17307                     var r = (c.more.length) ? 1 : 0;
17308                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17309                     cg.setWidth(ebox.right - sbox.x -2);
17310
17311                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17312                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17313                     cg.on('click', _this.onEventClick, _this, ev);
17314
17315                     ev.els.push(cg);
17316                     
17317                 }
17318                 
17319             }
17320             
17321             
17322             if(c.more.length){
17323                 var  cfg = {
17324                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17325                     style : 'position: absolute',
17326                     unselectable : "on",
17327                     cn : [
17328                         {
17329                             cls: 'fc-event-inner',
17330                             cn : [
17331                                 {
17332                                   tag:'span',
17333                                   cls: 'fc-event-title',
17334                                   html : 'More'
17335                                 }
17336
17337
17338                             ]
17339                         },
17340                         {
17341                             cls: 'ui-resizable-handle ui-resizable-e',
17342                             html : '&nbsp;&nbsp;&nbsp'
17343                         }
17344
17345                     ]
17346                 };
17347
17348                 var ctr = _this.el.select('.fc-event-container',true).first();
17349                 var cg = ctr.createChild(cfg);
17350
17351                 var sbox = c.select('.fc-day-content',true).first().getBox();
17352                 var ebox = c.select('.fc-day-content',true).first().getBox();
17353                 //Roo.log(cg);
17354                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17355                 cg.setWidth(ebox.right - sbox.x -2);
17356
17357                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17358                 
17359             }
17360             
17361         });
17362         
17363         
17364         
17365     },
17366     
17367     onEventEnter: function (e, el,event,d) {
17368         this.fireEvent('evententer', this, el, event);
17369     },
17370     
17371     onEventLeave: function (e, el,event,d) {
17372         this.fireEvent('eventleave', this, el, event);
17373     },
17374     
17375     onEventClick: function (e, el,event,d) {
17376         this.fireEvent('eventclick', this, el, event);
17377     },
17378     
17379     onMonthChange: function () {
17380         this.store.load();
17381     },
17382     
17383     onMoreEventClick: function(e, el, more)
17384     {
17385         var _this = this;
17386         
17387         this.calpopover.placement = 'right';
17388         this.calpopover.setTitle('More');
17389         
17390         this.calpopover.setContent('');
17391         
17392         var ctr = this.calpopover.el.select('.popover-content', true).first();
17393         
17394         Roo.each(more, function(m){
17395             var cfg = {
17396                 cls : 'fc-event-hori fc-event-draggable',
17397                 html : m.title
17398             };
17399             var cg = ctr.createChild(cfg);
17400             
17401             cg.on('click', _this.onEventClick, _this, m);
17402         });
17403         
17404         this.calpopover.show(el);
17405         
17406         
17407     },
17408     
17409     onLoad: function () 
17410     {   
17411         this.calevents = [];
17412         var cal = this;
17413         
17414         if(this.store.getCount() > 0){
17415             this.store.data.each(function(d){
17416                cal.addItem({
17417                     id : d.data.id,
17418                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17419                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17420                     time : d.data.start_time,
17421                     title : d.data.title,
17422                     description : d.data.description,
17423                     venue : d.data.venue
17424                 });
17425             });
17426         }
17427         
17428         this.renderEvents();
17429         
17430         if(this.calevents.length && this.loadMask){
17431             this.maskEl.hide();
17432         }
17433     },
17434     
17435     onBeforeLoad: function()
17436     {
17437         this.clearEvents();
17438         if(this.loadMask){
17439             this.maskEl.show();
17440         }
17441     }
17442 });
17443
17444  
17445  /*
17446  * - LGPL
17447  *
17448  * element
17449  * 
17450  */
17451
17452 /**
17453  * @class Roo.bootstrap.Popover
17454  * @extends Roo.bootstrap.Component
17455  * Bootstrap Popover class
17456  * @cfg {String} html contents of the popover   (or false to use children..)
17457  * @cfg {String} title of popover (or false to hide)
17458  * @cfg {String} placement how it is placed
17459  * @cfg {String} trigger click || hover (or false to trigger manually)
17460  * @cfg {String} over what (parent or false to trigger manually.)
17461  * @cfg {Number} delay - delay before showing
17462  
17463  * @constructor
17464  * Create a new Popover
17465  * @param {Object} config The config object
17466  */
17467
17468 Roo.bootstrap.Popover = function(config){
17469     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17470     
17471     this.addEvents({
17472         // raw events
17473          /**
17474          * @event show
17475          * After the popover show
17476          * 
17477          * @param {Roo.bootstrap.Popover} this
17478          */
17479         "show" : true,
17480         /**
17481          * @event hide
17482          * After the popover hide
17483          * 
17484          * @param {Roo.bootstrap.Popover} this
17485          */
17486         "hide" : true
17487     });
17488 };
17489
17490 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17491     
17492     title: 'Fill in a title',
17493     html: false,
17494     
17495     placement : 'right',
17496     trigger : 'hover', // hover
17497     
17498     delay : 0,
17499     
17500     over: 'parent',
17501     
17502     can_build_overlaid : false,
17503     
17504     getChildContainer : function()
17505     {
17506         return this.el.select('.popover-content',true).first();
17507     },
17508     
17509     getAutoCreate : function(){
17510          
17511         var cfg = {
17512            cls : 'popover roo-dynamic',
17513            style: 'display:block',
17514            cn : [
17515                 {
17516                     cls : 'arrow'
17517                 },
17518                 {
17519                     cls : 'popover-inner',
17520                     cn : [
17521                         {
17522                             tag: 'h3',
17523                             cls: 'popover-title',
17524                             html : this.title
17525                         },
17526                         {
17527                             cls : 'popover-content',
17528                             html : this.html
17529                         }
17530                     ]
17531                     
17532                 }
17533            ]
17534         };
17535         
17536         return cfg;
17537     },
17538     setTitle: function(str)
17539     {
17540         this.title = str;
17541         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17542     },
17543     setContent: function(str)
17544     {
17545         this.html = str;
17546         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17547     },
17548     // as it get's added to the bottom of the page.
17549     onRender : function(ct, position)
17550     {
17551         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17552         if(!this.el){
17553             var cfg = Roo.apply({},  this.getAutoCreate());
17554             cfg.id = Roo.id();
17555             
17556             if (this.cls) {
17557                 cfg.cls += ' ' + this.cls;
17558             }
17559             if (this.style) {
17560                 cfg.style = this.style;
17561             }
17562             //Roo.log("adding to ");
17563             this.el = Roo.get(document.body).createChild(cfg, position);
17564 //            Roo.log(this.el);
17565         }
17566         this.initEvents();
17567     },
17568     
17569     initEvents : function()
17570     {
17571         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17572         this.el.enableDisplayMode('block');
17573         this.el.hide();
17574         if (this.over === false) {
17575             return; 
17576         }
17577         if (this.triggers === false) {
17578             return;
17579         }
17580         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17581         var triggers = this.trigger ? this.trigger.split(' ') : [];
17582         Roo.each(triggers, function(trigger) {
17583         
17584             if (trigger == 'click') {
17585                 on_el.on('click', this.toggle, this);
17586             } else if (trigger != 'manual') {
17587                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17588                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17589       
17590                 on_el.on(eventIn  ,this.enter, this);
17591                 on_el.on(eventOut, this.leave, this);
17592             }
17593         }, this);
17594         
17595     },
17596     
17597     
17598     // private
17599     timeout : null,
17600     hoverState : null,
17601     
17602     toggle : function () {
17603         this.hoverState == 'in' ? this.leave() : this.enter();
17604     },
17605     
17606     enter : function () {
17607         
17608         clearTimeout(this.timeout);
17609     
17610         this.hoverState = 'in';
17611     
17612         if (!this.delay || !this.delay.show) {
17613             this.show();
17614             return;
17615         }
17616         var _t = this;
17617         this.timeout = setTimeout(function () {
17618             if (_t.hoverState == 'in') {
17619                 _t.show();
17620             }
17621         }, this.delay.show)
17622     },
17623     
17624     leave : function() {
17625         clearTimeout(this.timeout);
17626     
17627         this.hoverState = 'out';
17628     
17629         if (!this.delay || !this.delay.hide) {
17630             this.hide();
17631             return;
17632         }
17633         var _t = this;
17634         this.timeout = setTimeout(function () {
17635             if (_t.hoverState == 'out') {
17636                 _t.hide();
17637             }
17638         }, this.delay.hide)
17639     },
17640     
17641     show : function (on_el)
17642     {
17643         if (!on_el) {
17644             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17645         }
17646         
17647         // set content.
17648         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17649         if (this.html !== false) {
17650             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17651         }
17652         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17653         if (!this.title.length) {
17654             this.el.select('.popover-title',true).hide();
17655         }
17656         
17657         var placement = typeof this.placement == 'function' ?
17658             this.placement.call(this, this.el, on_el) :
17659             this.placement;
17660             
17661         var autoToken = /\s?auto?\s?/i;
17662         var autoPlace = autoToken.test(placement);
17663         if (autoPlace) {
17664             placement = placement.replace(autoToken, '') || 'top';
17665         }
17666         
17667         //this.el.detach()
17668         //this.el.setXY([0,0]);
17669         this.el.show();
17670         this.el.dom.style.display='block';
17671         this.el.addClass(placement);
17672         
17673         //this.el.appendTo(on_el);
17674         
17675         var p = this.getPosition();
17676         var box = this.el.getBox();
17677         
17678         if (autoPlace) {
17679             // fixme..
17680         }
17681         var align = Roo.bootstrap.Popover.alignment[placement];
17682         
17683 //        Roo.log(align);
17684         this.el.alignTo(on_el, align[0],align[1]);
17685         //var arrow = this.el.select('.arrow',true).first();
17686         //arrow.set(align[2], 
17687         
17688         this.el.addClass('in');
17689         
17690         
17691         if (this.el.hasClass('fade')) {
17692             // fade it?
17693         }
17694         
17695         this.hoverState = 'in';
17696         
17697         this.fireEvent('show', this);
17698         
17699     },
17700     hide : function()
17701     {
17702         this.el.setXY([0,0]);
17703         this.el.removeClass('in');
17704         this.el.hide();
17705         this.hoverState = null;
17706         
17707         this.fireEvent('hide', this);
17708     }
17709     
17710 });
17711
17712 Roo.bootstrap.Popover.alignment = {
17713     'left' : ['r-l', [-10,0], 'right'],
17714     'right' : ['l-r', [10,0], 'left'],
17715     'bottom' : ['t-b', [0,10], 'top'],
17716     'top' : [ 'b-t', [0,-10], 'bottom']
17717 };
17718
17719  /*
17720  * - LGPL
17721  *
17722  * Progress
17723  * 
17724  */
17725
17726 /**
17727  * @class Roo.bootstrap.Progress
17728  * @extends Roo.bootstrap.Component
17729  * Bootstrap Progress class
17730  * @cfg {Boolean} striped striped of the progress bar
17731  * @cfg {Boolean} active animated of the progress bar
17732  * 
17733  * 
17734  * @constructor
17735  * Create a new Progress
17736  * @param {Object} config The config object
17737  */
17738
17739 Roo.bootstrap.Progress = function(config){
17740     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17741 };
17742
17743 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17744     
17745     striped : false,
17746     active: false,
17747     
17748     getAutoCreate : function(){
17749         var cfg = {
17750             tag: 'div',
17751             cls: 'progress'
17752         };
17753         
17754         
17755         if(this.striped){
17756             cfg.cls += ' progress-striped';
17757         }
17758       
17759         if(this.active){
17760             cfg.cls += ' active';
17761         }
17762         
17763         
17764         return cfg;
17765     }
17766    
17767 });
17768
17769  
17770
17771  /*
17772  * - LGPL
17773  *
17774  * ProgressBar
17775  * 
17776  */
17777
17778 /**
17779  * @class Roo.bootstrap.ProgressBar
17780  * @extends Roo.bootstrap.Component
17781  * Bootstrap ProgressBar class
17782  * @cfg {Number} aria_valuenow aria-value now
17783  * @cfg {Number} aria_valuemin aria-value min
17784  * @cfg {Number} aria_valuemax aria-value max
17785  * @cfg {String} label label for the progress bar
17786  * @cfg {String} panel (success | info | warning | danger )
17787  * @cfg {String} role role of the progress bar
17788  * @cfg {String} sr_only text
17789  * 
17790  * 
17791  * @constructor
17792  * Create a new ProgressBar
17793  * @param {Object} config The config object
17794  */
17795
17796 Roo.bootstrap.ProgressBar = function(config){
17797     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17798 };
17799
17800 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17801     
17802     aria_valuenow : 0,
17803     aria_valuemin : 0,
17804     aria_valuemax : 100,
17805     label : false,
17806     panel : false,
17807     role : false,
17808     sr_only: false,
17809     
17810     getAutoCreate : function()
17811     {
17812         
17813         var cfg = {
17814             tag: 'div',
17815             cls: 'progress-bar',
17816             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17817         };
17818         
17819         if(this.sr_only){
17820             cfg.cn = {
17821                 tag: 'span',
17822                 cls: 'sr-only',
17823                 html: this.sr_only
17824             }
17825         }
17826         
17827         if(this.role){
17828             cfg.role = this.role;
17829         }
17830         
17831         if(this.aria_valuenow){
17832             cfg['aria-valuenow'] = this.aria_valuenow;
17833         }
17834         
17835         if(this.aria_valuemin){
17836             cfg['aria-valuemin'] = this.aria_valuemin;
17837         }
17838         
17839         if(this.aria_valuemax){
17840             cfg['aria-valuemax'] = this.aria_valuemax;
17841         }
17842         
17843         if(this.label && !this.sr_only){
17844             cfg.html = this.label;
17845         }
17846         
17847         if(this.panel){
17848             cfg.cls += ' progress-bar-' + this.panel;
17849         }
17850         
17851         return cfg;
17852     },
17853     
17854     update : function(aria_valuenow)
17855     {
17856         this.aria_valuenow = aria_valuenow;
17857         
17858         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17859     }
17860    
17861 });
17862
17863  
17864
17865  /*
17866  * - LGPL
17867  *
17868  * column
17869  * 
17870  */
17871
17872 /**
17873  * @class Roo.bootstrap.TabGroup
17874  * @extends Roo.bootstrap.Column
17875  * Bootstrap Column class
17876  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17877  * @cfg {Boolean} carousel true to make the group behave like a carousel
17878  * @cfg {Boolean} bullets show bullets for the panels
17879  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17880  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17881  * @cfg {Boolean} showarrow (true|false) show arrow default true
17882  * 
17883  * @constructor
17884  * Create a new TabGroup
17885  * @param {Object} config The config object
17886  */
17887
17888 Roo.bootstrap.TabGroup = function(config){
17889     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17890     if (!this.navId) {
17891         this.navId = Roo.id();
17892     }
17893     this.tabs = [];
17894     Roo.bootstrap.TabGroup.register(this);
17895     
17896 };
17897
17898 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17899     
17900     carousel : false,
17901     transition : false,
17902     bullets : 0,
17903     timer : 0,
17904     autoslide : false,
17905     slideFn : false,
17906     slideOnTouch : false,
17907     showarrow : true,
17908     
17909     getAutoCreate : function()
17910     {
17911         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17912         
17913         cfg.cls += ' tab-content';
17914         
17915         if (this.carousel) {
17916             cfg.cls += ' carousel slide';
17917             
17918             cfg.cn = [{
17919                cls : 'carousel-inner',
17920                cn : []
17921             }];
17922         
17923             if(this.bullets  && !Roo.isTouch){
17924                 
17925                 var bullets = {
17926                     cls : 'carousel-bullets',
17927                     cn : []
17928                 };
17929                
17930                 if(this.bullets_cls){
17931                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17932                 }
17933                 
17934                 bullets.cn.push({
17935                     cls : 'clear'
17936                 });
17937                 
17938                 cfg.cn[0].cn.push(bullets);
17939             }
17940             
17941             if(this.showarrow){
17942                 cfg.cn[0].cn.push({
17943                     tag : 'div',
17944                     class : 'carousel-arrow',
17945                     cn : [
17946                         {
17947                             tag : 'div',
17948                             class : 'carousel-prev',
17949                             cn : [
17950                                 {
17951                                     tag : 'i',
17952                                     class : 'fa fa-chevron-left'
17953                                 }
17954                             ]
17955                         },
17956                         {
17957                             tag : 'div',
17958                             class : 'carousel-next',
17959                             cn : [
17960                                 {
17961                                     tag : 'i',
17962                                     class : 'fa fa-chevron-right'
17963                                 }
17964                             ]
17965                         }
17966                     ]
17967                 });
17968             }
17969             
17970         }
17971         
17972         return cfg;
17973     },
17974     
17975     initEvents:  function()
17976     {
17977 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17978 //            this.el.on("touchstart", this.onTouchStart, this);
17979 //        }
17980         
17981         if(this.autoslide){
17982             var _this = this;
17983             
17984             this.slideFn = window.setInterval(function() {
17985                 _this.showPanelNext();
17986             }, this.timer);
17987         }
17988         
17989         if(this.showarrow){
17990             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17991             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17992         }
17993         
17994         
17995     },
17996     
17997 //    onTouchStart : function(e, el, o)
17998 //    {
17999 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18000 //            return;
18001 //        }
18002 //        
18003 //        this.showPanelNext();
18004 //    },
18005     
18006     
18007     getChildContainer : function()
18008     {
18009         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18010     },
18011     
18012     /**
18013     * register a Navigation item
18014     * @param {Roo.bootstrap.NavItem} the navitem to add
18015     */
18016     register : function(item)
18017     {
18018         this.tabs.push( item);
18019         item.navId = this.navId; // not really needed..
18020         this.addBullet();
18021     
18022     },
18023     
18024     getActivePanel : function()
18025     {
18026         var r = false;
18027         Roo.each(this.tabs, function(t) {
18028             if (t.active) {
18029                 r = t;
18030                 return false;
18031             }
18032             return null;
18033         });
18034         return r;
18035         
18036     },
18037     getPanelByName : function(n)
18038     {
18039         var r = false;
18040         Roo.each(this.tabs, function(t) {
18041             if (t.tabId == n) {
18042                 r = t;
18043                 return false;
18044             }
18045             return null;
18046         });
18047         return r;
18048     },
18049     indexOfPanel : function(p)
18050     {
18051         var r = false;
18052         Roo.each(this.tabs, function(t,i) {
18053             if (t.tabId == p.tabId) {
18054                 r = i;
18055                 return false;
18056             }
18057             return null;
18058         });
18059         return r;
18060     },
18061     /**
18062      * show a specific panel
18063      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18064      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18065      */
18066     showPanel : function (pan)
18067     {
18068         if(this.transition || typeof(pan) == 'undefined'){
18069             Roo.log("waiting for the transitionend");
18070             return;
18071         }
18072         
18073         if (typeof(pan) == 'number') {
18074             pan = this.tabs[pan];
18075         }
18076         
18077         if (typeof(pan) == 'string') {
18078             pan = this.getPanelByName(pan);
18079         }
18080         
18081         var cur = this.getActivePanel();
18082         
18083         if(!pan || !cur){
18084             Roo.log('pan or acitve pan is undefined');
18085             return false;
18086         }
18087         
18088         if (pan.tabId == this.getActivePanel().tabId) {
18089             return true;
18090         }
18091         
18092         if (false === cur.fireEvent('beforedeactivate')) {
18093             return false;
18094         }
18095         
18096         if(this.bullets > 0 && !Roo.isTouch){
18097             this.setActiveBullet(this.indexOfPanel(pan));
18098         }
18099         
18100         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18101             
18102             this.transition = true;
18103             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18104             var lr = dir == 'next' ? 'left' : 'right';
18105             pan.el.addClass(dir); // or prev
18106             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18107             cur.el.addClass(lr); // or right
18108             pan.el.addClass(lr);
18109             
18110             var _this = this;
18111             cur.el.on('transitionend', function() {
18112                 Roo.log("trans end?");
18113                 
18114                 pan.el.removeClass([lr,dir]);
18115                 pan.setActive(true);
18116                 
18117                 cur.el.removeClass([lr]);
18118                 cur.setActive(false);
18119                 
18120                 _this.transition = false;
18121                 
18122             }, this, { single:  true } );
18123             
18124             return true;
18125         }
18126         
18127         cur.setActive(false);
18128         pan.setActive(true);
18129         
18130         return true;
18131         
18132     },
18133     showPanelNext : function()
18134     {
18135         var i = this.indexOfPanel(this.getActivePanel());
18136         
18137         if (i >= this.tabs.length - 1 && !this.autoslide) {
18138             return;
18139         }
18140         
18141         if (i >= this.tabs.length - 1 && this.autoslide) {
18142             i = -1;
18143         }
18144         
18145         this.showPanel(this.tabs[i+1]);
18146     },
18147     
18148     showPanelPrev : function()
18149     {
18150         var i = this.indexOfPanel(this.getActivePanel());
18151         
18152         if (i  < 1 && !this.autoslide) {
18153             return;
18154         }
18155         
18156         if (i < 1 && this.autoslide) {
18157             i = this.tabs.length;
18158         }
18159         
18160         this.showPanel(this.tabs[i-1]);
18161     },
18162     
18163     
18164     addBullet: function()
18165     {
18166         if(!this.bullets || Roo.isTouch){
18167             return;
18168         }
18169         var ctr = this.el.select('.carousel-bullets',true).first();
18170         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18171         var bullet = ctr.createChild({
18172             cls : 'bullet bullet-' + i
18173         },ctr.dom.lastChild);
18174         
18175         
18176         var _this = this;
18177         
18178         bullet.on('click', (function(e, el, o, ii, t){
18179
18180             e.preventDefault();
18181
18182             this.showPanel(ii);
18183
18184             if(this.autoslide && this.slideFn){
18185                 clearInterval(this.slideFn);
18186                 this.slideFn = window.setInterval(function() {
18187                     _this.showPanelNext();
18188                 }, this.timer);
18189             }
18190
18191         }).createDelegate(this, [i, bullet], true));
18192                 
18193         
18194     },
18195      
18196     setActiveBullet : function(i)
18197     {
18198         if(Roo.isTouch){
18199             return;
18200         }
18201         
18202         Roo.each(this.el.select('.bullet', true).elements, function(el){
18203             el.removeClass('selected');
18204         });
18205
18206         var bullet = this.el.select('.bullet-' + i, true).first();
18207         
18208         if(!bullet){
18209             return;
18210         }
18211         
18212         bullet.addClass('selected');
18213     }
18214     
18215     
18216   
18217 });
18218
18219  
18220
18221  
18222  
18223 Roo.apply(Roo.bootstrap.TabGroup, {
18224     
18225     groups: {},
18226      /**
18227     * register a Navigation Group
18228     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18229     */
18230     register : function(navgrp)
18231     {
18232         this.groups[navgrp.navId] = navgrp;
18233         
18234     },
18235     /**
18236     * fetch a Navigation Group based on the navigation ID
18237     * if one does not exist , it will get created.
18238     * @param {string} the navgroup to add
18239     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18240     */
18241     get: function(navId) {
18242         if (typeof(this.groups[navId]) == 'undefined') {
18243             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18244         }
18245         return this.groups[navId] ;
18246     }
18247     
18248     
18249     
18250 });
18251
18252  /*
18253  * - LGPL
18254  *
18255  * TabPanel
18256  * 
18257  */
18258
18259 /**
18260  * @class Roo.bootstrap.TabPanel
18261  * @extends Roo.bootstrap.Component
18262  * Bootstrap TabPanel class
18263  * @cfg {Boolean} active panel active
18264  * @cfg {String} html panel content
18265  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18266  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18267  * @cfg {String} href click to link..
18268  * 
18269  * 
18270  * @constructor
18271  * Create a new TabPanel
18272  * @param {Object} config The config object
18273  */
18274
18275 Roo.bootstrap.TabPanel = function(config){
18276     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18277     this.addEvents({
18278         /**
18279              * @event changed
18280              * Fires when the active status changes
18281              * @param {Roo.bootstrap.TabPanel} this
18282              * @param {Boolean} state the new state
18283             
18284          */
18285         'changed': true,
18286         /**
18287              * @event beforedeactivate
18288              * Fires before a tab is de-activated - can be used to do validation on a form.
18289              * @param {Roo.bootstrap.TabPanel} this
18290              * @return {Boolean} false if there is an error
18291             
18292          */
18293         'beforedeactivate': true
18294      });
18295     
18296     this.tabId = this.tabId || Roo.id();
18297   
18298 };
18299
18300 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18301     
18302     active: false,
18303     html: false,
18304     tabId: false,
18305     navId : false,
18306     href : '',
18307     
18308     getAutoCreate : function(){
18309         var cfg = {
18310             tag: 'div',
18311             // item is needed for carousel - not sure if it has any effect otherwise
18312             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18313             html: this.html || ''
18314         };
18315         
18316         if(this.active){
18317             cfg.cls += ' active';
18318         }
18319         
18320         if(this.tabId){
18321             cfg.tabId = this.tabId;
18322         }
18323         
18324         
18325         return cfg;
18326     },
18327     
18328     initEvents:  function()
18329     {
18330         var p = this.parent();
18331         
18332         this.navId = this.navId || p.navId;
18333         
18334         if (typeof(this.navId) != 'undefined') {
18335             // not really needed.. but just in case.. parent should be a NavGroup.
18336             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18337             
18338             tg.register(this);
18339             
18340             var i = tg.tabs.length - 1;
18341             
18342             if(this.active && tg.bullets > 0 && i < tg.bullets){
18343                 tg.setActiveBullet(i);
18344             }
18345         }
18346         
18347         this.el.on('click', this.onClick, this);
18348         
18349         if(Roo.isTouch){
18350             this.el.on("touchstart", this.onTouchStart, this);
18351             this.el.on("touchmove", this.onTouchMove, this);
18352             this.el.on("touchend", this.onTouchEnd, this);
18353         }
18354         
18355     },
18356     
18357     onRender : function(ct, position)
18358     {
18359         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18360     },
18361     
18362     setActive : function(state)
18363     {
18364         Roo.log("panel - set active " + this.tabId + "=" + state);
18365         
18366         this.active = state;
18367         if (!state) {
18368             this.el.removeClass('active');
18369             
18370         } else  if (!this.el.hasClass('active')) {
18371             this.el.addClass('active');
18372         }
18373         
18374         this.fireEvent('changed', this, state);
18375     },
18376     
18377     onClick : function(e)
18378     {
18379         e.preventDefault();
18380         
18381         if(!this.href.length){
18382             return;
18383         }
18384         
18385         window.location.href = this.href;
18386     },
18387     
18388     startX : 0,
18389     startY : 0,
18390     endX : 0,
18391     endY : 0,
18392     swiping : false,
18393     
18394     onTouchStart : function(e)
18395     {
18396         this.swiping = false;
18397         
18398         this.startX = e.browserEvent.touches[0].clientX;
18399         this.startY = e.browserEvent.touches[0].clientY;
18400     },
18401     
18402     onTouchMove : function(e)
18403     {
18404         this.swiping = true;
18405         
18406         this.endX = e.browserEvent.touches[0].clientX;
18407         this.endY = e.browserEvent.touches[0].clientY;
18408     },
18409     
18410     onTouchEnd : function(e)
18411     {
18412         if(!this.swiping){
18413             this.onClick(e);
18414             return;
18415         }
18416         
18417         var tabGroup = this.parent();
18418         
18419         if(this.endX > this.startX){ // swiping right
18420             tabGroup.showPanelPrev();
18421             return;
18422         }
18423         
18424         if(this.startX > this.endX){ // swiping left
18425             tabGroup.showPanelNext();
18426             return;
18427         }
18428     }
18429     
18430     
18431 });
18432  
18433
18434  
18435
18436  /*
18437  * - LGPL
18438  *
18439  * DateField
18440  * 
18441  */
18442
18443 /**
18444  * @class Roo.bootstrap.DateField
18445  * @extends Roo.bootstrap.Input
18446  * Bootstrap DateField class
18447  * @cfg {Number} weekStart default 0
18448  * @cfg {String} viewMode default empty, (months|years)
18449  * @cfg {String} minViewMode default empty, (months|years)
18450  * @cfg {Number} startDate default -Infinity
18451  * @cfg {Number} endDate default Infinity
18452  * @cfg {Boolean} todayHighlight default false
18453  * @cfg {Boolean} todayBtn default false
18454  * @cfg {Boolean} calendarWeeks default false
18455  * @cfg {Object} daysOfWeekDisabled default empty
18456  * @cfg {Boolean} singleMode default false (true | false)
18457  * 
18458  * @cfg {Boolean} keyboardNavigation default true
18459  * @cfg {String} language default en
18460  * 
18461  * @constructor
18462  * Create a new DateField
18463  * @param {Object} config The config object
18464  */
18465
18466 Roo.bootstrap.DateField = function(config){
18467     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18468      this.addEvents({
18469             /**
18470              * @event show
18471              * Fires when this field show.
18472              * @param {Roo.bootstrap.DateField} this
18473              * @param {Mixed} date The date value
18474              */
18475             show : true,
18476             /**
18477              * @event show
18478              * Fires when this field hide.
18479              * @param {Roo.bootstrap.DateField} this
18480              * @param {Mixed} date The date value
18481              */
18482             hide : true,
18483             /**
18484              * @event select
18485              * Fires when select a date.
18486              * @param {Roo.bootstrap.DateField} this
18487              * @param {Mixed} date The date value
18488              */
18489             select : true,
18490             /**
18491              * @event beforeselect
18492              * Fires when before select a date.
18493              * @param {Roo.bootstrap.DateField} this
18494              * @param {Mixed} date The date value
18495              */
18496             beforeselect : true
18497         });
18498 };
18499
18500 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18501     
18502     /**
18503      * @cfg {String} format
18504      * The default date format string which can be overriden for localization support.  The format must be
18505      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18506      */
18507     format : "m/d/y",
18508     /**
18509      * @cfg {String} altFormats
18510      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18511      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18512      */
18513     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18514     
18515     weekStart : 0,
18516     
18517     viewMode : '',
18518     
18519     minViewMode : '',
18520     
18521     todayHighlight : false,
18522     
18523     todayBtn: false,
18524     
18525     language: 'en',
18526     
18527     keyboardNavigation: true,
18528     
18529     calendarWeeks: false,
18530     
18531     startDate: -Infinity,
18532     
18533     endDate: Infinity,
18534     
18535     daysOfWeekDisabled: [],
18536     
18537     _events: [],
18538     
18539     singleMode : false,
18540     
18541     UTCDate: function()
18542     {
18543         return new Date(Date.UTC.apply(Date, arguments));
18544     },
18545     
18546     UTCToday: function()
18547     {
18548         var today = new Date();
18549         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18550     },
18551     
18552     getDate: function() {
18553             var d = this.getUTCDate();
18554             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18555     },
18556     
18557     getUTCDate: function() {
18558             return this.date;
18559     },
18560     
18561     setDate: function(d) {
18562             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18563     },
18564     
18565     setUTCDate: function(d) {
18566             this.date = d;
18567             this.setValue(this.formatDate(this.date));
18568     },
18569         
18570     onRender: function(ct, position)
18571     {
18572         
18573         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18574         
18575         this.language = this.language || 'en';
18576         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18577         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18578         
18579         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18580         this.format = this.format || 'm/d/y';
18581         this.isInline = false;
18582         this.isInput = true;
18583         this.component = this.el.select('.add-on', true).first() || false;
18584         this.component = (this.component && this.component.length === 0) ? false : this.component;
18585         this.hasInput = this.component && this.inputEl().length;
18586         
18587         if (typeof(this.minViewMode === 'string')) {
18588             switch (this.minViewMode) {
18589                 case 'months':
18590                     this.minViewMode = 1;
18591                     break;
18592                 case 'years':
18593                     this.minViewMode = 2;
18594                     break;
18595                 default:
18596                     this.minViewMode = 0;
18597                     break;
18598             }
18599         }
18600         
18601         if (typeof(this.viewMode === 'string')) {
18602             switch (this.viewMode) {
18603                 case 'months':
18604                     this.viewMode = 1;
18605                     break;
18606                 case 'years':
18607                     this.viewMode = 2;
18608                     break;
18609                 default:
18610                     this.viewMode = 0;
18611                     break;
18612             }
18613         }
18614                 
18615         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18616         
18617 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18618         
18619         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18620         
18621         this.picker().on('mousedown', this.onMousedown, this);
18622         this.picker().on('click', this.onClick, this);
18623         
18624         this.picker().addClass('datepicker-dropdown');
18625         
18626         this.startViewMode = this.viewMode;
18627         
18628         if(this.singleMode){
18629             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18630                 v.setVisibilityMode(Roo.Element.DISPLAY);
18631                 v.hide();
18632             });
18633             
18634             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18635                 v.setStyle('width', '189px');
18636             });
18637         }
18638         
18639         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18640             if(!this.calendarWeeks){
18641                 v.remove();
18642                 return;
18643             }
18644             
18645             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18646             v.attr('colspan', function(i, val){
18647                 return parseInt(val) + 1;
18648             });
18649         });
18650                         
18651         
18652         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18653         
18654         this.setStartDate(this.startDate);
18655         this.setEndDate(this.endDate);
18656         
18657         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18658         
18659         this.fillDow();
18660         this.fillMonths();
18661         this.update();
18662         this.showMode();
18663         
18664         if(this.isInline) {
18665             this.showPopup();
18666         }
18667     },
18668     
18669     picker : function()
18670     {
18671         return this.pickerEl;
18672 //        return this.el.select('.datepicker', true).first();
18673     },
18674     
18675     fillDow: function()
18676     {
18677         var dowCnt = this.weekStart;
18678         
18679         var dow = {
18680             tag: 'tr',
18681             cn: [
18682                 
18683             ]
18684         };
18685         
18686         if(this.calendarWeeks){
18687             dow.cn.push({
18688                 tag: 'th',
18689                 cls: 'cw',
18690                 html: '&nbsp;'
18691             })
18692         }
18693         
18694         while (dowCnt < this.weekStart + 7) {
18695             dow.cn.push({
18696                 tag: 'th',
18697                 cls: 'dow',
18698                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18699             });
18700         }
18701         
18702         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18703     },
18704     
18705     fillMonths: function()
18706     {    
18707         var i = 0;
18708         var months = this.picker().select('>.datepicker-months td', true).first();
18709         
18710         months.dom.innerHTML = '';
18711         
18712         while (i < 12) {
18713             var month = {
18714                 tag: 'span',
18715                 cls: 'month',
18716                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18717             };
18718             
18719             months.createChild(month);
18720         }
18721         
18722     },
18723     
18724     update: function()
18725     {
18726         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;
18727         
18728         if (this.date < this.startDate) {
18729             this.viewDate = new Date(this.startDate);
18730         } else if (this.date > this.endDate) {
18731             this.viewDate = new Date(this.endDate);
18732         } else {
18733             this.viewDate = new Date(this.date);
18734         }
18735         
18736         this.fill();
18737     },
18738     
18739     fill: function() 
18740     {
18741         var d = new Date(this.viewDate),
18742                 year = d.getUTCFullYear(),
18743                 month = d.getUTCMonth(),
18744                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18745                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18746                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18747                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18748                 currentDate = this.date && this.date.valueOf(),
18749                 today = this.UTCToday();
18750         
18751         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18752         
18753 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18754         
18755 //        this.picker.select('>tfoot th.today').
18756 //                                              .text(dates[this.language].today)
18757 //                                              .toggle(this.todayBtn !== false);
18758     
18759         this.updateNavArrows();
18760         this.fillMonths();
18761                                                 
18762         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18763         
18764         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18765          
18766         prevMonth.setUTCDate(day);
18767         
18768         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18769         
18770         var nextMonth = new Date(prevMonth);
18771         
18772         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18773         
18774         nextMonth = nextMonth.valueOf();
18775         
18776         var fillMonths = false;
18777         
18778         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18779         
18780         while(prevMonth.valueOf() <= nextMonth) {
18781             var clsName = '';
18782             
18783             if (prevMonth.getUTCDay() === this.weekStart) {
18784                 if(fillMonths){
18785                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18786                 }
18787                     
18788                 fillMonths = {
18789                     tag: 'tr',
18790                     cn: []
18791                 };
18792                 
18793                 if(this.calendarWeeks){
18794                     // ISO 8601: First week contains first thursday.
18795                     // ISO also states week starts on Monday, but we can be more abstract here.
18796                     var
18797                     // Start of current week: based on weekstart/current date
18798                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18799                     // Thursday of this week
18800                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18801                     // First Thursday of year, year from thursday
18802                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18803                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18804                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18805                     
18806                     fillMonths.cn.push({
18807                         tag: 'td',
18808                         cls: 'cw',
18809                         html: calWeek
18810                     });
18811                 }
18812             }
18813             
18814             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18815                 clsName += ' old';
18816             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18817                 clsName += ' new';
18818             }
18819             if (this.todayHighlight &&
18820                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18821                 prevMonth.getUTCMonth() == today.getMonth() &&
18822                 prevMonth.getUTCDate() == today.getDate()) {
18823                 clsName += ' today';
18824             }
18825             
18826             if (currentDate && prevMonth.valueOf() === currentDate) {
18827                 clsName += ' active';
18828             }
18829             
18830             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18831                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18832                     clsName += ' disabled';
18833             }
18834             
18835             fillMonths.cn.push({
18836                 tag: 'td',
18837                 cls: 'day ' + clsName,
18838                 html: prevMonth.getDate()
18839             });
18840             
18841             prevMonth.setDate(prevMonth.getDate()+1);
18842         }
18843           
18844         var currentYear = this.date && this.date.getUTCFullYear();
18845         var currentMonth = this.date && this.date.getUTCMonth();
18846         
18847         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18848         
18849         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18850             v.removeClass('active');
18851             
18852             if(currentYear === year && k === currentMonth){
18853                 v.addClass('active');
18854             }
18855             
18856             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18857                 v.addClass('disabled');
18858             }
18859             
18860         });
18861         
18862         
18863         year = parseInt(year/10, 10) * 10;
18864         
18865         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18866         
18867         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18868         
18869         year -= 1;
18870         for (var i = -1; i < 11; i++) {
18871             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18872                 tag: 'span',
18873                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18874                 html: year
18875             });
18876             
18877             year += 1;
18878         }
18879     },
18880     
18881     showMode: function(dir) 
18882     {
18883         if (dir) {
18884             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18885         }
18886         
18887         Roo.each(this.picker().select('>div',true).elements, function(v){
18888             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18889             v.hide();
18890         });
18891         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18892     },
18893     
18894     place: function()
18895     {
18896         if(this.isInline) {
18897             return;
18898         }
18899         
18900         this.picker().removeClass(['bottom', 'top']);
18901         
18902         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18903             /*
18904              * place to the top of element!
18905              *
18906              */
18907             
18908             this.picker().addClass('top');
18909             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18910             
18911             return;
18912         }
18913         
18914         this.picker().addClass('bottom');
18915         
18916         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18917     },
18918     
18919     parseDate : function(value)
18920     {
18921         if(!value || value instanceof Date){
18922             return value;
18923         }
18924         var v = Date.parseDate(value, this.format);
18925         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18926             v = Date.parseDate(value, 'Y-m-d');
18927         }
18928         if(!v && this.altFormats){
18929             if(!this.altFormatsArray){
18930                 this.altFormatsArray = this.altFormats.split("|");
18931             }
18932             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18933                 v = Date.parseDate(value, this.altFormatsArray[i]);
18934             }
18935         }
18936         return v;
18937     },
18938     
18939     formatDate : function(date, fmt)
18940     {   
18941         return (!date || !(date instanceof Date)) ?
18942         date : date.dateFormat(fmt || this.format);
18943     },
18944     
18945     onFocus : function()
18946     {
18947         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18948         this.showPopup();
18949     },
18950     
18951     onBlur : function()
18952     {
18953         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18954         
18955         var d = this.inputEl().getValue();
18956         
18957         this.setValue(d);
18958                 
18959         this.hidePopup();
18960     },
18961     
18962     showPopup : function()
18963     {
18964         this.picker().show();
18965         this.update();
18966         this.place();
18967         
18968         this.fireEvent('showpopup', this, this.date);
18969     },
18970     
18971     hidePopup : function()
18972     {
18973         if(this.isInline) {
18974             return;
18975         }
18976         this.picker().hide();
18977         this.viewMode = this.startViewMode;
18978         this.showMode();
18979         
18980         this.fireEvent('hidepopup', this, this.date);
18981         
18982     },
18983     
18984     onMousedown: function(e)
18985     {
18986         e.stopPropagation();
18987         e.preventDefault();
18988     },
18989     
18990     keyup: function(e)
18991     {
18992         Roo.bootstrap.DateField.superclass.keyup.call(this);
18993         this.update();
18994     },
18995
18996     setValue: function(v)
18997     {
18998         if(this.fireEvent('beforeselect', this, v) !== false){
18999             var d = new Date(this.parseDate(v) ).clearTime();
19000         
19001             if(isNaN(d.getTime())){
19002                 this.date = this.viewDate = '';
19003                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19004                 return;
19005             }
19006
19007             v = this.formatDate(d);
19008
19009             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19010
19011             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19012
19013             this.update();
19014
19015             this.fireEvent('select', this, this.date);
19016         }
19017     },
19018     
19019     getValue: function()
19020     {
19021         return this.formatDate(this.date);
19022     },
19023     
19024     fireKey: function(e)
19025     {
19026         if (!this.picker().isVisible()){
19027             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19028                 this.showPopup();
19029             }
19030             return;
19031         }
19032         
19033         var dateChanged = false,
19034         dir, day, month,
19035         newDate, newViewDate;
19036         
19037         switch(e.keyCode){
19038             case 27: // escape
19039                 this.hidePopup();
19040                 e.preventDefault();
19041                 break;
19042             case 37: // left
19043             case 39: // right
19044                 if (!this.keyboardNavigation) {
19045                     break;
19046                 }
19047                 dir = e.keyCode == 37 ? -1 : 1;
19048                 
19049                 if (e.ctrlKey){
19050                     newDate = this.moveYear(this.date, dir);
19051                     newViewDate = this.moveYear(this.viewDate, dir);
19052                 } else if (e.shiftKey){
19053                     newDate = this.moveMonth(this.date, dir);
19054                     newViewDate = this.moveMonth(this.viewDate, dir);
19055                 } else {
19056                     newDate = new Date(this.date);
19057                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19058                     newViewDate = new Date(this.viewDate);
19059                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19060                 }
19061                 if (this.dateWithinRange(newDate)){
19062                     this.date = newDate;
19063                     this.viewDate = newViewDate;
19064                     this.setValue(this.formatDate(this.date));
19065 //                    this.update();
19066                     e.preventDefault();
19067                     dateChanged = true;
19068                 }
19069                 break;
19070             case 38: // up
19071             case 40: // down
19072                 if (!this.keyboardNavigation) {
19073                     break;
19074                 }
19075                 dir = e.keyCode == 38 ? -1 : 1;
19076                 if (e.ctrlKey){
19077                     newDate = this.moveYear(this.date, dir);
19078                     newViewDate = this.moveYear(this.viewDate, dir);
19079                 } else if (e.shiftKey){
19080                     newDate = this.moveMonth(this.date, dir);
19081                     newViewDate = this.moveMonth(this.viewDate, dir);
19082                 } else {
19083                     newDate = new Date(this.date);
19084                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19085                     newViewDate = new Date(this.viewDate);
19086                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19087                 }
19088                 if (this.dateWithinRange(newDate)){
19089                     this.date = newDate;
19090                     this.viewDate = newViewDate;
19091                     this.setValue(this.formatDate(this.date));
19092 //                    this.update();
19093                     e.preventDefault();
19094                     dateChanged = true;
19095                 }
19096                 break;
19097             case 13: // enter
19098                 this.setValue(this.formatDate(this.date));
19099                 this.hidePopup();
19100                 e.preventDefault();
19101                 break;
19102             case 9: // tab
19103                 this.setValue(this.formatDate(this.date));
19104                 this.hidePopup();
19105                 break;
19106             case 16: // shift
19107             case 17: // ctrl
19108             case 18: // alt
19109                 break;
19110             default :
19111                 this.hide();
19112                 
19113         }
19114     },
19115     
19116     
19117     onClick: function(e) 
19118     {
19119         e.stopPropagation();
19120         e.preventDefault();
19121         
19122         var target = e.getTarget();
19123         
19124         if(target.nodeName.toLowerCase() === 'i'){
19125             target = Roo.get(target).dom.parentNode;
19126         }
19127         
19128         var nodeName = target.nodeName;
19129         var className = target.className;
19130         var html = target.innerHTML;
19131         //Roo.log(nodeName);
19132         
19133         switch(nodeName.toLowerCase()) {
19134             case 'th':
19135                 switch(className) {
19136                     case 'switch':
19137                         this.showMode(1);
19138                         break;
19139                     case 'prev':
19140                     case 'next':
19141                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19142                         switch(this.viewMode){
19143                                 case 0:
19144                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19145                                         break;
19146                                 case 1:
19147                                 case 2:
19148                                         this.viewDate = this.moveYear(this.viewDate, dir);
19149                                         break;
19150                         }
19151                         this.fill();
19152                         break;
19153                     case 'today':
19154                         var date = new Date();
19155                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19156 //                        this.fill()
19157                         this.setValue(this.formatDate(this.date));
19158                         
19159                         this.hidePopup();
19160                         break;
19161                 }
19162                 break;
19163             case 'span':
19164                 if (className.indexOf('disabled') < 0) {
19165                     this.viewDate.setUTCDate(1);
19166                     if (className.indexOf('month') > -1) {
19167                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19168                     } else {
19169                         var year = parseInt(html, 10) || 0;
19170                         this.viewDate.setUTCFullYear(year);
19171                         
19172                     }
19173                     
19174                     if(this.singleMode){
19175                         this.setValue(this.formatDate(this.viewDate));
19176                         this.hidePopup();
19177                         return;
19178                     }
19179                     
19180                     this.showMode(-1);
19181                     this.fill();
19182                 }
19183                 break;
19184                 
19185             case 'td':
19186                 //Roo.log(className);
19187                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19188                     var day = parseInt(html, 10) || 1;
19189                     var year = this.viewDate.getUTCFullYear(),
19190                         month = this.viewDate.getUTCMonth();
19191
19192                     if (className.indexOf('old') > -1) {
19193                         if(month === 0 ){
19194                             month = 11;
19195                             year -= 1;
19196                         }else{
19197                             month -= 1;
19198                         }
19199                     } else if (className.indexOf('new') > -1) {
19200                         if (month == 11) {
19201                             month = 0;
19202                             year += 1;
19203                         } else {
19204                             month += 1;
19205                         }
19206                     }
19207                     //Roo.log([year,month,day]);
19208                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19209                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19210 //                    this.fill();
19211                     //Roo.log(this.formatDate(this.date));
19212                     this.setValue(this.formatDate(this.date));
19213                     this.hidePopup();
19214                 }
19215                 break;
19216         }
19217     },
19218     
19219     setStartDate: function(startDate)
19220     {
19221         this.startDate = startDate || -Infinity;
19222         if (this.startDate !== -Infinity) {
19223             this.startDate = this.parseDate(this.startDate);
19224         }
19225         this.update();
19226         this.updateNavArrows();
19227     },
19228
19229     setEndDate: function(endDate)
19230     {
19231         this.endDate = endDate || Infinity;
19232         if (this.endDate !== Infinity) {
19233             this.endDate = this.parseDate(this.endDate);
19234         }
19235         this.update();
19236         this.updateNavArrows();
19237     },
19238     
19239     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19240     {
19241         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19242         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19243             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19244         }
19245         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19246             return parseInt(d, 10);
19247         });
19248         this.update();
19249         this.updateNavArrows();
19250     },
19251     
19252     updateNavArrows: function() 
19253     {
19254         if(this.singleMode){
19255             return;
19256         }
19257         
19258         var d = new Date(this.viewDate),
19259         year = d.getUTCFullYear(),
19260         month = d.getUTCMonth();
19261         
19262         Roo.each(this.picker().select('.prev', true).elements, function(v){
19263             v.show();
19264             switch (this.viewMode) {
19265                 case 0:
19266
19267                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19268                         v.hide();
19269                     }
19270                     break;
19271                 case 1:
19272                 case 2:
19273                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19274                         v.hide();
19275                     }
19276                     break;
19277             }
19278         });
19279         
19280         Roo.each(this.picker().select('.next', true).elements, function(v){
19281             v.show();
19282             switch (this.viewMode) {
19283                 case 0:
19284
19285                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19286                         v.hide();
19287                     }
19288                     break;
19289                 case 1:
19290                 case 2:
19291                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19292                         v.hide();
19293                     }
19294                     break;
19295             }
19296         })
19297     },
19298     
19299     moveMonth: function(date, dir)
19300     {
19301         if (!dir) {
19302             return date;
19303         }
19304         var new_date = new Date(date.valueOf()),
19305         day = new_date.getUTCDate(),
19306         month = new_date.getUTCMonth(),
19307         mag = Math.abs(dir),
19308         new_month, test;
19309         dir = dir > 0 ? 1 : -1;
19310         if (mag == 1){
19311             test = dir == -1
19312             // If going back one month, make sure month is not current month
19313             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19314             ? function(){
19315                 return new_date.getUTCMonth() == month;
19316             }
19317             // If going forward one month, make sure month is as expected
19318             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19319             : function(){
19320                 return new_date.getUTCMonth() != new_month;
19321             };
19322             new_month = month + dir;
19323             new_date.setUTCMonth(new_month);
19324             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19325             if (new_month < 0 || new_month > 11) {
19326                 new_month = (new_month + 12) % 12;
19327             }
19328         } else {
19329             // For magnitudes >1, move one month at a time...
19330             for (var i=0; i<mag; i++) {
19331                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19332                 new_date = this.moveMonth(new_date, dir);
19333             }
19334             // ...then reset the day, keeping it in the new month
19335             new_month = new_date.getUTCMonth();
19336             new_date.setUTCDate(day);
19337             test = function(){
19338                 return new_month != new_date.getUTCMonth();
19339             };
19340         }
19341         // Common date-resetting loop -- if date is beyond end of month, make it
19342         // end of month
19343         while (test()){
19344             new_date.setUTCDate(--day);
19345             new_date.setUTCMonth(new_month);
19346         }
19347         return new_date;
19348     },
19349
19350     moveYear: function(date, dir)
19351     {
19352         return this.moveMonth(date, dir*12);
19353     },
19354
19355     dateWithinRange: function(date)
19356     {
19357         return date >= this.startDate && date <= this.endDate;
19358     },
19359
19360     
19361     remove: function() 
19362     {
19363         this.picker().remove();
19364     },
19365     
19366     validateValue : function(value)
19367     {
19368         if(this.getVisibilityEl().hasClass('hidden')){
19369             return true;
19370         }
19371         
19372         if(value.length < 1)  {
19373             if(this.allowBlank){
19374                 return true;
19375             }
19376             return false;
19377         }
19378         
19379         if(value.length < this.minLength){
19380             return false;
19381         }
19382         if(value.length > this.maxLength){
19383             return false;
19384         }
19385         if(this.vtype){
19386             var vt = Roo.form.VTypes;
19387             if(!vt[this.vtype](value, this)){
19388                 return false;
19389             }
19390         }
19391         if(typeof this.validator == "function"){
19392             var msg = this.validator(value);
19393             if(msg !== true){
19394                 return false;
19395             }
19396         }
19397         
19398         if(this.regex && !this.regex.test(value)){
19399             return false;
19400         }
19401         
19402         if(typeof(this.parseDate(value)) == 'undefined'){
19403             return false;
19404         }
19405         
19406         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19407             return false;
19408         }      
19409         
19410         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19411             return false;
19412         } 
19413         
19414         
19415         return true;
19416     },
19417     
19418     reset : function()
19419     {
19420         this.date = this.viewDate = '';
19421         
19422         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19423     }
19424    
19425 });
19426
19427 Roo.apply(Roo.bootstrap.DateField,  {
19428     
19429     head : {
19430         tag: 'thead',
19431         cn: [
19432         {
19433             tag: 'tr',
19434             cn: [
19435             {
19436                 tag: 'th',
19437                 cls: 'prev',
19438                 html: '<i class="fa fa-arrow-left"/>'
19439             },
19440             {
19441                 tag: 'th',
19442                 cls: 'switch',
19443                 colspan: '5'
19444             },
19445             {
19446                 tag: 'th',
19447                 cls: 'next',
19448                 html: '<i class="fa fa-arrow-right"/>'
19449             }
19450
19451             ]
19452         }
19453         ]
19454     },
19455     
19456     content : {
19457         tag: 'tbody',
19458         cn: [
19459         {
19460             tag: 'tr',
19461             cn: [
19462             {
19463                 tag: 'td',
19464                 colspan: '7'
19465             }
19466             ]
19467         }
19468         ]
19469     },
19470     
19471     footer : {
19472         tag: 'tfoot',
19473         cn: [
19474         {
19475             tag: 'tr',
19476             cn: [
19477             {
19478                 tag: 'th',
19479                 colspan: '7',
19480                 cls: 'today'
19481             }
19482                     
19483             ]
19484         }
19485         ]
19486     },
19487     
19488     dates:{
19489         en: {
19490             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19491             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19492             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19493             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19494             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19495             today: "Today"
19496         }
19497     },
19498     
19499     modes: [
19500     {
19501         clsName: 'days',
19502         navFnc: 'Month',
19503         navStep: 1
19504     },
19505     {
19506         clsName: 'months',
19507         navFnc: 'FullYear',
19508         navStep: 1
19509     },
19510     {
19511         clsName: 'years',
19512         navFnc: 'FullYear',
19513         navStep: 10
19514     }]
19515 });
19516
19517 Roo.apply(Roo.bootstrap.DateField,  {
19518   
19519     template : {
19520         tag: 'div',
19521         cls: 'datepicker dropdown-menu roo-dynamic',
19522         cn: [
19523         {
19524             tag: 'div',
19525             cls: 'datepicker-days',
19526             cn: [
19527             {
19528                 tag: 'table',
19529                 cls: 'table-condensed',
19530                 cn:[
19531                 Roo.bootstrap.DateField.head,
19532                 {
19533                     tag: 'tbody'
19534                 },
19535                 Roo.bootstrap.DateField.footer
19536                 ]
19537             }
19538             ]
19539         },
19540         {
19541             tag: 'div',
19542             cls: 'datepicker-months',
19543             cn: [
19544             {
19545                 tag: 'table',
19546                 cls: 'table-condensed',
19547                 cn:[
19548                 Roo.bootstrap.DateField.head,
19549                 Roo.bootstrap.DateField.content,
19550                 Roo.bootstrap.DateField.footer
19551                 ]
19552             }
19553             ]
19554         },
19555         {
19556             tag: 'div',
19557             cls: 'datepicker-years',
19558             cn: [
19559             {
19560                 tag: 'table',
19561                 cls: 'table-condensed',
19562                 cn:[
19563                 Roo.bootstrap.DateField.head,
19564                 Roo.bootstrap.DateField.content,
19565                 Roo.bootstrap.DateField.footer
19566                 ]
19567             }
19568             ]
19569         }
19570         ]
19571     }
19572 });
19573
19574  
19575
19576  /*
19577  * - LGPL
19578  *
19579  * TimeField
19580  * 
19581  */
19582
19583 /**
19584  * @class Roo.bootstrap.TimeField
19585  * @extends Roo.bootstrap.Input
19586  * Bootstrap DateField class
19587  * 
19588  * 
19589  * @constructor
19590  * Create a new TimeField
19591  * @param {Object} config The config object
19592  */
19593
19594 Roo.bootstrap.TimeField = function(config){
19595     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19596     this.addEvents({
19597             /**
19598              * @event show
19599              * Fires when this field show.
19600              * @param {Roo.bootstrap.DateField} thisthis
19601              * @param {Mixed} date The date value
19602              */
19603             show : true,
19604             /**
19605              * @event show
19606              * Fires when this field hide.
19607              * @param {Roo.bootstrap.DateField} this
19608              * @param {Mixed} date The date value
19609              */
19610             hide : true,
19611             /**
19612              * @event select
19613              * Fires when select a date.
19614              * @param {Roo.bootstrap.DateField} this
19615              * @param {Mixed} date The date value
19616              */
19617             select : true
19618         });
19619 };
19620
19621 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19622     
19623     /**
19624      * @cfg {String} format
19625      * The default time format string which can be overriden for localization support.  The format must be
19626      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19627      */
19628     format : "H:i",
19629        
19630     onRender: function(ct, position)
19631     {
19632         
19633         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19634                 
19635         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19636         
19637         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19638         
19639         this.pop = this.picker().select('>.datepicker-time',true).first();
19640         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19641         
19642         this.picker().on('mousedown', this.onMousedown, this);
19643         this.picker().on('click', this.onClick, this);
19644         
19645         this.picker().addClass('datepicker-dropdown');
19646     
19647         this.fillTime();
19648         this.update();
19649             
19650         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19651         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19652         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19653         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19654         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19655         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19656
19657     },
19658     
19659     fireKey: function(e){
19660         if (!this.picker().isVisible()){
19661             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19662                 this.show();
19663             }
19664             return;
19665         }
19666
19667         e.preventDefault();
19668         
19669         switch(e.keyCode){
19670             case 27: // escape
19671                 this.hide();
19672                 break;
19673             case 37: // left
19674             case 39: // right
19675                 this.onTogglePeriod();
19676                 break;
19677             case 38: // up
19678                 this.onIncrementMinutes();
19679                 break;
19680             case 40: // down
19681                 this.onDecrementMinutes();
19682                 break;
19683             case 13: // enter
19684             case 9: // tab
19685                 this.setTime();
19686                 break;
19687         }
19688     },
19689     
19690     onClick: function(e) {
19691         e.stopPropagation();
19692         e.preventDefault();
19693     },
19694     
19695     picker : function()
19696     {
19697         return this.el.select('.datepicker', true).first();
19698     },
19699     
19700     fillTime: function()
19701     {    
19702         var time = this.pop.select('tbody', true).first();
19703         
19704         time.dom.innerHTML = '';
19705         
19706         time.createChild({
19707             tag: 'tr',
19708             cn: [
19709                 {
19710                     tag: 'td',
19711                     cn: [
19712                         {
19713                             tag: 'a',
19714                             href: '#',
19715                             cls: 'btn',
19716                             cn: [
19717                                 {
19718                                     tag: 'span',
19719                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19720                                 }
19721                             ]
19722                         } 
19723                     ]
19724                 },
19725                 {
19726                     tag: 'td',
19727                     cls: 'separator'
19728                 },
19729                 {
19730                     tag: 'td',
19731                     cn: [
19732                         {
19733                             tag: 'a',
19734                             href: '#',
19735                             cls: 'btn',
19736                             cn: [
19737                                 {
19738                                     tag: 'span',
19739                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19740                                 }
19741                             ]
19742                         }
19743                     ]
19744                 },
19745                 {
19746                     tag: 'td',
19747                     cls: 'separator'
19748                 }
19749             ]
19750         });
19751         
19752         time.createChild({
19753             tag: 'tr',
19754             cn: [
19755                 {
19756                     tag: 'td',
19757                     cn: [
19758                         {
19759                             tag: 'span',
19760                             cls: 'timepicker-hour',
19761                             html: '00'
19762                         }  
19763                     ]
19764                 },
19765                 {
19766                     tag: 'td',
19767                     cls: 'separator',
19768                     html: ':'
19769                 },
19770                 {
19771                     tag: 'td',
19772                     cn: [
19773                         {
19774                             tag: 'span',
19775                             cls: 'timepicker-minute',
19776                             html: '00'
19777                         }  
19778                     ]
19779                 },
19780                 {
19781                     tag: 'td',
19782                     cls: 'separator'
19783                 },
19784                 {
19785                     tag: 'td',
19786                     cn: [
19787                         {
19788                             tag: 'button',
19789                             type: 'button',
19790                             cls: 'btn btn-primary period',
19791                             html: 'AM'
19792                             
19793                         }
19794                     ]
19795                 }
19796             ]
19797         });
19798         
19799         time.createChild({
19800             tag: 'tr',
19801             cn: [
19802                 {
19803                     tag: 'td',
19804                     cn: [
19805                         {
19806                             tag: 'a',
19807                             href: '#',
19808                             cls: 'btn',
19809                             cn: [
19810                                 {
19811                                     tag: 'span',
19812                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19813                                 }
19814                             ]
19815                         }
19816                     ]
19817                 },
19818                 {
19819                     tag: 'td',
19820                     cls: 'separator'
19821                 },
19822                 {
19823                     tag: 'td',
19824                     cn: [
19825                         {
19826                             tag: 'a',
19827                             href: '#',
19828                             cls: 'btn',
19829                             cn: [
19830                                 {
19831                                     tag: 'span',
19832                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19833                                 }
19834                             ]
19835                         }
19836                     ]
19837                 },
19838                 {
19839                     tag: 'td',
19840                     cls: 'separator'
19841                 }
19842             ]
19843         });
19844         
19845     },
19846     
19847     update: function()
19848     {
19849         
19850         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19851         
19852         this.fill();
19853     },
19854     
19855     fill: function() 
19856     {
19857         var hours = this.time.getHours();
19858         var minutes = this.time.getMinutes();
19859         var period = 'AM';
19860         
19861         if(hours > 11){
19862             period = 'PM';
19863         }
19864         
19865         if(hours == 0){
19866             hours = 12;
19867         }
19868         
19869         
19870         if(hours > 12){
19871             hours = hours - 12;
19872         }
19873         
19874         if(hours < 10){
19875             hours = '0' + hours;
19876         }
19877         
19878         if(minutes < 10){
19879             minutes = '0' + minutes;
19880         }
19881         
19882         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19883         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19884         this.pop.select('button', true).first().dom.innerHTML = period;
19885         
19886     },
19887     
19888     place: function()
19889     {   
19890         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19891         
19892         var cls = ['bottom'];
19893         
19894         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19895             cls.pop();
19896             cls.push('top');
19897         }
19898         
19899         cls.push('right');
19900         
19901         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19902             cls.pop();
19903             cls.push('left');
19904         }
19905         
19906         this.picker().addClass(cls.join('-'));
19907         
19908         var _this = this;
19909         
19910         Roo.each(cls, function(c){
19911             if(c == 'bottom'){
19912                 _this.picker().setTop(_this.inputEl().getHeight());
19913                 return;
19914             }
19915             if(c == 'top'){
19916                 _this.picker().setTop(0 - _this.picker().getHeight());
19917                 return;
19918             }
19919             
19920             if(c == 'left'){
19921                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19922                 return;
19923             }
19924             if(c == 'right'){
19925                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19926                 return;
19927             }
19928         });
19929         
19930     },
19931   
19932     onFocus : function()
19933     {
19934         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19935         this.show();
19936     },
19937     
19938     onBlur : function()
19939     {
19940         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19941         this.hide();
19942     },
19943     
19944     show : function()
19945     {
19946         this.picker().show();
19947         this.pop.show();
19948         this.update();
19949         this.place();
19950         
19951         this.fireEvent('show', this, this.date);
19952     },
19953     
19954     hide : function()
19955     {
19956         this.picker().hide();
19957         this.pop.hide();
19958         
19959         this.fireEvent('hide', this, this.date);
19960     },
19961     
19962     setTime : function()
19963     {
19964         this.hide();
19965         this.setValue(this.time.format(this.format));
19966         
19967         this.fireEvent('select', this, this.date);
19968         
19969         
19970     },
19971     
19972     onMousedown: function(e){
19973         e.stopPropagation();
19974         e.preventDefault();
19975     },
19976     
19977     onIncrementHours: function()
19978     {
19979         Roo.log('onIncrementHours');
19980         this.time = this.time.add(Date.HOUR, 1);
19981         this.update();
19982         
19983     },
19984     
19985     onDecrementHours: function()
19986     {
19987         Roo.log('onDecrementHours');
19988         this.time = this.time.add(Date.HOUR, -1);
19989         this.update();
19990     },
19991     
19992     onIncrementMinutes: function()
19993     {
19994         Roo.log('onIncrementMinutes');
19995         this.time = this.time.add(Date.MINUTE, 1);
19996         this.update();
19997     },
19998     
19999     onDecrementMinutes: function()
20000     {
20001         Roo.log('onDecrementMinutes');
20002         this.time = this.time.add(Date.MINUTE, -1);
20003         this.update();
20004     },
20005     
20006     onTogglePeriod: function()
20007     {
20008         Roo.log('onTogglePeriod');
20009         this.time = this.time.add(Date.HOUR, 12);
20010         this.update();
20011     }
20012     
20013    
20014 });
20015
20016 Roo.apply(Roo.bootstrap.TimeField,  {
20017     
20018     content : {
20019         tag: 'tbody',
20020         cn: [
20021             {
20022                 tag: 'tr',
20023                 cn: [
20024                 {
20025                     tag: 'td',
20026                     colspan: '7'
20027                 }
20028                 ]
20029             }
20030         ]
20031     },
20032     
20033     footer : {
20034         tag: 'tfoot',
20035         cn: [
20036             {
20037                 tag: 'tr',
20038                 cn: [
20039                 {
20040                     tag: 'th',
20041                     colspan: '7',
20042                     cls: '',
20043                     cn: [
20044                         {
20045                             tag: 'button',
20046                             cls: 'btn btn-info ok',
20047                             html: 'OK'
20048                         }
20049                     ]
20050                 }
20051
20052                 ]
20053             }
20054         ]
20055     }
20056 });
20057
20058 Roo.apply(Roo.bootstrap.TimeField,  {
20059   
20060     template : {
20061         tag: 'div',
20062         cls: 'datepicker dropdown-menu',
20063         cn: [
20064             {
20065                 tag: 'div',
20066                 cls: 'datepicker-time',
20067                 cn: [
20068                 {
20069                     tag: 'table',
20070                     cls: 'table-condensed',
20071                     cn:[
20072                     Roo.bootstrap.TimeField.content,
20073                     Roo.bootstrap.TimeField.footer
20074                     ]
20075                 }
20076                 ]
20077             }
20078         ]
20079     }
20080 });
20081
20082  
20083
20084  /*
20085  * - LGPL
20086  *
20087  * MonthField
20088  * 
20089  */
20090
20091 /**
20092  * @class Roo.bootstrap.MonthField
20093  * @extends Roo.bootstrap.Input
20094  * Bootstrap MonthField class
20095  * 
20096  * @cfg {String} language default en
20097  * 
20098  * @constructor
20099  * Create a new MonthField
20100  * @param {Object} config The config object
20101  */
20102
20103 Roo.bootstrap.MonthField = function(config){
20104     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20105     
20106     this.addEvents({
20107         /**
20108          * @event show
20109          * Fires when this field show.
20110          * @param {Roo.bootstrap.MonthField} this
20111          * @param {Mixed} date The date value
20112          */
20113         show : true,
20114         /**
20115          * @event show
20116          * Fires when this field hide.
20117          * @param {Roo.bootstrap.MonthField} this
20118          * @param {Mixed} date The date value
20119          */
20120         hide : true,
20121         /**
20122          * @event select
20123          * Fires when select a date.
20124          * @param {Roo.bootstrap.MonthField} this
20125          * @param {String} oldvalue The old value
20126          * @param {String} newvalue The new value
20127          */
20128         select : true
20129     });
20130 };
20131
20132 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20133     
20134     onRender: function(ct, position)
20135     {
20136         
20137         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20138         
20139         this.language = this.language || 'en';
20140         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20141         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20142         
20143         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20144         this.isInline = false;
20145         this.isInput = true;
20146         this.component = this.el.select('.add-on', true).first() || false;
20147         this.component = (this.component && this.component.length === 0) ? false : this.component;
20148         this.hasInput = this.component && this.inputEL().length;
20149         
20150         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20151         
20152         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20153         
20154         this.picker().on('mousedown', this.onMousedown, this);
20155         this.picker().on('click', this.onClick, this);
20156         
20157         this.picker().addClass('datepicker-dropdown');
20158         
20159         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20160             v.setStyle('width', '189px');
20161         });
20162         
20163         this.fillMonths();
20164         
20165         this.update();
20166         
20167         if(this.isInline) {
20168             this.show();
20169         }
20170         
20171     },
20172     
20173     setValue: function(v, suppressEvent)
20174     {   
20175         var o = this.getValue();
20176         
20177         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20178         
20179         this.update();
20180
20181         if(suppressEvent !== true){
20182             this.fireEvent('select', this, o, v);
20183         }
20184         
20185     },
20186     
20187     getValue: function()
20188     {
20189         return this.value;
20190     },
20191     
20192     onClick: function(e) 
20193     {
20194         e.stopPropagation();
20195         e.preventDefault();
20196         
20197         var target = e.getTarget();
20198         
20199         if(target.nodeName.toLowerCase() === 'i'){
20200             target = Roo.get(target).dom.parentNode;
20201         }
20202         
20203         var nodeName = target.nodeName;
20204         var className = target.className;
20205         var html = target.innerHTML;
20206         
20207         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20208             return;
20209         }
20210         
20211         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20212         
20213         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20214         
20215         this.hide();
20216                         
20217     },
20218     
20219     picker : function()
20220     {
20221         return this.pickerEl;
20222     },
20223     
20224     fillMonths: function()
20225     {    
20226         var i = 0;
20227         var months = this.picker().select('>.datepicker-months td', true).first();
20228         
20229         months.dom.innerHTML = '';
20230         
20231         while (i < 12) {
20232             var month = {
20233                 tag: 'span',
20234                 cls: 'month',
20235                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20236             };
20237             
20238             months.createChild(month);
20239         }
20240         
20241     },
20242     
20243     update: function()
20244     {
20245         var _this = this;
20246         
20247         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20248             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20249         }
20250         
20251         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20252             e.removeClass('active');
20253             
20254             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20255                 e.addClass('active');
20256             }
20257         })
20258     },
20259     
20260     place: function()
20261     {
20262         if(this.isInline) {
20263             return;
20264         }
20265         
20266         this.picker().removeClass(['bottom', 'top']);
20267         
20268         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20269             /*
20270              * place to the top of element!
20271              *
20272              */
20273             
20274             this.picker().addClass('top');
20275             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20276             
20277             return;
20278         }
20279         
20280         this.picker().addClass('bottom');
20281         
20282         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20283     },
20284     
20285     onFocus : function()
20286     {
20287         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20288         this.show();
20289     },
20290     
20291     onBlur : function()
20292     {
20293         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20294         
20295         var d = this.inputEl().getValue();
20296         
20297         this.setValue(d);
20298                 
20299         this.hide();
20300     },
20301     
20302     show : function()
20303     {
20304         this.picker().show();
20305         this.picker().select('>.datepicker-months', true).first().show();
20306         this.update();
20307         this.place();
20308         
20309         this.fireEvent('show', this, this.date);
20310     },
20311     
20312     hide : function()
20313     {
20314         if(this.isInline) {
20315             return;
20316         }
20317         this.picker().hide();
20318         this.fireEvent('hide', this, this.date);
20319         
20320     },
20321     
20322     onMousedown: function(e)
20323     {
20324         e.stopPropagation();
20325         e.preventDefault();
20326     },
20327     
20328     keyup: function(e)
20329     {
20330         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20331         this.update();
20332     },
20333
20334     fireKey: function(e)
20335     {
20336         if (!this.picker().isVisible()){
20337             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20338                 this.show();
20339             }
20340             return;
20341         }
20342         
20343         var dir;
20344         
20345         switch(e.keyCode){
20346             case 27: // escape
20347                 this.hide();
20348                 e.preventDefault();
20349                 break;
20350             case 37: // left
20351             case 39: // right
20352                 dir = e.keyCode == 37 ? -1 : 1;
20353                 
20354                 this.vIndex = this.vIndex + dir;
20355                 
20356                 if(this.vIndex < 0){
20357                     this.vIndex = 0;
20358                 }
20359                 
20360                 if(this.vIndex > 11){
20361                     this.vIndex = 11;
20362                 }
20363                 
20364                 if(isNaN(this.vIndex)){
20365                     this.vIndex = 0;
20366                 }
20367                 
20368                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20369                 
20370                 break;
20371             case 38: // up
20372             case 40: // down
20373                 
20374                 dir = e.keyCode == 38 ? -1 : 1;
20375                 
20376                 this.vIndex = this.vIndex + dir * 4;
20377                 
20378                 if(this.vIndex < 0){
20379                     this.vIndex = 0;
20380                 }
20381                 
20382                 if(this.vIndex > 11){
20383                     this.vIndex = 11;
20384                 }
20385                 
20386                 if(isNaN(this.vIndex)){
20387                     this.vIndex = 0;
20388                 }
20389                 
20390                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20391                 break;
20392                 
20393             case 13: // enter
20394                 
20395                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20396                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20397                 }
20398                 
20399                 this.hide();
20400                 e.preventDefault();
20401                 break;
20402             case 9: // tab
20403                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20404                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20405                 }
20406                 this.hide();
20407                 break;
20408             case 16: // shift
20409             case 17: // ctrl
20410             case 18: // alt
20411                 break;
20412             default :
20413                 this.hide();
20414                 
20415         }
20416     },
20417     
20418     remove: function() 
20419     {
20420         this.picker().remove();
20421     }
20422    
20423 });
20424
20425 Roo.apply(Roo.bootstrap.MonthField,  {
20426     
20427     content : {
20428         tag: 'tbody',
20429         cn: [
20430         {
20431             tag: 'tr',
20432             cn: [
20433             {
20434                 tag: 'td',
20435                 colspan: '7'
20436             }
20437             ]
20438         }
20439         ]
20440     },
20441     
20442     dates:{
20443         en: {
20444             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20445             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20446         }
20447     }
20448 });
20449
20450 Roo.apply(Roo.bootstrap.MonthField,  {
20451   
20452     template : {
20453         tag: 'div',
20454         cls: 'datepicker dropdown-menu roo-dynamic',
20455         cn: [
20456             {
20457                 tag: 'div',
20458                 cls: 'datepicker-months',
20459                 cn: [
20460                 {
20461                     tag: 'table',
20462                     cls: 'table-condensed',
20463                     cn:[
20464                         Roo.bootstrap.DateField.content
20465                     ]
20466                 }
20467                 ]
20468             }
20469         ]
20470     }
20471 });
20472
20473  
20474
20475  
20476  /*
20477  * - LGPL
20478  *
20479  * CheckBox
20480  * 
20481  */
20482
20483 /**
20484  * @class Roo.bootstrap.CheckBox
20485  * @extends Roo.bootstrap.Input
20486  * Bootstrap CheckBox class
20487  * 
20488  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20489  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20490  * @cfg {String} boxLabel The text that appears beside the checkbox
20491  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20492  * @cfg {Boolean} checked initnal the element
20493  * @cfg {Boolean} inline inline the element (default false)
20494  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20495  * @cfg {String} tooltip label tooltip
20496  * 
20497  * @constructor
20498  * Create a new CheckBox
20499  * @param {Object} config The config object
20500  */
20501
20502 Roo.bootstrap.CheckBox = function(config){
20503     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20504    
20505     this.addEvents({
20506         /**
20507         * @event check
20508         * Fires when the element is checked or unchecked.
20509         * @param {Roo.bootstrap.CheckBox} this This input
20510         * @param {Boolean} checked The new checked value
20511         */
20512        check : true,
20513        /**
20514         * @event click
20515         * Fires when the element is click.
20516         * @param {Roo.bootstrap.CheckBox} this This input
20517         */
20518        click : true
20519     });
20520     
20521 };
20522
20523 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20524   
20525     inputType: 'checkbox',
20526     inputValue: 1,
20527     valueOff: 0,
20528     boxLabel: false,
20529     checked: false,
20530     weight : false,
20531     inline: false,
20532     tooltip : '',
20533     
20534     getAutoCreate : function()
20535     {
20536         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20537         
20538         var id = Roo.id();
20539         
20540         var cfg = {};
20541         
20542         cfg.cls = 'form-group ' + this.inputType; //input-group
20543         
20544         if(this.inline){
20545             cfg.cls += ' ' + this.inputType + '-inline';
20546         }
20547         
20548         var input =  {
20549             tag: 'input',
20550             id : id,
20551             type : this.inputType,
20552             value : this.inputValue,
20553             cls : 'roo-' + this.inputType, //'form-box',
20554             placeholder : this.placeholder || ''
20555             
20556         };
20557         
20558         if(this.inputType != 'radio'){
20559             var hidden =  {
20560                 tag: 'input',
20561                 type : 'hidden',
20562                 cls : 'roo-hidden-value',
20563                 value : this.checked ? this.inputValue : this.valueOff
20564             };
20565         }
20566         
20567             
20568         if (this.weight) { // Validity check?
20569             cfg.cls += " " + this.inputType + "-" + this.weight;
20570         }
20571         
20572         if (this.disabled) {
20573             input.disabled=true;
20574         }
20575         
20576         if(this.checked){
20577             input.checked = this.checked;
20578         }
20579         
20580         if (this.name) {
20581             
20582             input.name = this.name;
20583             
20584             if(this.inputType != 'radio'){
20585                 hidden.name = this.name;
20586                 input.name = '_hidden_' + this.name;
20587             }
20588         }
20589         
20590         if (this.size) {
20591             input.cls += ' input-' + this.size;
20592         }
20593         
20594         var settings=this;
20595         
20596         ['xs','sm','md','lg'].map(function(size){
20597             if (settings[size]) {
20598                 cfg.cls += ' col-' + size + '-' + settings[size];
20599             }
20600         });
20601         
20602         var inputblock = input;
20603          
20604         if (this.before || this.after) {
20605             
20606             inputblock = {
20607                 cls : 'input-group',
20608                 cn :  [] 
20609             };
20610             
20611             if (this.before) {
20612                 inputblock.cn.push({
20613                     tag :'span',
20614                     cls : 'input-group-addon',
20615                     html : this.before
20616                 });
20617             }
20618             
20619             inputblock.cn.push(input);
20620             
20621             if(this.inputType != 'radio'){
20622                 inputblock.cn.push(hidden);
20623             }
20624             
20625             if (this.after) {
20626                 inputblock.cn.push({
20627                     tag :'span',
20628                     cls : 'input-group-addon',
20629                     html : this.after
20630                 });
20631             }
20632             
20633         }
20634         
20635         if (align ==='left' && this.fieldLabel.length) {
20636 //                Roo.log("left and has label");
20637             cfg.cn = [
20638                 {
20639                     tag: 'label',
20640                     'for' :  id,
20641                     cls : 'control-label',
20642                     html : this.fieldLabel
20643                 },
20644                 {
20645                     cls : "", 
20646                     cn: [
20647                         inputblock
20648                     ]
20649                 }
20650             ];
20651             
20652             if(this.labelWidth > 12){
20653                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20654             }
20655             
20656             if(this.labelWidth < 13 && this.labelmd == 0){
20657                 this.labelmd = this.labelWidth;
20658             }
20659             
20660             if(this.labellg > 0){
20661                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20662                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20663             }
20664             
20665             if(this.labelmd > 0){
20666                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20667                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20668             }
20669             
20670             if(this.labelsm > 0){
20671                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20672                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20673             }
20674             
20675             if(this.labelxs > 0){
20676                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20677                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20678             }
20679             
20680         } else if ( this.fieldLabel.length) {
20681 //                Roo.log(" label");
20682                 cfg.cn = [
20683                    
20684                     {
20685                         tag: this.boxLabel ? 'span' : 'label',
20686                         'for': id,
20687                         cls: 'control-label box-input-label',
20688                         //cls : 'input-group-addon',
20689                         html : this.fieldLabel
20690                     },
20691                     
20692                     inputblock
20693                     
20694                 ];
20695
20696         } else {
20697             
20698 //                Roo.log(" no label && no align");
20699                 cfg.cn = [  inputblock ] ;
20700                 
20701                 
20702         }
20703         
20704         if(this.boxLabel){
20705              var boxLabelCfg = {
20706                 tag: 'label',
20707                 //'for': id, // box label is handled by onclick - so no for...
20708                 cls: 'box-label',
20709                 html: this.boxLabel
20710             };
20711             
20712             if(this.tooltip){
20713                 boxLabelCfg.tooltip = this.tooltip;
20714             }
20715              
20716             cfg.cn.push(boxLabelCfg);
20717         }
20718         
20719         if(this.inputType != 'radio'){
20720             cfg.cn.push(hidden);
20721         }
20722         
20723         return cfg;
20724         
20725     },
20726     
20727     /**
20728      * return the real input element.
20729      */
20730     inputEl: function ()
20731     {
20732         return this.el.select('input.roo-' + this.inputType,true).first();
20733     },
20734     hiddenEl: function ()
20735     {
20736         return this.el.select('input.roo-hidden-value',true).first();
20737     },
20738     
20739     labelEl: function()
20740     {
20741         return this.el.select('label.control-label',true).first();
20742     },
20743     /* depricated... */
20744     
20745     label: function()
20746     {
20747         return this.labelEl();
20748     },
20749     
20750     boxLabelEl: function()
20751     {
20752         return this.el.select('label.box-label',true).first();
20753     },
20754     
20755     initEvents : function()
20756     {
20757 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20758         
20759         this.inputEl().on('click', this.onClick,  this);
20760         
20761         if (this.boxLabel) { 
20762             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20763         }
20764         
20765         this.startValue = this.getValue();
20766         
20767         if(this.groupId){
20768             Roo.bootstrap.CheckBox.register(this);
20769         }
20770     },
20771     
20772     onClick : function(e)
20773     {   
20774         if(this.fireEvent('click', this, e) !== false){
20775             this.setChecked(!this.checked);
20776         }
20777         
20778     },
20779     
20780     setChecked : function(state,suppressEvent)
20781     {
20782         this.startValue = this.getValue();
20783
20784         if(this.inputType == 'radio'){
20785             
20786             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20787                 e.dom.checked = false;
20788             });
20789             
20790             this.inputEl().dom.checked = true;
20791             
20792             this.inputEl().dom.value = this.inputValue;
20793             
20794             if(suppressEvent !== true){
20795                 this.fireEvent('check', this, true);
20796             }
20797             
20798             this.validate();
20799             
20800             return;
20801         }
20802         
20803         this.checked = state;
20804         
20805         this.inputEl().dom.checked = state;
20806         
20807         
20808         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20809         
20810         if(suppressEvent !== true){
20811             this.fireEvent('check', this, state);
20812         }
20813         
20814         this.validate();
20815     },
20816     
20817     getValue : function()
20818     {
20819         if(this.inputType == 'radio'){
20820             return this.getGroupValue();
20821         }
20822         
20823         return this.hiddenEl().dom.value;
20824         
20825     },
20826     
20827     getGroupValue : function()
20828     {
20829         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20830             return '';
20831         }
20832         
20833         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20834     },
20835     
20836     setValue : function(v,suppressEvent)
20837     {
20838         if(this.inputType == 'radio'){
20839             this.setGroupValue(v, suppressEvent);
20840             return;
20841         }
20842         
20843         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20844         
20845         this.validate();
20846     },
20847     
20848     setGroupValue : function(v, suppressEvent)
20849     {
20850         this.startValue = this.getValue();
20851         
20852         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20853             e.dom.checked = false;
20854             
20855             if(e.dom.value == v){
20856                 e.dom.checked = true;
20857             }
20858         });
20859         
20860         if(suppressEvent !== true){
20861             this.fireEvent('check', this, true);
20862         }
20863
20864         this.validate();
20865         
20866         return;
20867     },
20868     
20869     validate : function()
20870     {
20871         if(this.getVisibilityEl().hasClass('hidden')){
20872             return true;
20873         }
20874         
20875         if(
20876                 this.disabled || 
20877                 (this.inputType == 'radio' && this.validateRadio()) ||
20878                 (this.inputType == 'checkbox' && this.validateCheckbox())
20879         ){
20880             this.markValid();
20881             return true;
20882         }
20883         
20884         this.markInvalid();
20885         return false;
20886     },
20887     
20888     validateRadio : function()
20889     {
20890         if(this.getVisibilityEl().hasClass('hidden')){
20891             return true;
20892         }
20893         
20894         if(this.allowBlank){
20895             return true;
20896         }
20897         
20898         var valid = false;
20899         
20900         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20901             if(!e.dom.checked){
20902                 return;
20903             }
20904             
20905             valid = true;
20906             
20907             return false;
20908         });
20909         
20910         return valid;
20911     },
20912     
20913     validateCheckbox : function()
20914     {
20915         if(!this.groupId){
20916             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20917             //return (this.getValue() == this.inputValue) ? true : false;
20918         }
20919         
20920         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20921         
20922         if(!group){
20923             return false;
20924         }
20925         
20926         var r = false;
20927         
20928         for(var i in group){
20929             if(group[i].el.isVisible(true)){
20930                 r = false;
20931                 break;
20932             }
20933             
20934             r = true;
20935         }
20936         
20937         for(var i in group){
20938             if(r){
20939                 break;
20940             }
20941             
20942             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20943         }
20944         
20945         return r;
20946     },
20947     
20948     /**
20949      * Mark this field as valid
20950      */
20951     markValid : function()
20952     {
20953         var _this = this;
20954         
20955         this.fireEvent('valid', this);
20956         
20957         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20958         
20959         if(this.groupId){
20960             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20961         }
20962         
20963         if(label){
20964             label.markValid();
20965         }
20966
20967         if(this.inputType == 'radio'){
20968             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20969                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20970                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20971             });
20972             
20973             return;
20974         }
20975
20976         if(!this.groupId){
20977             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20978             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20979             return;
20980         }
20981         
20982         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20983         
20984         if(!group){
20985             return;
20986         }
20987         
20988         for(var i in group){
20989             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20990             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20991         }
20992     },
20993     
20994      /**
20995      * Mark this field as invalid
20996      * @param {String} msg The validation message
20997      */
20998     markInvalid : function(msg)
20999     {
21000         if(this.allowBlank){
21001             return;
21002         }
21003         
21004         var _this = this;
21005         
21006         this.fireEvent('invalid', this, msg);
21007         
21008         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21009         
21010         if(this.groupId){
21011             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21012         }
21013         
21014         if(label){
21015             label.markInvalid();
21016         }
21017             
21018         if(this.inputType == 'radio'){
21019             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21020                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21021                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21022             });
21023             
21024             return;
21025         }
21026         
21027         if(!this.groupId){
21028             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21029             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21030             return;
21031         }
21032         
21033         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21034         
21035         if(!group){
21036             return;
21037         }
21038         
21039         for(var i in group){
21040             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21041             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21042         }
21043         
21044     },
21045     
21046     clearInvalid : function()
21047     {
21048         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21049         
21050         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21051         
21052         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21053         
21054         if (label && label.iconEl) {
21055             label.iconEl.removeClass(label.validClass);
21056             label.iconEl.removeClass(label.invalidClass);
21057         }
21058     },
21059     
21060     disable : function()
21061     {
21062         if(this.inputType != 'radio'){
21063             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21064             return;
21065         }
21066         
21067         var _this = this;
21068         
21069         if(this.rendered){
21070             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21071                 _this.getActionEl().addClass(this.disabledClass);
21072                 e.dom.disabled = true;
21073             });
21074         }
21075         
21076         this.disabled = true;
21077         this.fireEvent("disable", this);
21078         return this;
21079     },
21080
21081     enable : function()
21082     {
21083         if(this.inputType != 'radio'){
21084             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21085             return;
21086         }
21087         
21088         var _this = this;
21089         
21090         if(this.rendered){
21091             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21092                 _this.getActionEl().removeClass(this.disabledClass);
21093                 e.dom.disabled = false;
21094             });
21095         }
21096         
21097         this.disabled = false;
21098         this.fireEvent("enable", this);
21099         return this;
21100     },
21101     
21102     setBoxLabel : function(v)
21103     {
21104         this.boxLabel = v;
21105         
21106         if(this.rendered){
21107             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21108         }
21109     }
21110
21111 });
21112
21113 Roo.apply(Roo.bootstrap.CheckBox, {
21114     
21115     groups: {},
21116     
21117      /**
21118     * register a CheckBox Group
21119     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21120     */
21121     register : function(checkbox)
21122     {
21123         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21124             this.groups[checkbox.groupId] = {};
21125         }
21126         
21127         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21128             return;
21129         }
21130         
21131         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21132         
21133     },
21134     /**
21135     * fetch a CheckBox Group based on the group ID
21136     * @param {string} the group ID
21137     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21138     */
21139     get: function(groupId) {
21140         if (typeof(this.groups[groupId]) == 'undefined') {
21141             return false;
21142         }
21143         
21144         return this.groups[groupId] ;
21145     }
21146     
21147     
21148 });
21149 /*
21150  * - LGPL
21151  *
21152  * RadioItem
21153  * 
21154  */
21155
21156 /**
21157  * @class Roo.bootstrap.Radio
21158  * @extends Roo.bootstrap.Component
21159  * Bootstrap Radio class
21160  * @cfg {String} boxLabel - the label associated
21161  * @cfg {String} value - the value of radio
21162  * 
21163  * @constructor
21164  * Create a new Radio
21165  * @param {Object} config The config object
21166  */
21167 Roo.bootstrap.Radio = function(config){
21168     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21169     
21170 };
21171
21172 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21173     
21174     boxLabel : '',
21175     
21176     value : '',
21177     
21178     getAutoCreate : function()
21179     {
21180         var cfg = {
21181             tag : 'div',
21182             cls : 'form-group radio',
21183             cn : [
21184                 {
21185                     tag : 'label',
21186                     cls : 'box-label',
21187                     html : this.boxLabel
21188                 }
21189             ]
21190         };
21191         
21192         return cfg;
21193     },
21194     
21195     initEvents : function() 
21196     {
21197         this.parent().register(this);
21198         
21199         this.el.on('click', this.onClick, this);
21200         
21201     },
21202     
21203     onClick : function(e)
21204     {
21205         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21206             this.setChecked(true);
21207         }
21208     },
21209     
21210     setChecked : function(state, suppressEvent)
21211     {
21212         this.parent().setValue(this.value, suppressEvent);
21213         
21214     },
21215     
21216     setBoxLabel : function(v)
21217     {
21218         this.boxLabel = v;
21219         
21220         if(this.rendered){
21221             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21222         }
21223     }
21224     
21225 });
21226  
21227
21228  /*
21229  * - LGPL
21230  *
21231  * Input
21232  * 
21233  */
21234
21235 /**
21236  * @class Roo.bootstrap.SecurePass
21237  * @extends Roo.bootstrap.Input
21238  * Bootstrap SecurePass class
21239  *
21240  * 
21241  * @constructor
21242  * Create a new SecurePass
21243  * @param {Object} config The config object
21244  */
21245  
21246 Roo.bootstrap.SecurePass = function (config) {
21247     // these go here, so the translation tool can replace them..
21248     this.errors = {
21249         PwdEmpty: "Please type a password, and then retype it to confirm.",
21250         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21251         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21252         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21253         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21254         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21255         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21256         TooWeak: "Your password is Too Weak."
21257     },
21258     this.meterLabel = "Password strength:";
21259     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21260     this.meterClass = [
21261         "roo-password-meter-tooweak", 
21262         "roo-password-meter-weak", 
21263         "roo-password-meter-medium", 
21264         "roo-password-meter-strong", 
21265         "roo-password-meter-grey"
21266     ];
21267     
21268     this.errors = {};
21269     
21270     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21271 }
21272
21273 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21274     /**
21275      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21276      * {
21277      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21278      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21279      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21280      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21281      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21282      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21283      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21284      * })
21285      */
21286     // private
21287     
21288     meterWidth: 300,
21289     errorMsg :'',    
21290     errors: false,
21291     imageRoot: '/',
21292     /**
21293      * @cfg {String/Object} Label for the strength meter (defaults to
21294      * 'Password strength:')
21295      */
21296     // private
21297     meterLabel: '',
21298     /**
21299      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21300      * ['Weak', 'Medium', 'Strong'])
21301      */
21302     // private    
21303     pwdStrengths: false,    
21304     // private
21305     strength: 0,
21306     // private
21307     _lastPwd: null,
21308     // private
21309     kCapitalLetter: 0,
21310     kSmallLetter: 1,
21311     kDigit: 2,
21312     kPunctuation: 3,
21313     
21314     insecure: false,
21315     // private
21316     initEvents: function ()
21317     {
21318         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21319
21320         if (this.el.is('input[type=password]') && Roo.isSafari) {
21321             this.el.on('keydown', this.SafariOnKeyDown, this);
21322         }
21323
21324         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21325     },
21326     // private
21327     onRender: function (ct, position)
21328     {
21329         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21330         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21331         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21332
21333         this.trigger.createChild({
21334                    cn: [
21335                     {
21336                     //id: 'PwdMeter',
21337                     tag: 'div',
21338                     cls: 'roo-password-meter-grey col-xs-12',
21339                     style: {
21340                         //width: 0,
21341                         //width: this.meterWidth + 'px'                                                
21342                         }
21343                     },
21344                     {                            
21345                          cls: 'roo-password-meter-text'                          
21346                     }
21347                 ]            
21348         });
21349
21350          
21351         if (this.hideTrigger) {
21352             this.trigger.setDisplayed(false);
21353         }
21354         this.setSize(this.width || '', this.height || '');
21355     },
21356     // private
21357     onDestroy: function ()
21358     {
21359         if (this.trigger) {
21360             this.trigger.removeAllListeners();
21361             this.trigger.remove();
21362         }
21363         if (this.wrap) {
21364             this.wrap.remove();
21365         }
21366         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21367     },
21368     // private
21369     checkStrength: function ()
21370     {
21371         var pwd = this.inputEl().getValue();
21372         if (pwd == this._lastPwd) {
21373             return;
21374         }
21375
21376         var strength;
21377         if (this.ClientSideStrongPassword(pwd)) {
21378             strength = 3;
21379         } else if (this.ClientSideMediumPassword(pwd)) {
21380             strength = 2;
21381         } else if (this.ClientSideWeakPassword(pwd)) {
21382             strength = 1;
21383         } else {
21384             strength = 0;
21385         }
21386         
21387         Roo.log('strength1: ' + strength);
21388         
21389         //var pm = this.trigger.child('div/div/div').dom;
21390         var pm = this.trigger.child('div/div');
21391         pm.removeClass(this.meterClass);
21392         pm.addClass(this.meterClass[strength]);
21393                 
21394         
21395         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21396                 
21397         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21398         
21399         this._lastPwd = pwd;
21400     },
21401     reset: function ()
21402     {
21403         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21404         
21405         this._lastPwd = '';
21406         
21407         var pm = this.trigger.child('div/div');
21408         pm.removeClass(this.meterClass);
21409         pm.addClass('roo-password-meter-grey');        
21410         
21411         
21412         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21413         
21414         pt.innerHTML = '';
21415         this.inputEl().dom.type='password';
21416     },
21417     // private
21418     validateValue: function (value)
21419     {
21420         
21421         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21422             return false;
21423         }
21424         if (value.length == 0) {
21425             if (this.allowBlank) {
21426                 this.clearInvalid();
21427                 return true;
21428             }
21429
21430             this.markInvalid(this.errors.PwdEmpty);
21431             this.errorMsg = this.errors.PwdEmpty;
21432             return false;
21433         }
21434         
21435         if(this.insecure){
21436             return true;
21437         }
21438         
21439         if ('[\x21-\x7e]*'.match(value)) {
21440             this.markInvalid(this.errors.PwdBadChar);
21441             this.errorMsg = this.errors.PwdBadChar;
21442             return false;
21443         }
21444         if (value.length < 6) {
21445             this.markInvalid(this.errors.PwdShort);
21446             this.errorMsg = this.errors.PwdShort;
21447             return false;
21448         }
21449         if (value.length > 16) {
21450             this.markInvalid(this.errors.PwdLong);
21451             this.errorMsg = this.errors.PwdLong;
21452             return false;
21453         }
21454         var strength;
21455         if (this.ClientSideStrongPassword(value)) {
21456             strength = 3;
21457         } else if (this.ClientSideMediumPassword(value)) {
21458             strength = 2;
21459         } else if (this.ClientSideWeakPassword(value)) {
21460             strength = 1;
21461         } else {
21462             strength = 0;
21463         }
21464
21465         
21466         if (strength < 2) {
21467             //this.markInvalid(this.errors.TooWeak);
21468             this.errorMsg = this.errors.TooWeak;
21469             //return false;
21470         }
21471         
21472         
21473         console.log('strength2: ' + strength);
21474         
21475         //var pm = this.trigger.child('div/div/div').dom;
21476         
21477         var pm = this.trigger.child('div/div');
21478         pm.removeClass(this.meterClass);
21479         pm.addClass(this.meterClass[strength]);
21480                 
21481         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21482                 
21483         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21484         
21485         this.errorMsg = ''; 
21486         return true;
21487     },
21488     // private
21489     CharacterSetChecks: function (type)
21490     {
21491         this.type = type;
21492         this.fResult = false;
21493     },
21494     // private
21495     isctype: function (character, type)
21496     {
21497         switch (type) {  
21498             case this.kCapitalLetter:
21499                 if (character >= 'A' && character <= 'Z') {
21500                     return true;
21501                 }
21502                 break;
21503             
21504             case this.kSmallLetter:
21505                 if (character >= 'a' && character <= 'z') {
21506                     return true;
21507                 }
21508                 break;
21509             
21510             case this.kDigit:
21511                 if (character >= '0' && character <= '9') {
21512                     return true;
21513                 }
21514                 break;
21515             
21516             case this.kPunctuation:
21517                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21518                     return true;
21519                 }
21520                 break;
21521             
21522             default:
21523                 return false;
21524         }
21525
21526     },
21527     // private
21528     IsLongEnough: function (pwd, size)
21529     {
21530         return !(pwd == null || isNaN(size) || pwd.length < size);
21531     },
21532     // private
21533     SpansEnoughCharacterSets: function (word, nb)
21534     {
21535         if (!this.IsLongEnough(word, nb))
21536         {
21537             return false;
21538         }
21539
21540         var characterSetChecks = new Array(
21541             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21542             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21543         );
21544         
21545         for (var index = 0; index < word.length; ++index) {
21546             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21547                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21548                     characterSetChecks[nCharSet].fResult = true;
21549                     break;
21550                 }
21551             }
21552         }
21553
21554         var nCharSets = 0;
21555         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21556             if (characterSetChecks[nCharSet].fResult) {
21557                 ++nCharSets;
21558             }
21559         }
21560
21561         if (nCharSets < nb) {
21562             return false;
21563         }
21564         return true;
21565     },
21566     // private
21567     ClientSideStrongPassword: function (pwd)
21568     {
21569         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21570     },
21571     // private
21572     ClientSideMediumPassword: function (pwd)
21573     {
21574         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21575     },
21576     // private
21577     ClientSideWeakPassword: function (pwd)
21578     {
21579         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21580     }
21581           
21582 })//<script type="text/javascript">
21583
21584 /*
21585  * Based  Ext JS Library 1.1.1
21586  * Copyright(c) 2006-2007, Ext JS, LLC.
21587  * LGPL
21588  *
21589  */
21590  
21591 /**
21592  * @class Roo.HtmlEditorCore
21593  * @extends Roo.Component
21594  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21595  *
21596  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21597  */
21598
21599 Roo.HtmlEditorCore = function(config){
21600     
21601     
21602     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21603     
21604     
21605     this.addEvents({
21606         /**
21607          * @event initialize
21608          * Fires when the editor is fully initialized (including the iframe)
21609          * @param {Roo.HtmlEditorCore} this
21610          */
21611         initialize: true,
21612         /**
21613          * @event activate
21614          * Fires when the editor is first receives the focus. Any insertion must wait
21615          * until after this event.
21616          * @param {Roo.HtmlEditorCore} this
21617          */
21618         activate: true,
21619          /**
21620          * @event beforesync
21621          * Fires before the textarea is updated with content from the editor iframe. Return false
21622          * to cancel the sync.
21623          * @param {Roo.HtmlEditorCore} this
21624          * @param {String} html
21625          */
21626         beforesync: true,
21627          /**
21628          * @event beforepush
21629          * Fires before the iframe editor is updated with content from the textarea. Return false
21630          * to cancel the push.
21631          * @param {Roo.HtmlEditorCore} this
21632          * @param {String} html
21633          */
21634         beforepush: true,
21635          /**
21636          * @event sync
21637          * Fires when the textarea is updated with content from the editor iframe.
21638          * @param {Roo.HtmlEditorCore} this
21639          * @param {String} html
21640          */
21641         sync: true,
21642          /**
21643          * @event push
21644          * Fires when the iframe editor is updated with content from the textarea.
21645          * @param {Roo.HtmlEditorCore} this
21646          * @param {String} html
21647          */
21648         push: true,
21649         
21650         /**
21651          * @event editorevent
21652          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21653          * @param {Roo.HtmlEditorCore} this
21654          */
21655         editorevent: true
21656         
21657     });
21658     
21659     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21660     
21661     // defaults : white / black...
21662     this.applyBlacklists();
21663     
21664     
21665     
21666 };
21667
21668
21669 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21670
21671
21672      /**
21673      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21674      */
21675     
21676     owner : false,
21677     
21678      /**
21679      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21680      *                        Roo.resizable.
21681      */
21682     resizable : false,
21683      /**
21684      * @cfg {Number} height (in pixels)
21685      */   
21686     height: 300,
21687    /**
21688      * @cfg {Number} width (in pixels)
21689      */   
21690     width: 500,
21691     
21692     /**
21693      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21694      * 
21695      */
21696     stylesheets: false,
21697     
21698     // id of frame..
21699     frameId: false,
21700     
21701     // private properties
21702     validationEvent : false,
21703     deferHeight: true,
21704     initialized : false,
21705     activated : false,
21706     sourceEditMode : false,
21707     onFocus : Roo.emptyFn,
21708     iframePad:3,
21709     hideMode:'offsets',
21710     
21711     clearUp: true,
21712     
21713     // blacklist + whitelisted elements..
21714     black: false,
21715     white: false,
21716      
21717     bodyCls : '',
21718
21719     /**
21720      * Protected method that will not generally be called directly. It
21721      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21722      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21723      */
21724     getDocMarkup : function(){
21725         // body styles..
21726         var st = '';
21727         
21728         // inherit styels from page...?? 
21729         if (this.stylesheets === false) {
21730             
21731             Roo.get(document.head).select('style').each(function(node) {
21732                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21733             });
21734             
21735             Roo.get(document.head).select('link').each(function(node) { 
21736                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21737             });
21738             
21739         } else if (!this.stylesheets.length) {
21740                 // simple..
21741                 st = '<style type="text/css">' +
21742                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21743                    '</style>';
21744         } else { 
21745             st = '<style type="text/css">' +
21746                     this.stylesheets +
21747                 '</style>';
21748         }
21749         
21750         st +=  '<style type="text/css">' +
21751             'IMG { cursor: pointer } ' +
21752         '</style>';
21753
21754         var cls = 'roo-htmleditor-body';
21755         
21756         if(this.bodyCls.length){
21757             cls += ' ' + this.bodyCls;
21758         }
21759         
21760         return '<html><head>' + st  +
21761             //<style type="text/css">' +
21762             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21763             //'</style>' +
21764             ' </head><body class="' +  cls + '"></body></html>';
21765     },
21766
21767     // private
21768     onRender : function(ct, position)
21769     {
21770         var _t = this;
21771         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21772         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21773         
21774         
21775         this.el.dom.style.border = '0 none';
21776         this.el.dom.setAttribute('tabIndex', -1);
21777         this.el.addClass('x-hidden hide');
21778         
21779         
21780         
21781         if(Roo.isIE){ // fix IE 1px bogus margin
21782             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21783         }
21784        
21785         
21786         this.frameId = Roo.id();
21787         
21788          
21789         
21790         var iframe = this.owner.wrap.createChild({
21791             tag: 'iframe',
21792             cls: 'form-control', // bootstrap..
21793             id: this.frameId,
21794             name: this.frameId,
21795             frameBorder : 'no',
21796             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21797         }, this.el
21798         );
21799         
21800         
21801         this.iframe = iframe.dom;
21802
21803          this.assignDocWin();
21804         
21805         this.doc.designMode = 'on';
21806        
21807         this.doc.open();
21808         this.doc.write(this.getDocMarkup());
21809         this.doc.close();
21810
21811         
21812         var task = { // must defer to wait for browser to be ready
21813             run : function(){
21814                 //console.log("run task?" + this.doc.readyState);
21815                 this.assignDocWin();
21816                 if(this.doc.body || this.doc.readyState == 'complete'){
21817                     try {
21818                         this.doc.designMode="on";
21819                     } catch (e) {
21820                         return;
21821                     }
21822                     Roo.TaskMgr.stop(task);
21823                     this.initEditor.defer(10, this);
21824                 }
21825             },
21826             interval : 10,
21827             duration: 10000,
21828             scope: this
21829         };
21830         Roo.TaskMgr.start(task);
21831
21832     },
21833
21834     // private
21835     onResize : function(w, h)
21836     {
21837          Roo.log('resize: ' +w + ',' + h );
21838         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21839         if(!this.iframe){
21840             return;
21841         }
21842         if(typeof w == 'number'){
21843             
21844             this.iframe.style.width = w + 'px';
21845         }
21846         if(typeof h == 'number'){
21847             
21848             this.iframe.style.height = h + 'px';
21849             if(this.doc){
21850                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21851             }
21852         }
21853         
21854     },
21855
21856     /**
21857      * Toggles the editor between standard and source edit mode.
21858      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21859      */
21860     toggleSourceEdit : function(sourceEditMode){
21861         
21862         this.sourceEditMode = sourceEditMode === true;
21863         
21864         if(this.sourceEditMode){
21865  
21866             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21867             
21868         }else{
21869             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21870             //this.iframe.className = '';
21871             this.deferFocus();
21872         }
21873         //this.setSize(this.owner.wrap.getSize());
21874         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21875     },
21876
21877     
21878   
21879
21880     /**
21881      * Protected method that will not generally be called directly. If you need/want
21882      * custom HTML cleanup, this is the method you should override.
21883      * @param {String} html The HTML to be cleaned
21884      * return {String} The cleaned HTML
21885      */
21886     cleanHtml : function(html){
21887         html = String(html);
21888         if(html.length > 5){
21889             if(Roo.isSafari){ // strip safari nonsense
21890                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21891             }
21892         }
21893         if(html == '&nbsp;'){
21894             html = '';
21895         }
21896         return html;
21897     },
21898
21899     /**
21900      * HTML Editor -> Textarea
21901      * Protected method that will not generally be called directly. Syncs the contents
21902      * of the editor iframe with the textarea.
21903      */
21904     syncValue : function(){
21905         if(this.initialized){
21906             var bd = (this.doc.body || this.doc.documentElement);
21907             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21908             var html = bd.innerHTML;
21909             if(Roo.isSafari){
21910                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21911                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21912                 if(m && m[1]){
21913                     html = '<div style="'+m[0]+'">' + html + '</div>';
21914                 }
21915             }
21916             html = this.cleanHtml(html);
21917             // fix up the special chars.. normaly like back quotes in word...
21918             // however we do not want to do this with chinese..
21919             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21920                 var cc = b.charCodeAt();
21921                 if (
21922                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21923                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21924                     (cc >= 0xf900 && cc < 0xfb00 )
21925                 ) {
21926                         return b;
21927                 }
21928                 return "&#"+cc+";" 
21929             });
21930             if(this.owner.fireEvent('beforesync', this, html) !== false){
21931                 this.el.dom.value = html;
21932                 this.owner.fireEvent('sync', this, html);
21933             }
21934         }
21935     },
21936
21937     /**
21938      * Protected method that will not generally be called directly. Pushes the value of the textarea
21939      * into the iframe editor.
21940      */
21941     pushValue : function(){
21942         if(this.initialized){
21943             var v = this.el.dom.value.trim();
21944             
21945 //            if(v.length < 1){
21946 //                v = '&#160;';
21947 //            }
21948             
21949             if(this.owner.fireEvent('beforepush', this, v) !== false){
21950                 var d = (this.doc.body || this.doc.documentElement);
21951                 d.innerHTML = v;
21952                 this.cleanUpPaste();
21953                 this.el.dom.value = d.innerHTML;
21954                 this.owner.fireEvent('push', this, v);
21955             }
21956         }
21957     },
21958
21959     // private
21960     deferFocus : function(){
21961         this.focus.defer(10, this);
21962     },
21963
21964     // doc'ed in Field
21965     focus : function(){
21966         if(this.win && !this.sourceEditMode){
21967             this.win.focus();
21968         }else{
21969             this.el.focus();
21970         }
21971     },
21972     
21973     assignDocWin: function()
21974     {
21975         var iframe = this.iframe;
21976         
21977          if(Roo.isIE){
21978             this.doc = iframe.contentWindow.document;
21979             this.win = iframe.contentWindow;
21980         } else {
21981 //            if (!Roo.get(this.frameId)) {
21982 //                return;
21983 //            }
21984 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21985 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21986             
21987             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21988                 return;
21989             }
21990             
21991             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21992             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21993         }
21994     },
21995     
21996     // private
21997     initEditor : function(){
21998         //console.log("INIT EDITOR");
21999         this.assignDocWin();
22000         
22001         
22002         
22003         this.doc.designMode="on";
22004         this.doc.open();
22005         this.doc.write(this.getDocMarkup());
22006         this.doc.close();
22007         
22008         var dbody = (this.doc.body || this.doc.documentElement);
22009         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22010         // this copies styles from the containing element into thsi one..
22011         // not sure why we need all of this..
22012         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22013         
22014         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22015         //ss['background-attachment'] = 'fixed'; // w3c
22016         dbody.bgProperties = 'fixed'; // ie
22017         //Roo.DomHelper.applyStyles(dbody, ss);
22018         Roo.EventManager.on(this.doc, {
22019             //'mousedown': this.onEditorEvent,
22020             'mouseup': this.onEditorEvent,
22021             'dblclick': this.onEditorEvent,
22022             'click': this.onEditorEvent,
22023             'keyup': this.onEditorEvent,
22024             buffer:100,
22025             scope: this
22026         });
22027         if(Roo.isGecko){
22028             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22029         }
22030         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22031             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22032         }
22033         this.initialized = true;
22034
22035         this.owner.fireEvent('initialize', this);
22036         this.pushValue();
22037     },
22038
22039     // private
22040     onDestroy : function(){
22041         
22042         
22043         
22044         if(this.rendered){
22045             
22046             //for (var i =0; i < this.toolbars.length;i++) {
22047             //    // fixme - ask toolbars for heights?
22048             //    this.toolbars[i].onDestroy();
22049            // }
22050             
22051             //this.wrap.dom.innerHTML = '';
22052             //this.wrap.remove();
22053         }
22054     },
22055
22056     // private
22057     onFirstFocus : function(){
22058         
22059         this.assignDocWin();
22060         
22061         
22062         this.activated = true;
22063          
22064     
22065         if(Roo.isGecko){ // prevent silly gecko errors
22066             this.win.focus();
22067             var s = this.win.getSelection();
22068             if(!s.focusNode || s.focusNode.nodeType != 3){
22069                 var r = s.getRangeAt(0);
22070                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22071                 r.collapse(true);
22072                 this.deferFocus();
22073             }
22074             try{
22075                 this.execCmd('useCSS', true);
22076                 this.execCmd('styleWithCSS', false);
22077             }catch(e){}
22078         }
22079         this.owner.fireEvent('activate', this);
22080     },
22081
22082     // private
22083     adjustFont: function(btn){
22084         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22085         //if(Roo.isSafari){ // safari
22086         //    adjust *= 2;
22087        // }
22088         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22089         if(Roo.isSafari){ // safari
22090             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22091             v =  (v < 10) ? 10 : v;
22092             v =  (v > 48) ? 48 : v;
22093             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22094             
22095         }
22096         
22097         
22098         v = Math.max(1, v+adjust);
22099         
22100         this.execCmd('FontSize', v  );
22101     },
22102
22103     onEditorEvent : function(e)
22104     {
22105         this.owner.fireEvent('editorevent', this, e);
22106       //  this.updateToolbar();
22107         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22108     },
22109
22110     insertTag : function(tg)
22111     {
22112         // could be a bit smarter... -> wrap the current selected tRoo..
22113         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22114             
22115             range = this.createRange(this.getSelection());
22116             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22117             wrappingNode.appendChild(range.extractContents());
22118             range.insertNode(wrappingNode);
22119
22120             return;
22121             
22122             
22123             
22124         }
22125         this.execCmd("formatblock",   tg);
22126         
22127     },
22128     
22129     insertText : function(txt)
22130     {
22131         
22132         
22133         var range = this.createRange();
22134         range.deleteContents();
22135                //alert(Sender.getAttribute('label'));
22136                
22137         range.insertNode(this.doc.createTextNode(txt));
22138     } ,
22139     
22140      
22141
22142     /**
22143      * Executes a Midas editor command on the editor document and performs necessary focus and
22144      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22145      * @param {String} cmd The Midas command
22146      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22147      */
22148     relayCmd : function(cmd, value){
22149         this.win.focus();
22150         this.execCmd(cmd, value);
22151         this.owner.fireEvent('editorevent', this);
22152         //this.updateToolbar();
22153         this.owner.deferFocus();
22154     },
22155
22156     /**
22157      * Executes a Midas editor command directly on the editor document.
22158      * For visual commands, you should use {@link #relayCmd} instead.
22159      * <b>This should only be called after the editor is initialized.</b>
22160      * @param {String} cmd The Midas command
22161      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22162      */
22163     execCmd : function(cmd, value){
22164         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22165         this.syncValue();
22166     },
22167  
22168  
22169    
22170     /**
22171      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22172      * to insert tRoo.
22173      * @param {String} text | dom node.. 
22174      */
22175     insertAtCursor : function(text)
22176     {
22177         
22178         if(!this.activated){
22179             return;
22180         }
22181         /*
22182         if(Roo.isIE){
22183             this.win.focus();
22184             var r = this.doc.selection.createRange();
22185             if(r){
22186                 r.collapse(true);
22187                 r.pasteHTML(text);
22188                 this.syncValue();
22189                 this.deferFocus();
22190             
22191             }
22192             return;
22193         }
22194         */
22195         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22196             this.win.focus();
22197             
22198             
22199             // from jquery ui (MIT licenced)
22200             var range, node;
22201             var win = this.win;
22202             
22203             if (win.getSelection && win.getSelection().getRangeAt) {
22204                 range = win.getSelection().getRangeAt(0);
22205                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22206                 range.insertNode(node);
22207             } else if (win.document.selection && win.document.selection.createRange) {
22208                 // no firefox support
22209                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22210                 win.document.selection.createRange().pasteHTML(txt);
22211             } else {
22212                 // no firefox support
22213                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22214                 this.execCmd('InsertHTML', txt);
22215             } 
22216             
22217             this.syncValue();
22218             
22219             this.deferFocus();
22220         }
22221     },
22222  // private
22223     mozKeyPress : function(e){
22224         if(e.ctrlKey){
22225             var c = e.getCharCode(), cmd;
22226           
22227             if(c > 0){
22228                 c = String.fromCharCode(c).toLowerCase();
22229                 switch(c){
22230                     case 'b':
22231                         cmd = 'bold';
22232                         break;
22233                     case 'i':
22234                         cmd = 'italic';
22235                         break;
22236                     
22237                     case 'u':
22238                         cmd = 'underline';
22239                         break;
22240                     
22241                     case 'v':
22242                         this.cleanUpPaste.defer(100, this);
22243                         return;
22244                         
22245                 }
22246                 if(cmd){
22247                     this.win.focus();
22248                     this.execCmd(cmd);
22249                     this.deferFocus();
22250                     e.preventDefault();
22251                 }
22252                 
22253             }
22254         }
22255     },
22256
22257     // private
22258     fixKeys : function(){ // load time branching for fastest keydown performance
22259         if(Roo.isIE){
22260             return function(e){
22261                 var k = e.getKey(), r;
22262                 if(k == e.TAB){
22263                     e.stopEvent();
22264                     r = this.doc.selection.createRange();
22265                     if(r){
22266                         r.collapse(true);
22267                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22268                         this.deferFocus();
22269                     }
22270                     return;
22271                 }
22272                 
22273                 if(k == e.ENTER){
22274                     r = this.doc.selection.createRange();
22275                     if(r){
22276                         var target = r.parentElement();
22277                         if(!target || target.tagName.toLowerCase() != 'li'){
22278                             e.stopEvent();
22279                             r.pasteHTML('<br />');
22280                             r.collapse(false);
22281                             r.select();
22282                         }
22283                     }
22284                 }
22285                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22286                     this.cleanUpPaste.defer(100, this);
22287                     return;
22288                 }
22289                 
22290                 
22291             };
22292         }else if(Roo.isOpera){
22293             return function(e){
22294                 var k = e.getKey();
22295                 if(k == e.TAB){
22296                     e.stopEvent();
22297                     this.win.focus();
22298                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22299                     this.deferFocus();
22300                 }
22301                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22302                     this.cleanUpPaste.defer(100, this);
22303                     return;
22304                 }
22305                 
22306             };
22307         }else if(Roo.isSafari){
22308             return function(e){
22309                 var k = e.getKey();
22310                 
22311                 if(k == e.TAB){
22312                     e.stopEvent();
22313                     this.execCmd('InsertText','\t');
22314                     this.deferFocus();
22315                     return;
22316                 }
22317                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22318                     this.cleanUpPaste.defer(100, this);
22319                     return;
22320                 }
22321                 
22322              };
22323         }
22324     }(),
22325     
22326     getAllAncestors: function()
22327     {
22328         var p = this.getSelectedNode();
22329         var a = [];
22330         if (!p) {
22331             a.push(p); // push blank onto stack..
22332             p = this.getParentElement();
22333         }
22334         
22335         
22336         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22337             a.push(p);
22338             p = p.parentNode;
22339         }
22340         a.push(this.doc.body);
22341         return a;
22342     },
22343     lastSel : false,
22344     lastSelNode : false,
22345     
22346     
22347     getSelection : function() 
22348     {
22349         this.assignDocWin();
22350         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22351     },
22352     
22353     getSelectedNode: function() 
22354     {
22355         // this may only work on Gecko!!!
22356         
22357         // should we cache this!!!!
22358         
22359         
22360         
22361          
22362         var range = this.createRange(this.getSelection()).cloneRange();
22363         
22364         if (Roo.isIE) {
22365             var parent = range.parentElement();
22366             while (true) {
22367                 var testRange = range.duplicate();
22368                 testRange.moveToElementText(parent);
22369                 if (testRange.inRange(range)) {
22370                     break;
22371                 }
22372                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22373                     break;
22374                 }
22375                 parent = parent.parentElement;
22376             }
22377             return parent;
22378         }
22379         
22380         // is ancestor a text element.
22381         var ac =  range.commonAncestorContainer;
22382         if (ac.nodeType == 3) {
22383             ac = ac.parentNode;
22384         }
22385         
22386         var ar = ac.childNodes;
22387          
22388         var nodes = [];
22389         var other_nodes = [];
22390         var has_other_nodes = false;
22391         for (var i=0;i<ar.length;i++) {
22392             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22393                 continue;
22394             }
22395             // fullly contained node.
22396             
22397             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22398                 nodes.push(ar[i]);
22399                 continue;
22400             }
22401             
22402             // probably selected..
22403             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22404                 other_nodes.push(ar[i]);
22405                 continue;
22406             }
22407             // outer..
22408             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22409                 continue;
22410             }
22411             
22412             
22413             has_other_nodes = true;
22414         }
22415         if (!nodes.length && other_nodes.length) {
22416             nodes= other_nodes;
22417         }
22418         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22419             return false;
22420         }
22421         
22422         return nodes[0];
22423     },
22424     createRange: function(sel)
22425     {
22426         // this has strange effects when using with 
22427         // top toolbar - not sure if it's a great idea.
22428         //this.editor.contentWindow.focus();
22429         if (typeof sel != "undefined") {
22430             try {
22431                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22432             } catch(e) {
22433                 return this.doc.createRange();
22434             }
22435         } else {
22436             return this.doc.createRange();
22437         }
22438     },
22439     getParentElement: function()
22440     {
22441         
22442         this.assignDocWin();
22443         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22444         
22445         var range = this.createRange(sel);
22446          
22447         try {
22448             var p = range.commonAncestorContainer;
22449             while (p.nodeType == 3) { // text node
22450                 p = p.parentNode;
22451             }
22452             return p;
22453         } catch (e) {
22454             return null;
22455         }
22456     
22457     },
22458     /***
22459      *
22460      * Range intersection.. the hard stuff...
22461      *  '-1' = before
22462      *  '0' = hits..
22463      *  '1' = after.
22464      *         [ -- selected range --- ]
22465      *   [fail]                        [fail]
22466      *
22467      *    basically..
22468      *      if end is before start or  hits it. fail.
22469      *      if start is after end or hits it fail.
22470      *
22471      *   if either hits (but other is outside. - then it's not 
22472      *   
22473      *    
22474      **/
22475     
22476     
22477     // @see http://www.thismuchiknow.co.uk/?p=64.
22478     rangeIntersectsNode : function(range, node)
22479     {
22480         var nodeRange = node.ownerDocument.createRange();
22481         try {
22482             nodeRange.selectNode(node);
22483         } catch (e) {
22484             nodeRange.selectNodeContents(node);
22485         }
22486     
22487         var rangeStartRange = range.cloneRange();
22488         rangeStartRange.collapse(true);
22489     
22490         var rangeEndRange = range.cloneRange();
22491         rangeEndRange.collapse(false);
22492     
22493         var nodeStartRange = nodeRange.cloneRange();
22494         nodeStartRange.collapse(true);
22495     
22496         var nodeEndRange = nodeRange.cloneRange();
22497         nodeEndRange.collapse(false);
22498     
22499         return rangeStartRange.compareBoundaryPoints(
22500                  Range.START_TO_START, nodeEndRange) == -1 &&
22501                rangeEndRange.compareBoundaryPoints(
22502                  Range.START_TO_START, nodeStartRange) == 1;
22503         
22504          
22505     },
22506     rangeCompareNode : function(range, node)
22507     {
22508         var nodeRange = node.ownerDocument.createRange();
22509         try {
22510             nodeRange.selectNode(node);
22511         } catch (e) {
22512             nodeRange.selectNodeContents(node);
22513         }
22514         
22515         
22516         range.collapse(true);
22517     
22518         nodeRange.collapse(true);
22519      
22520         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22521         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22522          
22523         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22524         
22525         var nodeIsBefore   =  ss == 1;
22526         var nodeIsAfter    = ee == -1;
22527         
22528         if (nodeIsBefore && nodeIsAfter) {
22529             return 0; // outer
22530         }
22531         if (!nodeIsBefore && nodeIsAfter) {
22532             return 1; //right trailed.
22533         }
22534         
22535         if (nodeIsBefore && !nodeIsAfter) {
22536             return 2;  // left trailed.
22537         }
22538         // fully contined.
22539         return 3;
22540     },
22541
22542     // private? - in a new class?
22543     cleanUpPaste :  function()
22544     {
22545         // cleans up the whole document..
22546         Roo.log('cleanuppaste');
22547         
22548         this.cleanUpChildren(this.doc.body);
22549         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22550         if (clean != this.doc.body.innerHTML) {
22551             this.doc.body.innerHTML = clean;
22552         }
22553         
22554     },
22555     
22556     cleanWordChars : function(input) {// change the chars to hex code
22557         var he = Roo.HtmlEditorCore;
22558         
22559         var output = input;
22560         Roo.each(he.swapCodes, function(sw) { 
22561             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22562             
22563             output = output.replace(swapper, sw[1]);
22564         });
22565         
22566         return output;
22567     },
22568     
22569     
22570     cleanUpChildren : function (n)
22571     {
22572         if (!n.childNodes.length) {
22573             return;
22574         }
22575         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22576            this.cleanUpChild(n.childNodes[i]);
22577         }
22578     },
22579     
22580     
22581         
22582     
22583     cleanUpChild : function (node)
22584     {
22585         var ed = this;
22586         //console.log(node);
22587         if (node.nodeName == "#text") {
22588             // clean up silly Windows -- stuff?
22589             return; 
22590         }
22591         if (node.nodeName == "#comment") {
22592             node.parentNode.removeChild(node);
22593             // clean up silly Windows -- stuff?
22594             return; 
22595         }
22596         var lcname = node.tagName.toLowerCase();
22597         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22598         // whitelist of tags..
22599         
22600         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22601             // remove node.
22602             node.parentNode.removeChild(node);
22603             return;
22604             
22605         }
22606         
22607         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22608         
22609         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22610         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22611         
22612         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22613         //    remove_keep_children = true;
22614         //}
22615         
22616         if (remove_keep_children) {
22617             this.cleanUpChildren(node);
22618             // inserts everything just before this node...
22619             while (node.childNodes.length) {
22620                 var cn = node.childNodes[0];
22621                 node.removeChild(cn);
22622                 node.parentNode.insertBefore(cn, node);
22623             }
22624             node.parentNode.removeChild(node);
22625             return;
22626         }
22627         
22628         if (!node.attributes || !node.attributes.length) {
22629             this.cleanUpChildren(node);
22630             return;
22631         }
22632         
22633         function cleanAttr(n,v)
22634         {
22635             
22636             if (v.match(/^\./) || v.match(/^\//)) {
22637                 return;
22638             }
22639             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22640                 return;
22641             }
22642             if (v.match(/^#/)) {
22643                 return;
22644             }
22645 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22646             node.removeAttribute(n);
22647             
22648         }
22649         
22650         var cwhite = this.cwhite;
22651         var cblack = this.cblack;
22652             
22653         function cleanStyle(n,v)
22654         {
22655             if (v.match(/expression/)) { //XSS?? should we even bother..
22656                 node.removeAttribute(n);
22657                 return;
22658             }
22659             
22660             var parts = v.split(/;/);
22661             var clean = [];
22662             
22663             Roo.each(parts, function(p) {
22664                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22665                 if (!p.length) {
22666                     return true;
22667                 }
22668                 var l = p.split(':').shift().replace(/\s+/g,'');
22669                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22670                 
22671                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22672 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22673                     //node.removeAttribute(n);
22674                     return true;
22675                 }
22676                 //Roo.log()
22677                 // only allow 'c whitelisted system attributes'
22678                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22679 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22680                     //node.removeAttribute(n);
22681                     return true;
22682                 }
22683                 
22684                 
22685                  
22686                 
22687                 clean.push(p);
22688                 return true;
22689             });
22690             if (clean.length) { 
22691                 node.setAttribute(n, clean.join(';'));
22692             } else {
22693                 node.removeAttribute(n);
22694             }
22695             
22696         }
22697         
22698         
22699         for (var i = node.attributes.length-1; i > -1 ; i--) {
22700             var a = node.attributes[i];
22701             //console.log(a);
22702             
22703             if (a.name.toLowerCase().substr(0,2)=='on')  {
22704                 node.removeAttribute(a.name);
22705                 continue;
22706             }
22707             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22708                 node.removeAttribute(a.name);
22709                 continue;
22710             }
22711             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22712                 cleanAttr(a.name,a.value); // fixme..
22713                 continue;
22714             }
22715             if (a.name == 'style') {
22716                 cleanStyle(a.name,a.value);
22717                 continue;
22718             }
22719             /// clean up MS crap..
22720             // tecnically this should be a list of valid class'es..
22721             
22722             
22723             if (a.name == 'class') {
22724                 if (a.value.match(/^Mso/)) {
22725                     node.className = '';
22726                 }
22727                 
22728                 if (a.value.match(/^body$/)) {
22729                     node.className = '';
22730                 }
22731                 continue;
22732             }
22733             
22734             // style cleanup!?
22735             // class cleanup?
22736             
22737         }
22738         
22739         
22740         this.cleanUpChildren(node);
22741         
22742         
22743     },
22744     
22745     /**
22746      * Clean up MS wordisms...
22747      */
22748     cleanWord : function(node)
22749     {
22750         
22751         
22752         if (!node) {
22753             this.cleanWord(this.doc.body);
22754             return;
22755         }
22756         if (node.nodeName == "#text") {
22757             // clean up silly Windows -- stuff?
22758             return; 
22759         }
22760         if (node.nodeName == "#comment") {
22761             node.parentNode.removeChild(node);
22762             // clean up silly Windows -- stuff?
22763             return; 
22764         }
22765         
22766         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22767             node.parentNode.removeChild(node);
22768             return;
22769         }
22770         
22771         // remove - but keep children..
22772         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22773             while (node.childNodes.length) {
22774                 var cn = node.childNodes[0];
22775                 node.removeChild(cn);
22776                 node.parentNode.insertBefore(cn, node);
22777             }
22778             node.parentNode.removeChild(node);
22779             this.iterateChildren(node, this.cleanWord);
22780             return;
22781         }
22782         // clean styles
22783         if (node.className.length) {
22784             
22785             var cn = node.className.split(/\W+/);
22786             var cna = [];
22787             Roo.each(cn, function(cls) {
22788                 if (cls.match(/Mso[a-zA-Z]+/)) {
22789                     return;
22790                 }
22791                 cna.push(cls);
22792             });
22793             node.className = cna.length ? cna.join(' ') : '';
22794             if (!cna.length) {
22795                 node.removeAttribute("class");
22796             }
22797         }
22798         
22799         if (node.hasAttribute("lang")) {
22800             node.removeAttribute("lang");
22801         }
22802         
22803         if (node.hasAttribute("style")) {
22804             
22805             var styles = node.getAttribute("style").split(";");
22806             var nstyle = [];
22807             Roo.each(styles, function(s) {
22808                 if (!s.match(/:/)) {
22809                     return;
22810                 }
22811                 var kv = s.split(":");
22812                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22813                     return;
22814                 }
22815                 // what ever is left... we allow.
22816                 nstyle.push(s);
22817             });
22818             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22819             if (!nstyle.length) {
22820                 node.removeAttribute('style');
22821             }
22822         }
22823         this.iterateChildren(node, this.cleanWord);
22824         
22825         
22826         
22827     },
22828     /**
22829      * iterateChildren of a Node, calling fn each time, using this as the scole..
22830      * @param {DomNode} node node to iterate children of.
22831      * @param {Function} fn method of this class to call on each item.
22832      */
22833     iterateChildren : function(node, fn)
22834     {
22835         if (!node.childNodes.length) {
22836                 return;
22837         }
22838         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22839            fn.call(this, node.childNodes[i])
22840         }
22841     },
22842     
22843     
22844     /**
22845      * cleanTableWidths.
22846      *
22847      * Quite often pasting from word etc.. results in tables with column and widths.
22848      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22849      *
22850      */
22851     cleanTableWidths : function(node)
22852     {
22853          
22854          
22855         if (!node) {
22856             this.cleanTableWidths(this.doc.body);
22857             return;
22858         }
22859         
22860         // ignore list...
22861         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22862             return; 
22863         }
22864         Roo.log(node.tagName);
22865         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22866             this.iterateChildren(node, this.cleanTableWidths);
22867             return;
22868         }
22869         if (node.hasAttribute('width')) {
22870             node.removeAttribute('width');
22871         }
22872         
22873          
22874         if (node.hasAttribute("style")) {
22875             // pretty basic...
22876             
22877             var styles = node.getAttribute("style").split(";");
22878             var nstyle = [];
22879             Roo.each(styles, function(s) {
22880                 if (!s.match(/:/)) {
22881                     return;
22882                 }
22883                 var kv = s.split(":");
22884                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22885                     return;
22886                 }
22887                 // what ever is left... we allow.
22888                 nstyle.push(s);
22889             });
22890             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22891             if (!nstyle.length) {
22892                 node.removeAttribute('style');
22893             }
22894         }
22895         
22896         this.iterateChildren(node, this.cleanTableWidths);
22897         
22898         
22899     },
22900     
22901     
22902     
22903     
22904     domToHTML : function(currentElement, depth, nopadtext) {
22905         
22906         depth = depth || 0;
22907         nopadtext = nopadtext || false;
22908     
22909         if (!currentElement) {
22910             return this.domToHTML(this.doc.body);
22911         }
22912         
22913         //Roo.log(currentElement);
22914         var j;
22915         var allText = false;
22916         var nodeName = currentElement.nodeName;
22917         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22918         
22919         if  (nodeName == '#text') {
22920             
22921             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22922         }
22923         
22924         
22925         var ret = '';
22926         if (nodeName != 'BODY') {
22927              
22928             var i = 0;
22929             // Prints the node tagName, such as <A>, <IMG>, etc
22930             if (tagName) {
22931                 var attr = [];
22932                 for(i = 0; i < currentElement.attributes.length;i++) {
22933                     // quoting?
22934                     var aname = currentElement.attributes.item(i).name;
22935                     if (!currentElement.attributes.item(i).value.length) {
22936                         continue;
22937                     }
22938                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22939                 }
22940                 
22941                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22942             } 
22943             else {
22944                 
22945                 // eack
22946             }
22947         } else {
22948             tagName = false;
22949         }
22950         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22951             return ret;
22952         }
22953         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22954             nopadtext = true;
22955         }
22956         
22957         
22958         // Traverse the tree
22959         i = 0;
22960         var currentElementChild = currentElement.childNodes.item(i);
22961         var allText = true;
22962         var innerHTML  = '';
22963         lastnode = '';
22964         while (currentElementChild) {
22965             // Formatting code (indent the tree so it looks nice on the screen)
22966             var nopad = nopadtext;
22967             if (lastnode == 'SPAN') {
22968                 nopad  = true;
22969             }
22970             // text
22971             if  (currentElementChild.nodeName == '#text') {
22972                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22973                 toadd = nopadtext ? toadd : toadd.trim();
22974                 if (!nopad && toadd.length > 80) {
22975                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22976                 }
22977                 innerHTML  += toadd;
22978                 
22979                 i++;
22980                 currentElementChild = currentElement.childNodes.item(i);
22981                 lastNode = '';
22982                 continue;
22983             }
22984             allText = false;
22985             
22986             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22987                 
22988             // Recursively traverse the tree structure of the child node
22989             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22990             lastnode = currentElementChild.nodeName;
22991             i++;
22992             currentElementChild=currentElement.childNodes.item(i);
22993         }
22994         
22995         ret += innerHTML;
22996         
22997         if (!allText) {
22998                 // The remaining code is mostly for formatting the tree
22999             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23000         }
23001         
23002         
23003         if (tagName) {
23004             ret+= "</"+tagName+">";
23005         }
23006         return ret;
23007         
23008     },
23009         
23010     applyBlacklists : function()
23011     {
23012         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23013         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23014         
23015         this.white = [];
23016         this.black = [];
23017         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23018             if (b.indexOf(tag) > -1) {
23019                 return;
23020             }
23021             this.white.push(tag);
23022             
23023         }, this);
23024         
23025         Roo.each(w, function(tag) {
23026             if (b.indexOf(tag) > -1) {
23027                 return;
23028             }
23029             if (this.white.indexOf(tag) > -1) {
23030                 return;
23031             }
23032             this.white.push(tag);
23033             
23034         }, this);
23035         
23036         
23037         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23038             if (w.indexOf(tag) > -1) {
23039                 return;
23040             }
23041             this.black.push(tag);
23042             
23043         }, this);
23044         
23045         Roo.each(b, function(tag) {
23046             if (w.indexOf(tag) > -1) {
23047                 return;
23048             }
23049             if (this.black.indexOf(tag) > -1) {
23050                 return;
23051             }
23052             this.black.push(tag);
23053             
23054         }, this);
23055         
23056         
23057         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23058         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23059         
23060         this.cwhite = [];
23061         this.cblack = [];
23062         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23063             if (b.indexOf(tag) > -1) {
23064                 return;
23065             }
23066             this.cwhite.push(tag);
23067             
23068         }, this);
23069         
23070         Roo.each(w, function(tag) {
23071             if (b.indexOf(tag) > -1) {
23072                 return;
23073             }
23074             if (this.cwhite.indexOf(tag) > -1) {
23075                 return;
23076             }
23077             this.cwhite.push(tag);
23078             
23079         }, this);
23080         
23081         
23082         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23083             if (w.indexOf(tag) > -1) {
23084                 return;
23085             }
23086             this.cblack.push(tag);
23087             
23088         }, this);
23089         
23090         Roo.each(b, function(tag) {
23091             if (w.indexOf(tag) > -1) {
23092                 return;
23093             }
23094             if (this.cblack.indexOf(tag) > -1) {
23095                 return;
23096             }
23097             this.cblack.push(tag);
23098             
23099         }, this);
23100     },
23101     
23102     setStylesheets : function(stylesheets)
23103     {
23104         if(typeof(stylesheets) == 'string'){
23105             Roo.get(this.iframe.contentDocument.head).createChild({
23106                 tag : 'link',
23107                 rel : 'stylesheet',
23108                 type : 'text/css',
23109                 href : stylesheets
23110             });
23111             
23112             return;
23113         }
23114         var _this = this;
23115      
23116         Roo.each(stylesheets, function(s) {
23117             if(!s.length){
23118                 return;
23119             }
23120             
23121             Roo.get(_this.iframe.contentDocument.head).createChild({
23122                 tag : 'link',
23123                 rel : 'stylesheet',
23124                 type : 'text/css',
23125                 href : s
23126             });
23127         });
23128
23129         
23130     },
23131     
23132     removeStylesheets : function()
23133     {
23134         var _this = this;
23135         
23136         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23137             s.remove();
23138         });
23139     },
23140     
23141     setStyle : function(style)
23142     {
23143         Roo.get(this.iframe.contentDocument.head).createChild({
23144             tag : 'style',
23145             type : 'text/css',
23146             html : style
23147         });
23148
23149         return;
23150     }
23151     
23152     // hide stuff that is not compatible
23153     /**
23154      * @event blur
23155      * @hide
23156      */
23157     /**
23158      * @event change
23159      * @hide
23160      */
23161     /**
23162      * @event focus
23163      * @hide
23164      */
23165     /**
23166      * @event specialkey
23167      * @hide
23168      */
23169     /**
23170      * @cfg {String} fieldClass @hide
23171      */
23172     /**
23173      * @cfg {String} focusClass @hide
23174      */
23175     /**
23176      * @cfg {String} autoCreate @hide
23177      */
23178     /**
23179      * @cfg {String} inputType @hide
23180      */
23181     /**
23182      * @cfg {String} invalidClass @hide
23183      */
23184     /**
23185      * @cfg {String} invalidText @hide
23186      */
23187     /**
23188      * @cfg {String} msgFx @hide
23189      */
23190     /**
23191      * @cfg {String} validateOnBlur @hide
23192      */
23193 });
23194
23195 Roo.HtmlEditorCore.white = [
23196         'area', 'br', 'img', 'input', 'hr', 'wbr',
23197         
23198        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23199        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23200        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23201        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23202        'table',   'ul',         'xmp', 
23203        
23204        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23205       'thead',   'tr', 
23206      
23207       'dir', 'menu', 'ol', 'ul', 'dl',
23208        
23209       'embed',  'object'
23210 ];
23211
23212
23213 Roo.HtmlEditorCore.black = [
23214     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23215         'applet', // 
23216         'base',   'basefont', 'bgsound', 'blink',  'body', 
23217         'frame',  'frameset', 'head',    'html',   'ilayer', 
23218         'iframe', 'layer',  'link',     'meta',    'object',   
23219         'script', 'style' ,'title',  'xml' // clean later..
23220 ];
23221 Roo.HtmlEditorCore.clean = [
23222     'script', 'style', 'title', 'xml'
23223 ];
23224 Roo.HtmlEditorCore.remove = [
23225     'font'
23226 ];
23227 // attributes..
23228
23229 Roo.HtmlEditorCore.ablack = [
23230     'on'
23231 ];
23232     
23233 Roo.HtmlEditorCore.aclean = [ 
23234     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23235 ];
23236
23237 // protocols..
23238 Roo.HtmlEditorCore.pwhite= [
23239         'http',  'https',  'mailto'
23240 ];
23241
23242 // white listed style attributes.
23243 Roo.HtmlEditorCore.cwhite= [
23244       //  'text-align', /// default is to allow most things..
23245       
23246          
23247 //        'font-size'//??
23248 ];
23249
23250 // black listed style attributes.
23251 Roo.HtmlEditorCore.cblack= [
23252       //  'font-size' -- this can be set by the project 
23253 ];
23254
23255
23256 Roo.HtmlEditorCore.swapCodes   =[ 
23257     [    8211, "--" ], 
23258     [    8212, "--" ], 
23259     [    8216,  "'" ],  
23260     [    8217, "'" ],  
23261     [    8220, '"' ],  
23262     [    8221, '"' ],  
23263     [    8226, "*" ],  
23264     [    8230, "..." ]
23265 ]; 
23266
23267     /*
23268  * - LGPL
23269  *
23270  * HtmlEditor
23271  * 
23272  */
23273
23274 /**
23275  * @class Roo.bootstrap.HtmlEditor
23276  * @extends Roo.bootstrap.TextArea
23277  * Bootstrap HtmlEditor class
23278
23279  * @constructor
23280  * Create a new HtmlEditor
23281  * @param {Object} config The config object
23282  */
23283
23284 Roo.bootstrap.HtmlEditor = function(config){
23285     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23286     if (!this.toolbars) {
23287         this.toolbars = [];
23288     }
23289     
23290     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23291     this.addEvents({
23292             /**
23293              * @event initialize
23294              * Fires when the editor is fully initialized (including the iframe)
23295              * @param {HtmlEditor} this
23296              */
23297             initialize: true,
23298             /**
23299              * @event activate
23300              * Fires when the editor is first receives the focus. Any insertion must wait
23301              * until after this event.
23302              * @param {HtmlEditor} this
23303              */
23304             activate: true,
23305              /**
23306              * @event beforesync
23307              * Fires before the textarea is updated with content from the editor iframe. Return false
23308              * to cancel the sync.
23309              * @param {HtmlEditor} this
23310              * @param {String} html
23311              */
23312             beforesync: true,
23313              /**
23314              * @event beforepush
23315              * Fires before the iframe editor is updated with content from the textarea. Return false
23316              * to cancel the push.
23317              * @param {HtmlEditor} this
23318              * @param {String} html
23319              */
23320             beforepush: true,
23321              /**
23322              * @event sync
23323              * Fires when the textarea is updated with content from the editor iframe.
23324              * @param {HtmlEditor} this
23325              * @param {String} html
23326              */
23327             sync: true,
23328              /**
23329              * @event push
23330              * Fires when the iframe editor is updated with content from the textarea.
23331              * @param {HtmlEditor} this
23332              * @param {String} html
23333              */
23334             push: true,
23335              /**
23336              * @event editmodechange
23337              * Fires when the editor switches edit modes
23338              * @param {HtmlEditor} this
23339              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23340              */
23341             editmodechange: true,
23342             /**
23343              * @event editorevent
23344              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23345              * @param {HtmlEditor} this
23346              */
23347             editorevent: true,
23348             /**
23349              * @event firstfocus
23350              * Fires when on first focus - needed by toolbars..
23351              * @param {HtmlEditor} this
23352              */
23353             firstfocus: true,
23354             /**
23355              * @event autosave
23356              * Auto save the htmlEditor value as a file into Events
23357              * @param {HtmlEditor} this
23358              */
23359             autosave: true,
23360             /**
23361              * @event savedpreview
23362              * preview the saved version of htmlEditor
23363              * @param {HtmlEditor} this
23364              */
23365             savedpreview: true
23366         });
23367 };
23368
23369
23370 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23371     
23372     
23373       /**
23374      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23375      */
23376     toolbars : false,
23377     
23378      /**
23379     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23380     */
23381     btns : [],
23382    
23383      /**
23384      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23385      *                        Roo.resizable.
23386      */
23387     resizable : false,
23388      /**
23389      * @cfg {Number} height (in pixels)
23390      */   
23391     height: 300,
23392    /**
23393      * @cfg {Number} width (in pixels)
23394      */   
23395     width: false,
23396     
23397     /**
23398      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23399      * 
23400      */
23401     stylesheets: false,
23402     
23403     // id of frame..
23404     frameId: false,
23405     
23406     // private properties
23407     validationEvent : false,
23408     deferHeight: true,
23409     initialized : false,
23410     activated : false,
23411     
23412     onFocus : Roo.emptyFn,
23413     iframePad:3,
23414     hideMode:'offsets',
23415     
23416     tbContainer : false,
23417     
23418     bodyCls : '',
23419     
23420     toolbarContainer :function() {
23421         return this.wrap.select('.x-html-editor-tb',true).first();
23422     },
23423
23424     /**
23425      * Protected method that will not generally be called directly. It
23426      * is called when the editor creates its toolbar. Override this method if you need to
23427      * add custom toolbar buttons.
23428      * @param {HtmlEditor} editor
23429      */
23430     createToolbar : function(){
23431         Roo.log('renewing');
23432         Roo.log("create toolbars");
23433         
23434         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23435         this.toolbars[0].render(this.toolbarContainer());
23436         
23437         return;
23438         
23439 //        if (!editor.toolbars || !editor.toolbars.length) {
23440 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23441 //        }
23442 //        
23443 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23444 //            editor.toolbars[i] = Roo.factory(
23445 //                    typeof(editor.toolbars[i]) == 'string' ?
23446 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23447 //                Roo.bootstrap.HtmlEditor);
23448 //            editor.toolbars[i].init(editor);
23449 //        }
23450     },
23451
23452      
23453     // private
23454     onRender : function(ct, position)
23455     {
23456        // Roo.log("Call onRender: " + this.xtype);
23457         var _t = this;
23458         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23459       
23460         this.wrap = this.inputEl().wrap({
23461             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23462         });
23463         
23464         this.editorcore.onRender(ct, position);
23465          
23466         if (this.resizable) {
23467             this.resizeEl = new Roo.Resizable(this.wrap, {
23468                 pinned : true,
23469                 wrap: true,
23470                 dynamic : true,
23471                 minHeight : this.height,
23472                 height: this.height,
23473                 handles : this.resizable,
23474                 width: this.width,
23475                 listeners : {
23476                     resize : function(r, w, h) {
23477                         _t.onResize(w,h); // -something
23478                     }
23479                 }
23480             });
23481             
23482         }
23483         this.createToolbar(this);
23484        
23485         
23486         if(!this.width && this.resizable){
23487             this.setSize(this.wrap.getSize());
23488         }
23489         if (this.resizeEl) {
23490             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23491             // should trigger onReize..
23492         }
23493         
23494     },
23495
23496     // private
23497     onResize : function(w, h)
23498     {
23499         Roo.log('resize: ' +w + ',' + h );
23500         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23501         var ew = false;
23502         var eh = false;
23503         
23504         if(this.inputEl() ){
23505             if(typeof w == 'number'){
23506                 var aw = w - this.wrap.getFrameWidth('lr');
23507                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23508                 ew = aw;
23509             }
23510             if(typeof h == 'number'){
23511                  var tbh = -11;  // fixme it needs to tool bar size!
23512                 for (var i =0; i < this.toolbars.length;i++) {
23513                     // fixme - ask toolbars for heights?
23514                     tbh += this.toolbars[i].el.getHeight();
23515                     //if (this.toolbars[i].footer) {
23516                     //    tbh += this.toolbars[i].footer.el.getHeight();
23517                     //}
23518                 }
23519               
23520                 
23521                 
23522                 
23523                 
23524                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23525                 ah -= 5; // knock a few pixes off for look..
23526                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23527                 var eh = ah;
23528             }
23529         }
23530         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23531         this.editorcore.onResize(ew,eh);
23532         
23533     },
23534
23535     /**
23536      * Toggles the editor between standard and source edit mode.
23537      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23538      */
23539     toggleSourceEdit : function(sourceEditMode)
23540     {
23541         this.editorcore.toggleSourceEdit(sourceEditMode);
23542         
23543         if(this.editorcore.sourceEditMode){
23544             Roo.log('editor - showing textarea');
23545             
23546 //            Roo.log('in');
23547 //            Roo.log(this.syncValue());
23548             this.syncValue();
23549             this.inputEl().removeClass(['hide', 'x-hidden']);
23550             this.inputEl().dom.removeAttribute('tabIndex');
23551             this.inputEl().focus();
23552         }else{
23553             Roo.log('editor - hiding textarea');
23554 //            Roo.log('out')
23555 //            Roo.log(this.pushValue()); 
23556             this.pushValue();
23557             
23558             this.inputEl().addClass(['hide', 'x-hidden']);
23559             this.inputEl().dom.setAttribute('tabIndex', -1);
23560             //this.deferFocus();
23561         }
23562          
23563         if(this.resizable){
23564             this.setSize(this.wrap.getSize());
23565         }
23566         
23567         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23568     },
23569  
23570     // private (for BoxComponent)
23571     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23572
23573     // private (for BoxComponent)
23574     getResizeEl : function(){
23575         return this.wrap;
23576     },
23577
23578     // private (for BoxComponent)
23579     getPositionEl : function(){
23580         return this.wrap;
23581     },
23582
23583     // private
23584     initEvents : function(){
23585         this.originalValue = this.getValue();
23586     },
23587
23588 //    /**
23589 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23590 //     * @method
23591 //     */
23592 //    markInvalid : Roo.emptyFn,
23593 //    /**
23594 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23595 //     * @method
23596 //     */
23597 //    clearInvalid : Roo.emptyFn,
23598
23599     setValue : function(v){
23600         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23601         this.editorcore.pushValue();
23602     },
23603
23604      
23605     // private
23606     deferFocus : function(){
23607         this.focus.defer(10, this);
23608     },
23609
23610     // doc'ed in Field
23611     focus : function(){
23612         this.editorcore.focus();
23613         
23614     },
23615       
23616
23617     // private
23618     onDestroy : function(){
23619         
23620         
23621         
23622         if(this.rendered){
23623             
23624             for (var i =0; i < this.toolbars.length;i++) {
23625                 // fixme - ask toolbars for heights?
23626                 this.toolbars[i].onDestroy();
23627             }
23628             
23629             this.wrap.dom.innerHTML = '';
23630             this.wrap.remove();
23631         }
23632     },
23633
23634     // private
23635     onFirstFocus : function(){
23636         //Roo.log("onFirstFocus");
23637         this.editorcore.onFirstFocus();
23638          for (var i =0; i < this.toolbars.length;i++) {
23639             this.toolbars[i].onFirstFocus();
23640         }
23641         
23642     },
23643     
23644     // private
23645     syncValue : function()
23646     {   
23647         this.editorcore.syncValue();
23648     },
23649     
23650     pushValue : function()
23651     {   
23652         this.editorcore.pushValue();
23653     }
23654      
23655     
23656     // hide stuff that is not compatible
23657     /**
23658      * @event blur
23659      * @hide
23660      */
23661     /**
23662      * @event change
23663      * @hide
23664      */
23665     /**
23666      * @event focus
23667      * @hide
23668      */
23669     /**
23670      * @event specialkey
23671      * @hide
23672      */
23673     /**
23674      * @cfg {String} fieldClass @hide
23675      */
23676     /**
23677      * @cfg {String} focusClass @hide
23678      */
23679     /**
23680      * @cfg {String} autoCreate @hide
23681      */
23682     /**
23683      * @cfg {String} inputType @hide
23684      */
23685     /**
23686      * @cfg {String} invalidClass @hide
23687      */
23688     /**
23689      * @cfg {String} invalidText @hide
23690      */
23691     /**
23692      * @cfg {String} msgFx @hide
23693      */
23694     /**
23695      * @cfg {String} validateOnBlur @hide
23696      */
23697 });
23698  
23699     
23700    
23701    
23702    
23703       
23704 Roo.namespace('Roo.bootstrap.htmleditor');
23705 /**
23706  * @class Roo.bootstrap.HtmlEditorToolbar1
23707  * Basic Toolbar
23708  * 
23709  * Usage:
23710  *
23711  new Roo.bootstrap.HtmlEditor({
23712     ....
23713     toolbars : [
23714         new Roo.bootstrap.HtmlEditorToolbar1({
23715             disable : { fonts: 1 , format: 1, ..., ... , ...],
23716             btns : [ .... ]
23717         })
23718     }
23719      
23720  * 
23721  * @cfg {Object} disable List of elements to disable..
23722  * @cfg {Array} btns List of additional buttons.
23723  * 
23724  * 
23725  * NEEDS Extra CSS? 
23726  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23727  */
23728  
23729 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23730 {
23731     
23732     Roo.apply(this, config);
23733     
23734     // default disabled, based on 'good practice'..
23735     this.disable = this.disable || {};
23736     Roo.applyIf(this.disable, {
23737         fontSize : true,
23738         colors : true,
23739         specialElements : true
23740     });
23741     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23742     
23743     this.editor = config.editor;
23744     this.editorcore = config.editor.editorcore;
23745     
23746     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23747     
23748     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23749     // dont call parent... till later.
23750 }
23751 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23752      
23753     bar : true,
23754     
23755     editor : false,
23756     editorcore : false,
23757     
23758     
23759     formats : [
23760         "p" ,  
23761         "h1","h2","h3","h4","h5","h6", 
23762         "pre", "code", 
23763         "abbr", "acronym", "address", "cite", "samp", "var",
23764         'div','span'
23765     ],
23766     
23767     onRender : function(ct, position)
23768     {
23769        // Roo.log("Call onRender: " + this.xtype);
23770         
23771        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23772        Roo.log(this.el);
23773        this.el.dom.style.marginBottom = '0';
23774        var _this = this;
23775        var editorcore = this.editorcore;
23776        var editor= this.editor;
23777        
23778        var children = [];
23779        var btn = function(id,cmd , toggle, handler, html){
23780        
23781             var  event = toggle ? 'toggle' : 'click';
23782        
23783             var a = {
23784                 size : 'sm',
23785                 xtype: 'Button',
23786                 xns: Roo.bootstrap,
23787                 glyphicon : id,
23788                 cmd : id || cmd,
23789                 enableToggle:toggle !== false,
23790                 html : html || '',
23791                 pressed : toggle ? false : null,
23792                 listeners : {}
23793             };
23794             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23795                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23796             };
23797             children.push(a);
23798             return a;
23799        }
23800        
23801     //    var cb_box = function...
23802         
23803         var style = {
23804                 xtype: 'Button',
23805                 size : 'sm',
23806                 xns: Roo.bootstrap,
23807                 glyphicon : 'font',
23808                 //html : 'submit'
23809                 menu : {
23810                     xtype: 'Menu',
23811                     xns: Roo.bootstrap,
23812                     items:  []
23813                 }
23814         };
23815         Roo.each(this.formats, function(f) {
23816             style.menu.items.push({
23817                 xtype :'MenuItem',
23818                 xns: Roo.bootstrap,
23819                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23820                 tagname : f,
23821                 listeners : {
23822                     click : function()
23823                     {
23824                         editorcore.insertTag(this.tagname);
23825                         editor.focus();
23826                     }
23827                 }
23828                 
23829             });
23830         });
23831         children.push(style);   
23832         
23833         btn('bold',false,true);
23834         btn('italic',false,true);
23835         btn('align-left', 'justifyleft',true);
23836         btn('align-center', 'justifycenter',true);
23837         btn('align-right' , 'justifyright',true);
23838         btn('link', false, false, function(btn) {
23839             //Roo.log("create link?");
23840             var url = prompt(this.createLinkText, this.defaultLinkValue);
23841             if(url && url != 'http:/'+'/'){
23842                 this.editorcore.relayCmd('createlink', url);
23843             }
23844         }),
23845         btn('list','insertunorderedlist',true);
23846         btn('pencil', false,true, function(btn){
23847                 Roo.log(this);
23848                 this.toggleSourceEdit(btn.pressed);
23849         });
23850         
23851         if (this.editor.btns.length > 0) {
23852             for (var i = 0; i<this.editor.btns.length; i++) {
23853                 children.push(this.editor.btns[i]);
23854             }
23855         }
23856         
23857         /*
23858         var cog = {
23859                 xtype: 'Button',
23860                 size : 'sm',
23861                 xns: Roo.bootstrap,
23862                 glyphicon : 'cog',
23863                 //html : 'submit'
23864                 menu : {
23865                     xtype: 'Menu',
23866                     xns: Roo.bootstrap,
23867                     items:  []
23868                 }
23869         };
23870         
23871         cog.menu.items.push({
23872             xtype :'MenuItem',
23873             xns: Roo.bootstrap,
23874             html : Clean styles,
23875             tagname : f,
23876             listeners : {
23877                 click : function()
23878                 {
23879                     editorcore.insertTag(this.tagname);
23880                     editor.focus();
23881                 }
23882             }
23883             
23884         });
23885        */
23886         
23887          
23888        this.xtype = 'NavSimplebar';
23889         
23890         for(var i=0;i< children.length;i++) {
23891             
23892             this.buttons.add(this.addxtypeChild(children[i]));
23893             
23894         }
23895         
23896         editor.on('editorevent', this.updateToolbar, this);
23897     },
23898     onBtnClick : function(id)
23899     {
23900        this.editorcore.relayCmd(id);
23901        this.editorcore.focus();
23902     },
23903     
23904     /**
23905      * Protected method that will not generally be called directly. It triggers
23906      * a toolbar update by reading the markup state of the current selection in the editor.
23907      */
23908     updateToolbar: function(){
23909
23910         if(!this.editorcore.activated){
23911             this.editor.onFirstFocus(); // is this neeed?
23912             return;
23913         }
23914
23915         var btns = this.buttons; 
23916         var doc = this.editorcore.doc;
23917         btns.get('bold').setActive(doc.queryCommandState('bold'));
23918         btns.get('italic').setActive(doc.queryCommandState('italic'));
23919         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23920         
23921         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23922         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23923         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23924         
23925         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23926         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23927          /*
23928         
23929         var ans = this.editorcore.getAllAncestors();
23930         if (this.formatCombo) {
23931             
23932             
23933             var store = this.formatCombo.store;
23934             this.formatCombo.setValue("");
23935             for (var i =0; i < ans.length;i++) {
23936                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23937                     // select it..
23938                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23939                     break;
23940                 }
23941             }
23942         }
23943         
23944         
23945         
23946         // hides menus... - so this cant be on a menu...
23947         Roo.bootstrap.MenuMgr.hideAll();
23948         */
23949         Roo.bootstrap.MenuMgr.hideAll();
23950         //this.editorsyncValue();
23951     },
23952     onFirstFocus: function() {
23953         this.buttons.each(function(item){
23954            item.enable();
23955         });
23956     },
23957     toggleSourceEdit : function(sourceEditMode){
23958         
23959           
23960         if(sourceEditMode){
23961             Roo.log("disabling buttons");
23962            this.buttons.each( function(item){
23963                 if(item.cmd != 'pencil'){
23964                     item.disable();
23965                 }
23966             });
23967           
23968         }else{
23969             Roo.log("enabling buttons");
23970             if(this.editorcore.initialized){
23971                 this.buttons.each( function(item){
23972                     item.enable();
23973                 });
23974             }
23975             
23976         }
23977         Roo.log("calling toggole on editor");
23978         // tell the editor that it's been pressed..
23979         this.editor.toggleSourceEdit(sourceEditMode);
23980        
23981     }
23982 });
23983
23984
23985
23986
23987
23988 /**
23989  * @class Roo.bootstrap.Table.AbstractSelectionModel
23990  * @extends Roo.util.Observable
23991  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23992  * implemented by descendant classes.  This class should not be directly instantiated.
23993  * @constructor
23994  */
23995 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23996     this.locked = false;
23997     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23998 };
23999
24000
24001 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24002     /** @ignore Called by the grid automatically. Do not call directly. */
24003     init : function(grid){
24004         this.grid = grid;
24005         this.initEvents();
24006     },
24007
24008     /**
24009      * Locks the selections.
24010      */
24011     lock : function(){
24012         this.locked = true;
24013     },
24014
24015     /**
24016      * Unlocks the selections.
24017      */
24018     unlock : function(){
24019         this.locked = false;
24020     },
24021
24022     /**
24023      * Returns true if the selections are locked.
24024      * @return {Boolean}
24025      */
24026     isLocked : function(){
24027         return this.locked;
24028     }
24029 });
24030 /**
24031  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24032  * @class Roo.bootstrap.Table.RowSelectionModel
24033  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24034  * It supports multiple selections and keyboard selection/navigation. 
24035  * @constructor
24036  * @param {Object} config
24037  */
24038
24039 Roo.bootstrap.Table.RowSelectionModel = function(config){
24040     Roo.apply(this, config);
24041     this.selections = new Roo.util.MixedCollection(false, function(o){
24042         return o.id;
24043     });
24044
24045     this.last = false;
24046     this.lastActive = false;
24047
24048     this.addEvents({
24049         /**
24050              * @event selectionchange
24051              * Fires when the selection changes
24052              * @param {SelectionModel} this
24053              */
24054             "selectionchange" : true,
24055         /**
24056              * @event afterselectionchange
24057              * Fires after the selection changes (eg. by key press or clicking)
24058              * @param {SelectionModel} this
24059              */
24060             "afterselectionchange" : true,
24061         /**
24062              * @event beforerowselect
24063              * Fires when a row is selected being selected, return false to cancel.
24064              * @param {SelectionModel} this
24065              * @param {Number} rowIndex The selected index
24066              * @param {Boolean} keepExisting False if other selections will be cleared
24067              */
24068             "beforerowselect" : true,
24069         /**
24070              * @event rowselect
24071              * Fires when a row is selected.
24072              * @param {SelectionModel} this
24073              * @param {Number} rowIndex The selected index
24074              * @param {Roo.data.Record} r The record
24075              */
24076             "rowselect" : true,
24077         /**
24078              * @event rowdeselect
24079              * Fires when a row is deselected.
24080              * @param {SelectionModel} this
24081              * @param {Number} rowIndex The selected index
24082              */
24083         "rowdeselect" : true
24084     });
24085     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24086     this.locked = false;
24087  };
24088
24089 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24090     /**
24091      * @cfg {Boolean} singleSelect
24092      * True to allow selection of only one row at a time (defaults to false)
24093      */
24094     singleSelect : false,
24095
24096     // private
24097     initEvents : function()
24098     {
24099
24100         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24101         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24102         //}else{ // allow click to work like normal
24103          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24104         //}
24105         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24106         this.grid.on("rowclick", this.handleMouseDown, this);
24107         
24108         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24109             "up" : function(e){
24110                 if(!e.shiftKey){
24111                     this.selectPrevious(e.shiftKey);
24112                 }else if(this.last !== false && this.lastActive !== false){
24113                     var last = this.last;
24114                     this.selectRange(this.last,  this.lastActive-1);
24115                     this.grid.getView().focusRow(this.lastActive);
24116                     if(last !== false){
24117                         this.last = last;
24118                     }
24119                 }else{
24120                     this.selectFirstRow();
24121                 }
24122                 this.fireEvent("afterselectionchange", this);
24123             },
24124             "down" : function(e){
24125                 if(!e.shiftKey){
24126                     this.selectNext(e.shiftKey);
24127                 }else if(this.last !== false && this.lastActive !== false){
24128                     var last = this.last;
24129                     this.selectRange(this.last,  this.lastActive+1);
24130                     this.grid.getView().focusRow(this.lastActive);
24131                     if(last !== false){
24132                         this.last = last;
24133                     }
24134                 }else{
24135                     this.selectFirstRow();
24136                 }
24137                 this.fireEvent("afterselectionchange", this);
24138             },
24139             scope: this
24140         });
24141         this.grid.store.on('load', function(){
24142             this.selections.clear();
24143         },this);
24144         /*
24145         var view = this.grid.view;
24146         view.on("refresh", this.onRefresh, this);
24147         view.on("rowupdated", this.onRowUpdated, this);
24148         view.on("rowremoved", this.onRemove, this);
24149         */
24150     },
24151
24152     // private
24153     onRefresh : function()
24154     {
24155         var ds = this.grid.store, i, v = this.grid.view;
24156         var s = this.selections;
24157         s.each(function(r){
24158             if((i = ds.indexOfId(r.id)) != -1){
24159                 v.onRowSelect(i);
24160             }else{
24161                 s.remove(r);
24162             }
24163         });
24164     },
24165
24166     // private
24167     onRemove : function(v, index, r){
24168         this.selections.remove(r);
24169     },
24170
24171     // private
24172     onRowUpdated : function(v, index, r){
24173         if(this.isSelected(r)){
24174             v.onRowSelect(index);
24175         }
24176     },
24177
24178     /**
24179      * Select records.
24180      * @param {Array} records The records to select
24181      * @param {Boolean} keepExisting (optional) True to keep existing selections
24182      */
24183     selectRecords : function(records, keepExisting)
24184     {
24185         if(!keepExisting){
24186             this.clearSelections();
24187         }
24188             var ds = this.grid.store;
24189         for(var i = 0, len = records.length; i < len; i++){
24190             this.selectRow(ds.indexOf(records[i]), true);
24191         }
24192     },
24193
24194     /**
24195      * Gets the number of selected rows.
24196      * @return {Number}
24197      */
24198     getCount : function(){
24199         return this.selections.length;
24200     },
24201
24202     /**
24203      * Selects the first row in the grid.
24204      */
24205     selectFirstRow : function(){
24206         this.selectRow(0);
24207     },
24208
24209     /**
24210      * Select the last row.
24211      * @param {Boolean} keepExisting (optional) True to keep existing selections
24212      */
24213     selectLastRow : function(keepExisting){
24214         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24215         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24216     },
24217
24218     /**
24219      * Selects the row immediately following the last selected row.
24220      * @param {Boolean} keepExisting (optional) True to keep existing selections
24221      */
24222     selectNext : function(keepExisting)
24223     {
24224             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24225             this.selectRow(this.last+1, keepExisting);
24226             this.grid.getView().focusRow(this.last);
24227         }
24228     },
24229
24230     /**
24231      * Selects the row that precedes the last selected row.
24232      * @param {Boolean} keepExisting (optional) True to keep existing selections
24233      */
24234     selectPrevious : function(keepExisting){
24235         if(this.last){
24236             this.selectRow(this.last-1, keepExisting);
24237             this.grid.getView().focusRow(this.last);
24238         }
24239     },
24240
24241     /**
24242      * Returns the selected records
24243      * @return {Array} Array of selected records
24244      */
24245     getSelections : function(){
24246         return [].concat(this.selections.items);
24247     },
24248
24249     /**
24250      * Returns the first selected record.
24251      * @return {Record}
24252      */
24253     getSelected : function(){
24254         return this.selections.itemAt(0);
24255     },
24256
24257
24258     /**
24259      * Clears all selections.
24260      */
24261     clearSelections : function(fast)
24262     {
24263         if(this.locked) {
24264             return;
24265         }
24266         if(fast !== true){
24267                 var ds = this.grid.store;
24268             var s = this.selections;
24269             s.each(function(r){
24270                 this.deselectRow(ds.indexOfId(r.id));
24271             }, this);
24272             s.clear();
24273         }else{
24274             this.selections.clear();
24275         }
24276         this.last = false;
24277     },
24278
24279
24280     /**
24281      * Selects all rows.
24282      */
24283     selectAll : function(){
24284         if(this.locked) {
24285             return;
24286         }
24287         this.selections.clear();
24288         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24289             this.selectRow(i, true);
24290         }
24291     },
24292
24293     /**
24294      * Returns True if there is a selection.
24295      * @return {Boolean}
24296      */
24297     hasSelection : function(){
24298         return this.selections.length > 0;
24299     },
24300
24301     /**
24302      * Returns True if the specified row is selected.
24303      * @param {Number/Record} record The record or index of the record to check
24304      * @return {Boolean}
24305      */
24306     isSelected : function(index){
24307             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24308         return (r && this.selections.key(r.id) ? true : false);
24309     },
24310
24311     /**
24312      * Returns True if the specified record id is selected.
24313      * @param {String} id The id of record to check
24314      * @return {Boolean}
24315      */
24316     isIdSelected : function(id){
24317         return (this.selections.key(id) ? true : false);
24318     },
24319
24320
24321     // private
24322     handleMouseDBClick : function(e, t){
24323         
24324     },
24325     // private
24326     handleMouseDown : function(e, t)
24327     {
24328             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24329         if(this.isLocked() || rowIndex < 0 ){
24330             return;
24331         };
24332         if(e.shiftKey && this.last !== false){
24333             var last = this.last;
24334             this.selectRange(last, rowIndex, e.ctrlKey);
24335             this.last = last; // reset the last
24336             t.focus();
24337     
24338         }else{
24339             var isSelected = this.isSelected(rowIndex);
24340             //Roo.log("select row:" + rowIndex);
24341             if(isSelected){
24342                 this.deselectRow(rowIndex);
24343             } else {
24344                         this.selectRow(rowIndex, true);
24345             }
24346     
24347             /*
24348                 if(e.button !== 0 && isSelected){
24349                 alert('rowIndex 2: ' + rowIndex);
24350                     view.focusRow(rowIndex);
24351                 }else if(e.ctrlKey && isSelected){
24352                     this.deselectRow(rowIndex);
24353                 }else if(!isSelected){
24354                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24355                     view.focusRow(rowIndex);
24356                 }
24357             */
24358         }
24359         this.fireEvent("afterselectionchange", this);
24360     },
24361     // private
24362     handleDragableRowClick :  function(grid, rowIndex, e) 
24363     {
24364         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24365             this.selectRow(rowIndex, false);
24366             grid.view.focusRow(rowIndex);
24367              this.fireEvent("afterselectionchange", this);
24368         }
24369     },
24370     
24371     /**
24372      * Selects multiple rows.
24373      * @param {Array} rows Array of the indexes of the row to select
24374      * @param {Boolean} keepExisting (optional) True to keep existing selections
24375      */
24376     selectRows : function(rows, keepExisting){
24377         if(!keepExisting){
24378             this.clearSelections();
24379         }
24380         for(var i = 0, len = rows.length; i < len; i++){
24381             this.selectRow(rows[i], true);
24382         }
24383     },
24384
24385     /**
24386      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24387      * @param {Number} startRow The index of the first row in the range
24388      * @param {Number} endRow The index of the last row in the range
24389      * @param {Boolean} keepExisting (optional) True to retain existing selections
24390      */
24391     selectRange : function(startRow, endRow, keepExisting){
24392         if(this.locked) {
24393             return;
24394         }
24395         if(!keepExisting){
24396             this.clearSelections();
24397         }
24398         if(startRow <= endRow){
24399             for(var i = startRow; i <= endRow; i++){
24400                 this.selectRow(i, true);
24401             }
24402         }else{
24403             for(var i = startRow; i >= endRow; i--){
24404                 this.selectRow(i, true);
24405             }
24406         }
24407     },
24408
24409     /**
24410      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24411      * @param {Number} startRow The index of the first row in the range
24412      * @param {Number} endRow The index of the last row in the range
24413      */
24414     deselectRange : function(startRow, endRow, preventViewNotify){
24415         if(this.locked) {
24416             return;
24417         }
24418         for(var i = startRow; i <= endRow; i++){
24419             this.deselectRow(i, preventViewNotify);
24420         }
24421     },
24422
24423     /**
24424      * Selects a row.
24425      * @param {Number} row The index of the row to select
24426      * @param {Boolean} keepExisting (optional) True to keep existing selections
24427      */
24428     selectRow : function(index, keepExisting, preventViewNotify)
24429     {
24430             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24431             return;
24432         }
24433         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24434             if(!keepExisting || this.singleSelect){
24435                 this.clearSelections();
24436             }
24437             
24438             var r = this.grid.store.getAt(index);
24439             //console.log('selectRow - record id :' + r.id);
24440             
24441             this.selections.add(r);
24442             this.last = this.lastActive = index;
24443             if(!preventViewNotify){
24444                 var proxy = new Roo.Element(
24445                                 this.grid.getRowDom(index)
24446                 );
24447                 proxy.addClass('bg-info info');
24448             }
24449             this.fireEvent("rowselect", this, index, r);
24450             this.fireEvent("selectionchange", this);
24451         }
24452     },
24453
24454     /**
24455      * Deselects a row.
24456      * @param {Number} row The index of the row to deselect
24457      */
24458     deselectRow : function(index, preventViewNotify)
24459     {
24460         if(this.locked) {
24461             return;
24462         }
24463         if(this.last == index){
24464             this.last = false;
24465         }
24466         if(this.lastActive == index){
24467             this.lastActive = false;
24468         }
24469         
24470         var r = this.grid.store.getAt(index);
24471         if (!r) {
24472             return;
24473         }
24474         
24475         this.selections.remove(r);
24476         //.console.log('deselectRow - record id :' + r.id);
24477         if(!preventViewNotify){
24478         
24479             var proxy = new Roo.Element(
24480                 this.grid.getRowDom(index)
24481             );
24482             proxy.removeClass('bg-info info');
24483         }
24484         this.fireEvent("rowdeselect", this, index);
24485         this.fireEvent("selectionchange", this);
24486     },
24487
24488     // private
24489     restoreLast : function(){
24490         if(this._last){
24491             this.last = this._last;
24492         }
24493     },
24494
24495     // private
24496     acceptsNav : function(row, col, cm){
24497         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24498     },
24499
24500     // private
24501     onEditorKey : function(field, e){
24502         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24503         if(k == e.TAB){
24504             e.stopEvent();
24505             ed.completeEdit();
24506             if(e.shiftKey){
24507                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24508             }else{
24509                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24510             }
24511         }else if(k == e.ENTER && !e.ctrlKey){
24512             e.stopEvent();
24513             ed.completeEdit();
24514             if(e.shiftKey){
24515                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24516             }else{
24517                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24518             }
24519         }else if(k == e.ESC){
24520             ed.cancelEdit();
24521         }
24522         if(newCell){
24523             g.startEditing(newCell[0], newCell[1]);
24524         }
24525     }
24526 });
24527 /*
24528  * Based on:
24529  * Ext JS Library 1.1.1
24530  * Copyright(c) 2006-2007, Ext JS, LLC.
24531  *
24532  * Originally Released Under LGPL - original licence link has changed is not relivant.
24533  *
24534  * Fork - LGPL
24535  * <script type="text/javascript">
24536  */
24537  
24538 /**
24539  * @class Roo.bootstrap.PagingToolbar
24540  * @extends Roo.bootstrap.NavSimplebar
24541  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24542  * @constructor
24543  * Create a new PagingToolbar
24544  * @param {Object} config The config object
24545  * @param {Roo.data.Store} store
24546  */
24547 Roo.bootstrap.PagingToolbar = function(config)
24548 {
24549     // old args format still supported... - xtype is prefered..
24550         // created from xtype...
24551     
24552     this.ds = config.dataSource;
24553     
24554     if (config.store && !this.ds) {
24555         this.store= Roo.factory(config.store, Roo.data);
24556         this.ds = this.store;
24557         this.ds.xmodule = this.xmodule || false;
24558     }
24559     
24560     this.toolbarItems = [];
24561     if (config.items) {
24562         this.toolbarItems = config.items;
24563     }
24564     
24565     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24566     
24567     this.cursor = 0;
24568     
24569     if (this.ds) { 
24570         this.bind(this.ds);
24571     }
24572     
24573     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24574     
24575 };
24576
24577 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24578     /**
24579      * @cfg {Roo.data.Store} dataSource
24580      * The underlying data store providing the paged data
24581      */
24582     /**
24583      * @cfg {String/HTMLElement/Element} container
24584      * container The id or element that will contain the toolbar
24585      */
24586     /**
24587      * @cfg {Boolean} displayInfo
24588      * True to display the displayMsg (defaults to false)
24589      */
24590     /**
24591      * @cfg {Number} pageSize
24592      * The number of records to display per page (defaults to 20)
24593      */
24594     pageSize: 20,
24595     /**
24596      * @cfg {String} displayMsg
24597      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24598      */
24599     displayMsg : 'Displaying {0} - {1} of {2}',
24600     /**
24601      * @cfg {String} emptyMsg
24602      * The message to display when no records are found (defaults to "No data to display")
24603      */
24604     emptyMsg : 'No data to display',
24605     /**
24606      * Customizable piece of the default paging text (defaults to "Page")
24607      * @type String
24608      */
24609     beforePageText : "Page",
24610     /**
24611      * Customizable piece of the default paging text (defaults to "of %0")
24612      * @type String
24613      */
24614     afterPageText : "of {0}",
24615     /**
24616      * Customizable piece of the default paging text (defaults to "First Page")
24617      * @type String
24618      */
24619     firstText : "First Page",
24620     /**
24621      * Customizable piece of the default paging text (defaults to "Previous Page")
24622      * @type String
24623      */
24624     prevText : "Previous Page",
24625     /**
24626      * Customizable piece of the default paging text (defaults to "Next Page")
24627      * @type String
24628      */
24629     nextText : "Next Page",
24630     /**
24631      * Customizable piece of the default paging text (defaults to "Last Page")
24632      * @type String
24633      */
24634     lastText : "Last Page",
24635     /**
24636      * Customizable piece of the default paging text (defaults to "Refresh")
24637      * @type String
24638      */
24639     refreshText : "Refresh",
24640
24641     buttons : false,
24642     // private
24643     onRender : function(ct, position) 
24644     {
24645         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24646         this.navgroup.parentId = this.id;
24647         this.navgroup.onRender(this.el, null);
24648         // add the buttons to the navgroup
24649         
24650         if(this.displayInfo){
24651             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24652             this.displayEl = this.el.select('.x-paging-info', true).first();
24653 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24654 //            this.displayEl = navel.el.select('span',true).first();
24655         }
24656         
24657         var _this = this;
24658         
24659         if(this.buttons){
24660             Roo.each(_this.buttons, function(e){ // this might need to use render????
24661                Roo.factory(e).render(_this.el);
24662             });
24663         }
24664             
24665         Roo.each(_this.toolbarItems, function(e) {
24666             _this.navgroup.addItem(e);
24667         });
24668         
24669         
24670         this.first = this.navgroup.addItem({
24671             tooltip: this.firstText,
24672             cls: "prev",
24673             icon : 'fa fa-backward',
24674             disabled: true,
24675             preventDefault: true,
24676             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24677         });
24678         
24679         this.prev =  this.navgroup.addItem({
24680             tooltip: this.prevText,
24681             cls: "prev",
24682             icon : 'fa fa-step-backward',
24683             disabled: true,
24684             preventDefault: true,
24685             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24686         });
24687     //this.addSeparator();
24688         
24689         
24690         var field = this.navgroup.addItem( {
24691             tagtype : 'span',
24692             cls : 'x-paging-position',
24693             
24694             html : this.beforePageText  +
24695                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24696                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24697          } ); //?? escaped?
24698         
24699         this.field = field.el.select('input', true).first();
24700         this.field.on("keydown", this.onPagingKeydown, this);
24701         this.field.on("focus", function(){this.dom.select();});
24702     
24703     
24704         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24705         //this.field.setHeight(18);
24706         //this.addSeparator();
24707         this.next = this.navgroup.addItem({
24708             tooltip: this.nextText,
24709             cls: "next",
24710             html : ' <i class="fa fa-step-forward">',
24711             disabled: true,
24712             preventDefault: true,
24713             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24714         });
24715         this.last = this.navgroup.addItem({
24716             tooltip: this.lastText,
24717             icon : 'fa fa-forward',
24718             cls: "next",
24719             disabled: true,
24720             preventDefault: true,
24721             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24722         });
24723     //this.addSeparator();
24724         this.loading = this.navgroup.addItem({
24725             tooltip: this.refreshText,
24726             icon: 'fa fa-refresh',
24727             preventDefault: true,
24728             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24729         });
24730         
24731     },
24732
24733     // private
24734     updateInfo : function(){
24735         if(this.displayEl){
24736             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24737             var msg = count == 0 ?
24738                 this.emptyMsg :
24739                 String.format(
24740                     this.displayMsg,
24741                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24742                 );
24743             this.displayEl.update(msg);
24744         }
24745     },
24746
24747     // private
24748     onLoad : function(ds, r, o)
24749     {
24750         this.cursor = o.params.start ? o.params.start : 0;
24751         
24752         var d = this.getPageData(),
24753             ap = d.activePage,
24754             ps = d.pages;
24755         
24756         
24757         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24758         this.field.dom.value = ap;
24759         this.first.setDisabled(ap == 1);
24760         this.prev.setDisabled(ap == 1);
24761         this.next.setDisabled(ap == ps);
24762         this.last.setDisabled(ap == ps);
24763         this.loading.enable();
24764         this.updateInfo();
24765     },
24766
24767     // private
24768     getPageData : function(){
24769         var total = this.ds.getTotalCount();
24770         return {
24771             total : total,
24772             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24773             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24774         };
24775     },
24776
24777     // private
24778     onLoadError : function(){
24779         this.loading.enable();
24780     },
24781
24782     // private
24783     onPagingKeydown : function(e){
24784         var k = e.getKey();
24785         var d = this.getPageData();
24786         if(k == e.RETURN){
24787             var v = this.field.dom.value, pageNum;
24788             if(!v || isNaN(pageNum = parseInt(v, 10))){
24789                 this.field.dom.value = d.activePage;
24790                 return;
24791             }
24792             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24793             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24794             e.stopEvent();
24795         }
24796         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))
24797         {
24798           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24799           this.field.dom.value = pageNum;
24800           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24801           e.stopEvent();
24802         }
24803         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24804         {
24805           var v = this.field.dom.value, pageNum; 
24806           var increment = (e.shiftKey) ? 10 : 1;
24807           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24808                 increment *= -1;
24809           }
24810           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24811             this.field.dom.value = d.activePage;
24812             return;
24813           }
24814           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24815           {
24816             this.field.dom.value = parseInt(v, 10) + increment;
24817             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24818             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24819           }
24820           e.stopEvent();
24821         }
24822     },
24823
24824     // private
24825     beforeLoad : function(){
24826         if(this.loading){
24827             this.loading.disable();
24828         }
24829     },
24830
24831     // private
24832     onClick : function(which){
24833         
24834         var ds = this.ds;
24835         if (!ds) {
24836             return;
24837         }
24838         
24839         switch(which){
24840             case "first":
24841                 ds.load({params:{start: 0, limit: this.pageSize}});
24842             break;
24843             case "prev":
24844                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24845             break;
24846             case "next":
24847                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24848             break;
24849             case "last":
24850                 var total = ds.getTotalCount();
24851                 var extra = total % this.pageSize;
24852                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24853                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24854             break;
24855             case "refresh":
24856                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24857             break;
24858         }
24859     },
24860
24861     /**
24862      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24863      * @param {Roo.data.Store} store The data store to unbind
24864      */
24865     unbind : function(ds){
24866         ds.un("beforeload", this.beforeLoad, this);
24867         ds.un("load", this.onLoad, this);
24868         ds.un("loadexception", this.onLoadError, this);
24869         ds.un("remove", this.updateInfo, this);
24870         ds.un("add", this.updateInfo, this);
24871         this.ds = undefined;
24872     },
24873
24874     /**
24875      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24876      * @param {Roo.data.Store} store The data store to bind
24877      */
24878     bind : function(ds){
24879         ds.on("beforeload", this.beforeLoad, this);
24880         ds.on("load", this.onLoad, this);
24881         ds.on("loadexception", this.onLoadError, this);
24882         ds.on("remove", this.updateInfo, this);
24883         ds.on("add", this.updateInfo, this);
24884         this.ds = ds;
24885     }
24886 });/*
24887  * - LGPL
24888  *
24889  * element
24890  * 
24891  */
24892
24893 /**
24894  * @class Roo.bootstrap.MessageBar
24895  * @extends Roo.bootstrap.Component
24896  * Bootstrap MessageBar class
24897  * @cfg {String} html contents of the MessageBar
24898  * @cfg {String} weight (info | success | warning | danger) default info
24899  * @cfg {String} beforeClass insert the bar before the given class
24900  * @cfg {Boolean} closable (true | false) default false
24901  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24902  * 
24903  * @constructor
24904  * Create a new Element
24905  * @param {Object} config The config object
24906  */
24907
24908 Roo.bootstrap.MessageBar = function(config){
24909     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24910 };
24911
24912 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24913     
24914     html: '',
24915     weight: 'info',
24916     closable: false,
24917     fixed: false,
24918     beforeClass: 'bootstrap-sticky-wrap',
24919     
24920     getAutoCreate : function(){
24921         
24922         var cfg = {
24923             tag: 'div',
24924             cls: 'alert alert-dismissable alert-' + this.weight,
24925             cn: [
24926                 {
24927                     tag: 'span',
24928                     cls: 'message',
24929                     html: this.html || ''
24930                 }
24931             ]
24932         };
24933         
24934         if(this.fixed){
24935             cfg.cls += ' alert-messages-fixed';
24936         }
24937         
24938         if(this.closable){
24939             cfg.cn.push({
24940                 tag: 'button',
24941                 cls: 'close',
24942                 html: 'x'
24943             });
24944         }
24945         
24946         return cfg;
24947     },
24948     
24949     onRender : function(ct, position)
24950     {
24951         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24952         
24953         if(!this.el){
24954             var cfg = Roo.apply({},  this.getAutoCreate());
24955             cfg.id = Roo.id();
24956             
24957             if (this.cls) {
24958                 cfg.cls += ' ' + this.cls;
24959             }
24960             if (this.style) {
24961                 cfg.style = this.style;
24962             }
24963             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24964             
24965             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24966         }
24967         
24968         this.el.select('>button.close').on('click', this.hide, this);
24969         
24970     },
24971     
24972     show : function()
24973     {
24974         if (!this.rendered) {
24975             this.render();
24976         }
24977         
24978         this.el.show();
24979         
24980         this.fireEvent('show', this);
24981         
24982     },
24983     
24984     hide : function()
24985     {
24986         if (!this.rendered) {
24987             this.render();
24988         }
24989         
24990         this.el.hide();
24991         
24992         this.fireEvent('hide', this);
24993     },
24994     
24995     update : function()
24996     {
24997 //        var e = this.el.dom.firstChild;
24998 //        
24999 //        if(this.closable){
25000 //            e = e.nextSibling;
25001 //        }
25002 //        
25003 //        e.data = this.html || '';
25004
25005         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25006     }
25007    
25008 });
25009
25010  
25011
25012      /*
25013  * - LGPL
25014  *
25015  * Graph
25016  * 
25017  */
25018
25019
25020 /**
25021  * @class Roo.bootstrap.Graph
25022  * @extends Roo.bootstrap.Component
25023  * Bootstrap Graph class
25024 > Prameters
25025  -sm {number} sm 4
25026  -md {number} md 5
25027  @cfg {String} graphtype  bar | vbar | pie
25028  @cfg {number} g_x coodinator | centre x (pie)
25029  @cfg {number} g_y coodinator | centre y (pie)
25030  @cfg {number} g_r radius (pie)
25031  @cfg {number} g_height height of the chart (respected by all elements in the set)
25032  @cfg {number} g_width width of the chart (respected by all elements in the set)
25033  @cfg {Object} title The title of the chart
25034     
25035  -{Array}  values
25036  -opts (object) options for the chart 
25037      o {
25038      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25039      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25040      o vgutter (number)
25041      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.
25042      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25043      o to
25044      o stretch (boolean)
25045      o }
25046  -opts (object) options for the pie
25047      o{
25048      o cut
25049      o startAngle (number)
25050      o endAngle (number)
25051      } 
25052  *
25053  * @constructor
25054  * Create a new Input
25055  * @param {Object} config The config object
25056  */
25057
25058 Roo.bootstrap.Graph = function(config){
25059     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25060     
25061     this.addEvents({
25062         // img events
25063         /**
25064          * @event click
25065          * The img click event for the img.
25066          * @param {Roo.EventObject} e
25067          */
25068         "click" : true
25069     });
25070 };
25071
25072 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25073     
25074     sm: 4,
25075     md: 5,
25076     graphtype: 'bar',
25077     g_height: 250,
25078     g_width: 400,
25079     g_x: 50,
25080     g_y: 50,
25081     g_r: 30,
25082     opts:{
25083         //g_colors: this.colors,
25084         g_type: 'soft',
25085         g_gutter: '20%'
25086
25087     },
25088     title : false,
25089
25090     getAutoCreate : function(){
25091         
25092         var cfg = {
25093             tag: 'div',
25094             html : null
25095         };
25096         
25097         
25098         return  cfg;
25099     },
25100
25101     onRender : function(ct,position){
25102         
25103         
25104         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25105         
25106         if (typeof(Raphael) == 'undefined') {
25107             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25108             return;
25109         }
25110         
25111         this.raphael = Raphael(this.el.dom);
25112         
25113                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25114                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25115                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25116                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25117                 /*
25118                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25119                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25120                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25121                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25122                 
25123                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25124                 r.barchart(330, 10, 300, 220, data1);
25125                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25126                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25127                 */
25128                 
25129                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25130                 // r.barchart(30, 30, 560, 250,  xdata, {
25131                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25132                 //     axis : "0 0 1 1",
25133                 //     axisxlabels :  xdata
25134                 //     //yvalues : cols,
25135                    
25136                 // });
25137 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25138 //        
25139 //        this.load(null,xdata,{
25140 //                axis : "0 0 1 1",
25141 //                axisxlabels :  xdata
25142 //                });
25143
25144     },
25145
25146     load : function(graphtype,xdata,opts)
25147     {
25148         this.raphael.clear();
25149         if(!graphtype) {
25150             graphtype = this.graphtype;
25151         }
25152         if(!opts){
25153             opts = this.opts;
25154         }
25155         var r = this.raphael,
25156             fin = function () {
25157                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25158             },
25159             fout = function () {
25160                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25161             },
25162             pfin = function() {
25163                 this.sector.stop();
25164                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25165
25166                 if (this.label) {
25167                     this.label[0].stop();
25168                     this.label[0].attr({ r: 7.5 });
25169                     this.label[1].attr({ "font-weight": 800 });
25170                 }
25171             },
25172             pfout = function() {
25173                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25174
25175                 if (this.label) {
25176                     this.label[0].animate({ r: 5 }, 500, "bounce");
25177                     this.label[1].attr({ "font-weight": 400 });
25178                 }
25179             };
25180
25181         switch(graphtype){
25182             case 'bar':
25183                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25184                 break;
25185             case 'hbar':
25186                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25187                 break;
25188             case 'pie':
25189 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25190 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25191 //            
25192                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25193                 
25194                 break;
25195
25196         }
25197         
25198         if(this.title){
25199             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25200         }
25201         
25202     },
25203     
25204     setTitle: function(o)
25205     {
25206         this.title = o;
25207     },
25208     
25209     initEvents: function() {
25210         
25211         if(!this.href){
25212             this.el.on('click', this.onClick, this);
25213         }
25214     },
25215     
25216     onClick : function(e)
25217     {
25218         Roo.log('img onclick');
25219         this.fireEvent('click', this, e);
25220     }
25221    
25222 });
25223
25224  
25225 /*
25226  * - LGPL
25227  *
25228  * numberBox
25229  * 
25230  */
25231 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25232
25233 /**
25234  * @class Roo.bootstrap.dash.NumberBox
25235  * @extends Roo.bootstrap.Component
25236  * Bootstrap NumberBox class
25237  * @cfg {String} headline Box headline
25238  * @cfg {String} content Box content
25239  * @cfg {String} icon Box icon
25240  * @cfg {String} footer Footer text
25241  * @cfg {String} fhref Footer href
25242  * 
25243  * @constructor
25244  * Create a new NumberBox
25245  * @param {Object} config The config object
25246  */
25247
25248
25249 Roo.bootstrap.dash.NumberBox = function(config){
25250     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25251     
25252 };
25253
25254 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25255     
25256     headline : '',
25257     content : '',
25258     icon : '',
25259     footer : '',
25260     fhref : '',
25261     ficon : '',
25262     
25263     getAutoCreate : function(){
25264         
25265         var cfg = {
25266             tag : 'div',
25267             cls : 'small-box ',
25268             cn : [
25269                 {
25270                     tag : 'div',
25271                     cls : 'inner',
25272                     cn :[
25273                         {
25274                             tag : 'h3',
25275                             cls : 'roo-headline',
25276                             html : this.headline
25277                         },
25278                         {
25279                             tag : 'p',
25280                             cls : 'roo-content',
25281                             html : this.content
25282                         }
25283                     ]
25284                 }
25285             ]
25286         };
25287         
25288         if(this.icon){
25289             cfg.cn.push({
25290                 tag : 'div',
25291                 cls : 'icon',
25292                 cn :[
25293                     {
25294                         tag : 'i',
25295                         cls : 'ion ' + this.icon
25296                     }
25297                 ]
25298             });
25299         }
25300         
25301         if(this.footer){
25302             var footer = {
25303                 tag : 'a',
25304                 cls : 'small-box-footer',
25305                 href : this.fhref || '#',
25306                 html : this.footer
25307             };
25308             
25309             cfg.cn.push(footer);
25310             
25311         }
25312         
25313         return  cfg;
25314     },
25315
25316     onRender : function(ct,position){
25317         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25318
25319
25320        
25321                 
25322     },
25323
25324     setHeadline: function (value)
25325     {
25326         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25327     },
25328     
25329     setFooter: function (value, href)
25330     {
25331         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25332         
25333         if(href){
25334             this.el.select('a.small-box-footer',true).first().attr('href', href);
25335         }
25336         
25337     },
25338
25339     setContent: function (value)
25340     {
25341         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25342     },
25343
25344     initEvents: function() 
25345     {   
25346         
25347     }
25348     
25349 });
25350
25351  
25352 /*
25353  * - LGPL
25354  *
25355  * TabBox
25356  * 
25357  */
25358 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25359
25360 /**
25361  * @class Roo.bootstrap.dash.TabBox
25362  * @extends Roo.bootstrap.Component
25363  * Bootstrap TabBox class
25364  * @cfg {String} title Title of the TabBox
25365  * @cfg {String} icon Icon of the TabBox
25366  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25367  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25368  * 
25369  * @constructor
25370  * Create a new TabBox
25371  * @param {Object} config The config object
25372  */
25373
25374
25375 Roo.bootstrap.dash.TabBox = function(config){
25376     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25377     this.addEvents({
25378         // raw events
25379         /**
25380          * @event addpane
25381          * When a pane is added
25382          * @param {Roo.bootstrap.dash.TabPane} pane
25383          */
25384         "addpane" : true,
25385         /**
25386          * @event activatepane
25387          * When a pane is activated
25388          * @param {Roo.bootstrap.dash.TabPane} pane
25389          */
25390         "activatepane" : true
25391         
25392          
25393     });
25394     
25395     this.panes = [];
25396 };
25397
25398 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25399
25400     title : '',
25401     icon : false,
25402     showtabs : true,
25403     tabScrollable : false,
25404     
25405     getChildContainer : function()
25406     {
25407         return this.el.select('.tab-content', true).first();
25408     },
25409     
25410     getAutoCreate : function(){
25411         
25412         var header = {
25413             tag: 'li',
25414             cls: 'pull-left header',
25415             html: this.title,
25416             cn : []
25417         };
25418         
25419         if(this.icon){
25420             header.cn.push({
25421                 tag: 'i',
25422                 cls: 'fa ' + this.icon
25423             });
25424         }
25425         
25426         var h = {
25427             tag: 'ul',
25428             cls: 'nav nav-tabs pull-right',
25429             cn: [
25430                 header
25431             ]
25432         };
25433         
25434         if(this.tabScrollable){
25435             h = {
25436                 tag: 'div',
25437                 cls: 'tab-header',
25438                 cn: [
25439                     {
25440                         tag: 'ul',
25441                         cls: 'nav nav-tabs pull-right',
25442                         cn: [
25443                             header
25444                         ]
25445                     }
25446                 ]
25447             };
25448         }
25449         
25450         var cfg = {
25451             tag: 'div',
25452             cls: 'nav-tabs-custom',
25453             cn: [
25454                 h,
25455                 {
25456                     tag: 'div',
25457                     cls: 'tab-content no-padding',
25458                     cn: []
25459                 }
25460             ]
25461         };
25462
25463         return  cfg;
25464     },
25465     initEvents : function()
25466     {
25467         //Roo.log('add add pane handler');
25468         this.on('addpane', this.onAddPane, this);
25469     },
25470      /**
25471      * Updates the box title
25472      * @param {String} html to set the title to.
25473      */
25474     setTitle : function(value)
25475     {
25476         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25477     },
25478     onAddPane : function(pane)
25479     {
25480         this.panes.push(pane);
25481         //Roo.log('addpane');
25482         //Roo.log(pane);
25483         // tabs are rendere left to right..
25484         if(!this.showtabs){
25485             return;
25486         }
25487         
25488         var ctr = this.el.select('.nav-tabs', true).first();
25489          
25490          
25491         var existing = ctr.select('.nav-tab',true);
25492         var qty = existing.getCount();;
25493         
25494         
25495         var tab = ctr.createChild({
25496             tag : 'li',
25497             cls : 'nav-tab' + (qty ? '' : ' active'),
25498             cn : [
25499                 {
25500                     tag : 'a',
25501                     href:'#',
25502                     html : pane.title
25503                 }
25504             ]
25505         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25506         pane.tab = tab;
25507         
25508         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25509         if (!qty) {
25510             pane.el.addClass('active');
25511         }
25512         
25513                 
25514     },
25515     onTabClick : function(ev,un,ob,pane)
25516     {
25517         //Roo.log('tab - prev default');
25518         ev.preventDefault();
25519         
25520         
25521         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25522         pane.tab.addClass('active');
25523         //Roo.log(pane.title);
25524         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25525         // technically we should have a deactivate event.. but maybe add later.
25526         // and it should not de-activate the selected tab...
25527         this.fireEvent('activatepane', pane);
25528         pane.el.addClass('active');
25529         pane.fireEvent('activate');
25530         
25531         
25532     },
25533     
25534     getActivePane : function()
25535     {
25536         var r = false;
25537         Roo.each(this.panes, function(p) {
25538             if(p.el.hasClass('active')){
25539                 r = p;
25540                 return false;
25541             }
25542             
25543             return;
25544         });
25545         
25546         return r;
25547     }
25548     
25549     
25550 });
25551
25552  
25553 /*
25554  * - LGPL
25555  *
25556  * Tab pane
25557  * 
25558  */
25559 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25560 /**
25561  * @class Roo.bootstrap.TabPane
25562  * @extends Roo.bootstrap.Component
25563  * Bootstrap TabPane class
25564  * @cfg {Boolean} active (false | true) Default false
25565  * @cfg {String} title title of panel
25566
25567  * 
25568  * @constructor
25569  * Create a new TabPane
25570  * @param {Object} config The config object
25571  */
25572
25573 Roo.bootstrap.dash.TabPane = function(config){
25574     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25575     
25576     this.addEvents({
25577         // raw events
25578         /**
25579          * @event activate
25580          * When a pane is activated
25581          * @param {Roo.bootstrap.dash.TabPane} pane
25582          */
25583         "activate" : true
25584          
25585     });
25586 };
25587
25588 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25589     
25590     active : false,
25591     title : '',
25592     
25593     // the tabBox that this is attached to.
25594     tab : false,
25595      
25596     getAutoCreate : function() 
25597     {
25598         var cfg = {
25599             tag: 'div',
25600             cls: 'tab-pane'
25601         };
25602         
25603         if(this.active){
25604             cfg.cls += ' active';
25605         }
25606         
25607         return cfg;
25608     },
25609     initEvents  : function()
25610     {
25611         //Roo.log('trigger add pane handler');
25612         this.parent().fireEvent('addpane', this)
25613     },
25614     
25615      /**
25616      * Updates the tab title 
25617      * @param {String} html to set the title to.
25618      */
25619     setTitle: function(str)
25620     {
25621         if (!this.tab) {
25622             return;
25623         }
25624         this.title = str;
25625         this.tab.select('a', true).first().dom.innerHTML = str;
25626         
25627     }
25628     
25629     
25630     
25631 });
25632
25633  
25634
25635
25636  /*
25637  * - LGPL
25638  *
25639  * menu
25640  * 
25641  */
25642 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25643
25644 /**
25645  * @class Roo.bootstrap.menu.Menu
25646  * @extends Roo.bootstrap.Component
25647  * Bootstrap Menu class - container for Menu
25648  * @cfg {String} html Text of the menu
25649  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25650  * @cfg {String} icon Font awesome icon
25651  * @cfg {String} pos Menu align to (top | bottom) default bottom
25652  * 
25653  * 
25654  * @constructor
25655  * Create a new Menu
25656  * @param {Object} config The config object
25657  */
25658
25659
25660 Roo.bootstrap.menu.Menu = function(config){
25661     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25662     
25663     this.addEvents({
25664         /**
25665          * @event beforeshow
25666          * Fires before this menu is displayed
25667          * @param {Roo.bootstrap.menu.Menu} this
25668          */
25669         beforeshow : true,
25670         /**
25671          * @event beforehide
25672          * Fires before this menu is hidden
25673          * @param {Roo.bootstrap.menu.Menu} this
25674          */
25675         beforehide : true,
25676         /**
25677          * @event show
25678          * Fires after this menu is displayed
25679          * @param {Roo.bootstrap.menu.Menu} this
25680          */
25681         show : true,
25682         /**
25683          * @event hide
25684          * Fires after this menu is hidden
25685          * @param {Roo.bootstrap.menu.Menu} this
25686          */
25687         hide : true,
25688         /**
25689          * @event click
25690          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25691          * @param {Roo.bootstrap.menu.Menu} this
25692          * @param {Roo.EventObject} e
25693          */
25694         click : true
25695     });
25696     
25697 };
25698
25699 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25700     
25701     submenu : false,
25702     html : '',
25703     weight : 'default',
25704     icon : false,
25705     pos : 'bottom',
25706     
25707     
25708     getChildContainer : function() {
25709         if(this.isSubMenu){
25710             return this.el;
25711         }
25712         
25713         return this.el.select('ul.dropdown-menu', true).first();  
25714     },
25715     
25716     getAutoCreate : function()
25717     {
25718         var text = [
25719             {
25720                 tag : 'span',
25721                 cls : 'roo-menu-text',
25722                 html : this.html
25723             }
25724         ];
25725         
25726         if(this.icon){
25727             text.unshift({
25728                 tag : 'i',
25729                 cls : 'fa ' + this.icon
25730             })
25731         }
25732         
25733         
25734         var cfg = {
25735             tag : 'div',
25736             cls : 'btn-group',
25737             cn : [
25738                 {
25739                     tag : 'button',
25740                     cls : 'dropdown-button btn btn-' + this.weight,
25741                     cn : text
25742                 },
25743                 {
25744                     tag : 'button',
25745                     cls : 'dropdown-toggle btn btn-' + this.weight,
25746                     cn : [
25747                         {
25748                             tag : 'span',
25749                             cls : 'caret'
25750                         }
25751                     ]
25752                 },
25753                 {
25754                     tag : 'ul',
25755                     cls : 'dropdown-menu'
25756                 }
25757             ]
25758             
25759         };
25760         
25761         if(this.pos == 'top'){
25762             cfg.cls += ' dropup';
25763         }
25764         
25765         if(this.isSubMenu){
25766             cfg = {
25767                 tag : 'ul',
25768                 cls : 'dropdown-menu'
25769             }
25770         }
25771         
25772         return cfg;
25773     },
25774     
25775     onRender : function(ct, position)
25776     {
25777         this.isSubMenu = ct.hasClass('dropdown-submenu');
25778         
25779         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25780     },
25781     
25782     initEvents : function() 
25783     {
25784         if(this.isSubMenu){
25785             return;
25786         }
25787         
25788         this.hidden = true;
25789         
25790         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25791         this.triggerEl.on('click', this.onTriggerPress, this);
25792         
25793         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25794         this.buttonEl.on('click', this.onClick, this);
25795         
25796     },
25797     
25798     list : function()
25799     {
25800         if(this.isSubMenu){
25801             return this.el;
25802         }
25803         
25804         return this.el.select('ul.dropdown-menu', true).first();
25805     },
25806     
25807     onClick : function(e)
25808     {
25809         this.fireEvent("click", this, e);
25810     },
25811     
25812     onTriggerPress  : function(e)
25813     {   
25814         if (this.isVisible()) {
25815             this.hide();
25816         } else {
25817             this.show();
25818         }
25819     },
25820     
25821     isVisible : function(){
25822         return !this.hidden;
25823     },
25824     
25825     show : function()
25826     {
25827         this.fireEvent("beforeshow", this);
25828         
25829         this.hidden = false;
25830         this.el.addClass('open');
25831         
25832         Roo.get(document).on("mouseup", this.onMouseUp, this);
25833         
25834         this.fireEvent("show", this);
25835         
25836         
25837     },
25838     
25839     hide : function()
25840     {
25841         this.fireEvent("beforehide", this);
25842         
25843         this.hidden = true;
25844         this.el.removeClass('open');
25845         
25846         Roo.get(document).un("mouseup", this.onMouseUp);
25847         
25848         this.fireEvent("hide", this);
25849     },
25850     
25851     onMouseUp : function()
25852     {
25853         this.hide();
25854     }
25855     
25856 });
25857
25858  
25859  /*
25860  * - LGPL
25861  *
25862  * menu item
25863  * 
25864  */
25865 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25866
25867 /**
25868  * @class Roo.bootstrap.menu.Item
25869  * @extends Roo.bootstrap.Component
25870  * Bootstrap MenuItem class
25871  * @cfg {Boolean} submenu (true | false) default false
25872  * @cfg {String} html text of the item
25873  * @cfg {String} href the link
25874  * @cfg {Boolean} disable (true | false) default false
25875  * @cfg {Boolean} preventDefault (true | false) default true
25876  * @cfg {String} icon Font awesome icon
25877  * @cfg {String} pos Submenu align to (left | right) default right 
25878  * 
25879  * 
25880  * @constructor
25881  * Create a new Item
25882  * @param {Object} config The config object
25883  */
25884
25885
25886 Roo.bootstrap.menu.Item = function(config){
25887     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25888     this.addEvents({
25889         /**
25890          * @event mouseover
25891          * Fires when the mouse is hovering over this menu
25892          * @param {Roo.bootstrap.menu.Item} this
25893          * @param {Roo.EventObject} e
25894          */
25895         mouseover : true,
25896         /**
25897          * @event mouseout
25898          * Fires when the mouse exits this menu
25899          * @param {Roo.bootstrap.menu.Item} this
25900          * @param {Roo.EventObject} e
25901          */
25902         mouseout : true,
25903         // raw events
25904         /**
25905          * @event click
25906          * The raw click event for the entire grid.
25907          * @param {Roo.EventObject} e
25908          */
25909         click : true
25910     });
25911 };
25912
25913 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25914     
25915     submenu : false,
25916     href : '',
25917     html : '',
25918     preventDefault: true,
25919     disable : false,
25920     icon : false,
25921     pos : 'right',
25922     
25923     getAutoCreate : function()
25924     {
25925         var text = [
25926             {
25927                 tag : 'span',
25928                 cls : 'roo-menu-item-text',
25929                 html : this.html
25930             }
25931         ];
25932         
25933         if(this.icon){
25934             text.unshift({
25935                 tag : 'i',
25936                 cls : 'fa ' + this.icon
25937             })
25938         }
25939         
25940         var cfg = {
25941             tag : 'li',
25942             cn : [
25943                 {
25944                     tag : 'a',
25945                     href : this.href || '#',
25946                     cn : text
25947                 }
25948             ]
25949         };
25950         
25951         if(this.disable){
25952             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25953         }
25954         
25955         if(this.submenu){
25956             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25957             
25958             if(this.pos == 'left'){
25959                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25960             }
25961         }
25962         
25963         return cfg;
25964     },
25965     
25966     initEvents : function() 
25967     {
25968         this.el.on('mouseover', this.onMouseOver, this);
25969         this.el.on('mouseout', this.onMouseOut, this);
25970         
25971         this.el.select('a', true).first().on('click', this.onClick, this);
25972         
25973     },
25974     
25975     onClick : function(e)
25976     {
25977         if(this.preventDefault){
25978             e.preventDefault();
25979         }
25980         
25981         this.fireEvent("click", this, e);
25982     },
25983     
25984     onMouseOver : function(e)
25985     {
25986         if(this.submenu && this.pos == 'left'){
25987             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25988         }
25989         
25990         this.fireEvent("mouseover", this, e);
25991     },
25992     
25993     onMouseOut : function(e)
25994     {
25995         this.fireEvent("mouseout", this, e);
25996     }
25997 });
25998
25999  
26000
26001  /*
26002  * - LGPL
26003  *
26004  * menu separator
26005  * 
26006  */
26007 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26008
26009 /**
26010  * @class Roo.bootstrap.menu.Separator
26011  * @extends Roo.bootstrap.Component
26012  * Bootstrap Separator class
26013  * 
26014  * @constructor
26015  * Create a new Separator
26016  * @param {Object} config The config object
26017  */
26018
26019
26020 Roo.bootstrap.menu.Separator = function(config){
26021     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26022 };
26023
26024 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26025     
26026     getAutoCreate : function(){
26027         var cfg = {
26028             tag : 'li',
26029             cls: 'divider'
26030         };
26031         
26032         return cfg;
26033     }
26034    
26035 });
26036
26037  
26038
26039  /*
26040  * - LGPL
26041  *
26042  * Tooltip
26043  * 
26044  */
26045
26046 /**
26047  * @class Roo.bootstrap.Tooltip
26048  * Bootstrap Tooltip class
26049  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26050  * to determine which dom element triggers the tooltip.
26051  * 
26052  * It needs to add support for additional attributes like tooltip-position
26053  * 
26054  * @constructor
26055  * Create a new Toolti
26056  * @param {Object} config The config object
26057  */
26058
26059 Roo.bootstrap.Tooltip = function(config){
26060     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26061     
26062     this.alignment = Roo.bootstrap.Tooltip.alignment;
26063     
26064     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26065         this.alignment = config.alignment;
26066     }
26067     
26068 };
26069
26070 Roo.apply(Roo.bootstrap.Tooltip, {
26071     /**
26072      * @function init initialize tooltip monitoring.
26073      * @static
26074      */
26075     currentEl : false,
26076     currentTip : false,
26077     currentRegion : false,
26078     
26079     //  init : delay?
26080     
26081     init : function()
26082     {
26083         Roo.get(document).on('mouseover', this.enter ,this);
26084         Roo.get(document).on('mouseout', this.leave, this);
26085          
26086         
26087         this.currentTip = new Roo.bootstrap.Tooltip();
26088     },
26089     
26090     enter : function(ev)
26091     {
26092         var dom = ev.getTarget();
26093         
26094         //Roo.log(['enter',dom]);
26095         var el = Roo.fly(dom);
26096         if (this.currentEl) {
26097             //Roo.log(dom);
26098             //Roo.log(this.currentEl);
26099             //Roo.log(this.currentEl.contains(dom));
26100             if (this.currentEl == el) {
26101                 return;
26102             }
26103             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26104                 return;
26105             }
26106
26107         }
26108         
26109         if (this.currentTip.el) {
26110             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26111         }    
26112         //Roo.log(ev);
26113         
26114         if(!el || el.dom == document){
26115             return;
26116         }
26117         
26118         var bindEl = el;
26119         
26120         // you can not look for children, as if el is the body.. then everythign is the child..
26121         if (!el.attr('tooltip')) { //
26122             if (!el.select("[tooltip]").elements.length) {
26123                 return;
26124             }
26125             // is the mouse over this child...?
26126             bindEl = el.select("[tooltip]").first();
26127             var xy = ev.getXY();
26128             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26129                 //Roo.log("not in region.");
26130                 return;
26131             }
26132             //Roo.log("child element over..");
26133             
26134         }
26135         this.currentEl = bindEl;
26136         this.currentTip.bind(bindEl);
26137         this.currentRegion = Roo.lib.Region.getRegion(dom);
26138         this.currentTip.enter();
26139         
26140     },
26141     leave : function(ev)
26142     {
26143         var dom = ev.getTarget();
26144         //Roo.log(['leave',dom]);
26145         if (!this.currentEl) {
26146             return;
26147         }
26148         
26149         
26150         if (dom != this.currentEl.dom) {
26151             return;
26152         }
26153         var xy = ev.getXY();
26154         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26155             return;
26156         }
26157         // only activate leave if mouse cursor is outside... bounding box..
26158         
26159         
26160         
26161         
26162         if (this.currentTip) {
26163             this.currentTip.leave();
26164         }
26165         //Roo.log('clear currentEl');
26166         this.currentEl = false;
26167         
26168         
26169     },
26170     alignment : {
26171         'left' : ['r-l', [-2,0], 'right'],
26172         'right' : ['l-r', [2,0], 'left'],
26173         'bottom' : ['t-b', [0,2], 'top'],
26174         'top' : [ 'b-t', [0,-2], 'bottom']
26175     }
26176     
26177 });
26178
26179
26180 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26181     
26182     
26183     bindEl : false,
26184     
26185     delay : null, // can be { show : 300 , hide: 500}
26186     
26187     timeout : null,
26188     
26189     hoverState : null, //???
26190     
26191     placement : 'bottom', 
26192     
26193     alignment : false,
26194     
26195     getAutoCreate : function(){
26196     
26197         var cfg = {
26198            cls : 'tooltip',
26199            role : 'tooltip',
26200            cn : [
26201                 {
26202                     cls : 'tooltip-arrow'
26203                 },
26204                 {
26205                     cls : 'tooltip-inner'
26206                 }
26207            ]
26208         };
26209         
26210         return cfg;
26211     },
26212     bind : function(el)
26213     {
26214         this.bindEl = el;
26215     },
26216       
26217     
26218     enter : function () {
26219        
26220         if (this.timeout != null) {
26221             clearTimeout(this.timeout);
26222         }
26223         
26224         this.hoverState = 'in';
26225          //Roo.log("enter - show");
26226         if (!this.delay || !this.delay.show) {
26227             this.show();
26228             return;
26229         }
26230         var _t = this;
26231         this.timeout = setTimeout(function () {
26232             if (_t.hoverState == 'in') {
26233                 _t.show();
26234             }
26235         }, this.delay.show);
26236     },
26237     leave : function()
26238     {
26239         clearTimeout(this.timeout);
26240     
26241         this.hoverState = 'out';
26242          if (!this.delay || !this.delay.hide) {
26243             this.hide();
26244             return;
26245         }
26246        
26247         var _t = this;
26248         this.timeout = setTimeout(function () {
26249             //Roo.log("leave - timeout");
26250             
26251             if (_t.hoverState == 'out') {
26252                 _t.hide();
26253                 Roo.bootstrap.Tooltip.currentEl = false;
26254             }
26255         }, delay);
26256     },
26257     
26258     show : function (msg)
26259     {
26260         if (!this.el) {
26261             this.render(document.body);
26262         }
26263         // set content.
26264         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26265         
26266         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26267         
26268         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26269         
26270         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26271         
26272         var placement = typeof this.placement == 'function' ?
26273             this.placement.call(this, this.el, on_el) :
26274             this.placement;
26275             
26276         var autoToken = /\s?auto?\s?/i;
26277         var autoPlace = autoToken.test(placement);
26278         if (autoPlace) {
26279             placement = placement.replace(autoToken, '') || 'top';
26280         }
26281         
26282         //this.el.detach()
26283         //this.el.setXY([0,0]);
26284         this.el.show();
26285         //this.el.dom.style.display='block';
26286         
26287         //this.el.appendTo(on_el);
26288         
26289         var p = this.getPosition();
26290         var box = this.el.getBox();
26291         
26292         if (autoPlace) {
26293             // fixme..
26294         }
26295         
26296         var align = this.alignment[placement];
26297         
26298         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26299         
26300         if(placement == 'top' || placement == 'bottom'){
26301             if(xy[0] < 0){
26302                 placement = 'right';
26303             }
26304             
26305             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26306                 placement = 'left';
26307             }
26308             
26309             var scroll = Roo.select('body', true).first().getScroll();
26310             
26311             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26312                 placement = 'top';
26313             }
26314             
26315             align = this.alignment[placement];
26316         }
26317         
26318         this.el.alignTo(this.bindEl, align[0],align[1]);
26319         //var arrow = this.el.select('.arrow',true).first();
26320         //arrow.set(align[2], 
26321         
26322         this.el.addClass(placement);
26323         
26324         this.el.addClass('in fade');
26325         
26326         this.hoverState = null;
26327         
26328         if (this.el.hasClass('fade')) {
26329             // fade it?
26330         }
26331         
26332     },
26333     hide : function()
26334     {
26335          
26336         if (!this.el) {
26337             return;
26338         }
26339         //this.el.setXY([0,0]);
26340         this.el.removeClass('in');
26341         //this.el.hide();
26342         
26343     }
26344     
26345 });
26346  
26347
26348  /*
26349  * - LGPL
26350  *
26351  * Location Picker
26352  * 
26353  */
26354
26355 /**
26356  * @class Roo.bootstrap.LocationPicker
26357  * @extends Roo.bootstrap.Component
26358  * Bootstrap LocationPicker class
26359  * @cfg {Number} latitude Position when init default 0
26360  * @cfg {Number} longitude Position when init default 0
26361  * @cfg {Number} zoom default 15
26362  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26363  * @cfg {Boolean} mapTypeControl default false
26364  * @cfg {Boolean} disableDoubleClickZoom default false
26365  * @cfg {Boolean} scrollwheel default true
26366  * @cfg {Boolean} streetViewControl default false
26367  * @cfg {Number} radius default 0
26368  * @cfg {String} locationName
26369  * @cfg {Boolean} draggable default true
26370  * @cfg {Boolean} enableAutocomplete default false
26371  * @cfg {Boolean} enableReverseGeocode default true
26372  * @cfg {String} markerTitle
26373  * 
26374  * @constructor
26375  * Create a new LocationPicker
26376  * @param {Object} config The config object
26377  */
26378
26379
26380 Roo.bootstrap.LocationPicker = function(config){
26381     
26382     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26383     
26384     this.addEvents({
26385         /**
26386          * @event initial
26387          * Fires when the picker initialized.
26388          * @param {Roo.bootstrap.LocationPicker} this
26389          * @param {Google Location} location
26390          */
26391         initial : true,
26392         /**
26393          * @event positionchanged
26394          * Fires when the picker position changed.
26395          * @param {Roo.bootstrap.LocationPicker} this
26396          * @param {Google Location} location
26397          */
26398         positionchanged : true,
26399         /**
26400          * @event resize
26401          * Fires when the map resize.
26402          * @param {Roo.bootstrap.LocationPicker} this
26403          */
26404         resize : true,
26405         /**
26406          * @event show
26407          * Fires when the map show.
26408          * @param {Roo.bootstrap.LocationPicker} this
26409          */
26410         show : true,
26411         /**
26412          * @event hide
26413          * Fires when the map hide.
26414          * @param {Roo.bootstrap.LocationPicker} this
26415          */
26416         hide : true,
26417         /**
26418          * @event mapClick
26419          * Fires when click the map.
26420          * @param {Roo.bootstrap.LocationPicker} this
26421          * @param {Map event} e
26422          */
26423         mapClick : true,
26424         /**
26425          * @event mapRightClick
26426          * Fires when right click the map.
26427          * @param {Roo.bootstrap.LocationPicker} this
26428          * @param {Map event} e
26429          */
26430         mapRightClick : true,
26431         /**
26432          * @event markerClick
26433          * Fires when click the marker.
26434          * @param {Roo.bootstrap.LocationPicker} this
26435          * @param {Map event} e
26436          */
26437         markerClick : true,
26438         /**
26439          * @event markerRightClick
26440          * Fires when right click the marker.
26441          * @param {Roo.bootstrap.LocationPicker} this
26442          * @param {Map event} e
26443          */
26444         markerRightClick : true,
26445         /**
26446          * @event OverlayViewDraw
26447          * Fires when OverlayView Draw
26448          * @param {Roo.bootstrap.LocationPicker} this
26449          */
26450         OverlayViewDraw : true,
26451         /**
26452          * @event OverlayViewOnAdd
26453          * Fires when OverlayView Draw
26454          * @param {Roo.bootstrap.LocationPicker} this
26455          */
26456         OverlayViewOnAdd : true,
26457         /**
26458          * @event OverlayViewOnRemove
26459          * Fires when OverlayView Draw
26460          * @param {Roo.bootstrap.LocationPicker} this
26461          */
26462         OverlayViewOnRemove : true,
26463         /**
26464          * @event OverlayViewShow
26465          * Fires when OverlayView Draw
26466          * @param {Roo.bootstrap.LocationPicker} this
26467          * @param {Pixel} cpx
26468          */
26469         OverlayViewShow : true,
26470         /**
26471          * @event OverlayViewHide
26472          * Fires when OverlayView Draw
26473          * @param {Roo.bootstrap.LocationPicker} this
26474          */
26475         OverlayViewHide : true,
26476         /**
26477          * @event loadexception
26478          * Fires when load google lib failed.
26479          * @param {Roo.bootstrap.LocationPicker} this
26480          */
26481         loadexception : true
26482     });
26483         
26484 };
26485
26486 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26487     
26488     gMapContext: false,
26489     
26490     latitude: 0,
26491     longitude: 0,
26492     zoom: 15,
26493     mapTypeId: false,
26494     mapTypeControl: false,
26495     disableDoubleClickZoom: false,
26496     scrollwheel: true,
26497     streetViewControl: false,
26498     radius: 0,
26499     locationName: '',
26500     draggable: true,
26501     enableAutocomplete: false,
26502     enableReverseGeocode: true,
26503     markerTitle: '',
26504     
26505     getAutoCreate: function()
26506     {
26507
26508         var cfg = {
26509             tag: 'div',
26510             cls: 'roo-location-picker'
26511         };
26512         
26513         return cfg
26514     },
26515     
26516     initEvents: function(ct, position)
26517     {       
26518         if(!this.el.getWidth() || this.isApplied()){
26519             return;
26520         }
26521         
26522         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26523         
26524         this.initial();
26525     },
26526     
26527     initial: function()
26528     {
26529         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26530             this.fireEvent('loadexception', this);
26531             return;
26532         }
26533         
26534         if(!this.mapTypeId){
26535             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26536         }
26537         
26538         this.gMapContext = this.GMapContext();
26539         
26540         this.initOverlayView();
26541         
26542         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26543         
26544         var _this = this;
26545                 
26546         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26547             _this.setPosition(_this.gMapContext.marker.position);
26548         });
26549         
26550         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26551             _this.fireEvent('mapClick', this, event);
26552             
26553         });
26554
26555         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26556             _this.fireEvent('mapRightClick', this, event);
26557             
26558         });
26559         
26560         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26561             _this.fireEvent('markerClick', this, event);
26562             
26563         });
26564
26565         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26566             _this.fireEvent('markerRightClick', this, event);
26567             
26568         });
26569         
26570         this.setPosition(this.gMapContext.location);
26571         
26572         this.fireEvent('initial', this, this.gMapContext.location);
26573     },
26574     
26575     initOverlayView: function()
26576     {
26577         var _this = this;
26578         
26579         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26580             
26581             draw: function()
26582             {
26583                 _this.fireEvent('OverlayViewDraw', _this);
26584             },
26585             
26586             onAdd: function()
26587             {
26588                 _this.fireEvent('OverlayViewOnAdd', _this);
26589             },
26590             
26591             onRemove: function()
26592             {
26593                 _this.fireEvent('OverlayViewOnRemove', _this);
26594             },
26595             
26596             show: function(cpx)
26597             {
26598                 _this.fireEvent('OverlayViewShow', _this, cpx);
26599             },
26600             
26601             hide: function()
26602             {
26603                 _this.fireEvent('OverlayViewHide', _this);
26604             }
26605             
26606         });
26607     },
26608     
26609     fromLatLngToContainerPixel: function(event)
26610     {
26611         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26612     },
26613     
26614     isApplied: function() 
26615     {
26616         return this.getGmapContext() == false ? false : true;
26617     },
26618     
26619     getGmapContext: function() 
26620     {
26621         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26622     },
26623     
26624     GMapContext: function() 
26625     {
26626         var position = new google.maps.LatLng(this.latitude, this.longitude);
26627         
26628         var _map = new google.maps.Map(this.el.dom, {
26629             center: position,
26630             zoom: this.zoom,
26631             mapTypeId: this.mapTypeId,
26632             mapTypeControl: this.mapTypeControl,
26633             disableDoubleClickZoom: this.disableDoubleClickZoom,
26634             scrollwheel: this.scrollwheel,
26635             streetViewControl: this.streetViewControl,
26636             locationName: this.locationName,
26637             draggable: this.draggable,
26638             enableAutocomplete: this.enableAutocomplete,
26639             enableReverseGeocode: this.enableReverseGeocode
26640         });
26641         
26642         var _marker = new google.maps.Marker({
26643             position: position,
26644             map: _map,
26645             title: this.markerTitle,
26646             draggable: this.draggable
26647         });
26648         
26649         return {
26650             map: _map,
26651             marker: _marker,
26652             circle: null,
26653             location: position,
26654             radius: this.radius,
26655             locationName: this.locationName,
26656             addressComponents: {
26657                 formatted_address: null,
26658                 addressLine1: null,
26659                 addressLine2: null,
26660                 streetName: null,
26661                 streetNumber: null,
26662                 city: null,
26663                 district: null,
26664                 state: null,
26665                 stateOrProvince: null
26666             },
26667             settings: this,
26668             domContainer: this.el.dom,
26669             geodecoder: new google.maps.Geocoder()
26670         };
26671     },
26672     
26673     drawCircle: function(center, radius, options) 
26674     {
26675         if (this.gMapContext.circle != null) {
26676             this.gMapContext.circle.setMap(null);
26677         }
26678         if (radius > 0) {
26679             radius *= 1;
26680             options = Roo.apply({}, options, {
26681                 strokeColor: "#0000FF",
26682                 strokeOpacity: .35,
26683                 strokeWeight: 2,
26684                 fillColor: "#0000FF",
26685                 fillOpacity: .2
26686             });
26687             
26688             options.map = this.gMapContext.map;
26689             options.radius = radius;
26690             options.center = center;
26691             this.gMapContext.circle = new google.maps.Circle(options);
26692             return this.gMapContext.circle;
26693         }
26694         
26695         return null;
26696     },
26697     
26698     setPosition: function(location) 
26699     {
26700         this.gMapContext.location = location;
26701         this.gMapContext.marker.setPosition(location);
26702         this.gMapContext.map.panTo(location);
26703         this.drawCircle(location, this.gMapContext.radius, {});
26704         
26705         var _this = this;
26706         
26707         if (this.gMapContext.settings.enableReverseGeocode) {
26708             this.gMapContext.geodecoder.geocode({
26709                 latLng: this.gMapContext.location
26710             }, function(results, status) {
26711                 
26712                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26713                     _this.gMapContext.locationName = results[0].formatted_address;
26714                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26715                     
26716                     _this.fireEvent('positionchanged', this, location);
26717                 }
26718             });
26719             
26720             return;
26721         }
26722         
26723         this.fireEvent('positionchanged', this, location);
26724     },
26725     
26726     resize: function()
26727     {
26728         google.maps.event.trigger(this.gMapContext.map, "resize");
26729         
26730         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26731         
26732         this.fireEvent('resize', this);
26733     },
26734     
26735     setPositionByLatLng: function(latitude, longitude)
26736     {
26737         this.setPosition(new google.maps.LatLng(latitude, longitude));
26738     },
26739     
26740     getCurrentPosition: function() 
26741     {
26742         return {
26743             latitude: this.gMapContext.location.lat(),
26744             longitude: this.gMapContext.location.lng()
26745         };
26746     },
26747     
26748     getAddressName: function() 
26749     {
26750         return this.gMapContext.locationName;
26751     },
26752     
26753     getAddressComponents: function() 
26754     {
26755         return this.gMapContext.addressComponents;
26756     },
26757     
26758     address_component_from_google_geocode: function(address_components) 
26759     {
26760         var result = {};
26761         
26762         for (var i = 0; i < address_components.length; i++) {
26763             var component = address_components[i];
26764             if (component.types.indexOf("postal_code") >= 0) {
26765                 result.postalCode = component.short_name;
26766             } else if (component.types.indexOf("street_number") >= 0) {
26767                 result.streetNumber = component.short_name;
26768             } else if (component.types.indexOf("route") >= 0) {
26769                 result.streetName = component.short_name;
26770             } else if (component.types.indexOf("neighborhood") >= 0) {
26771                 result.city = component.short_name;
26772             } else if (component.types.indexOf("locality") >= 0) {
26773                 result.city = component.short_name;
26774             } else if (component.types.indexOf("sublocality") >= 0) {
26775                 result.district = component.short_name;
26776             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26777                 result.stateOrProvince = component.short_name;
26778             } else if (component.types.indexOf("country") >= 0) {
26779                 result.country = component.short_name;
26780             }
26781         }
26782         
26783         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26784         result.addressLine2 = "";
26785         return result;
26786     },
26787     
26788     setZoomLevel: function(zoom)
26789     {
26790         this.gMapContext.map.setZoom(zoom);
26791     },
26792     
26793     show: function()
26794     {
26795         if(!this.el){
26796             return;
26797         }
26798         
26799         this.el.show();
26800         
26801         this.resize();
26802         
26803         this.fireEvent('show', this);
26804     },
26805     
26806     hide: function()
26807     {
26808         if(!this.el){
26809             return;
26810         }
26811         
26812         this.el.hide();
26813         
26814         this.fireEvent('hide', this);
26815     }
26816     
26817 });
26818
26819 Roo.apply(Roo.bootstrap.LocationPicker, {
26820     
26821     OverlayView : function(map, options)
26822     {
26823         options = options || {};
26824         
26825         this.setMap(map);
26826     }
26827     
26828     
26829 });/*
26830  * - LGPL
26831  *
26832  * Alert
26833  * 
26834  */
26835
26836 /**
26837  * @class Roo.bootstrap.Alert
26838  * @extends Roo.bootstrap.Component
26839  * Bootstrap Alert class
26840  * @cfg {String} title The title of alert
26841  * @cfg {String} html The content of alert
26842  * @cfg {String} weight (  success | info | warning | danger )
26843  * @cfg {String} faicon font-awesomeicon
26844  * 
26845  * @constructor
26846  * Create a new alert
26847  * @param {Object} config The config object
26848  */
26849
26850
26851 Roo.bootstrap.Alert = function(config){
26852     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26853     
26854 };
26855
26856 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26857     
26858     title: '',
26859     html: '',
26860     weight: false,
26861     faicon: false,
26862     
26863     getAutoCreate : function()
26864     {
26865         
26866         var cfg = {
26867             tag : 'div',
26868             cls : 'alert',
26869             cn : [
26870                 {
26871                     tag : 'i',
26872                     cls : 'roo-alert-icon'
26873                     
26874                 },
26875                 {
26876                     tag : 'b',
26877                     cls : 'roo-alert-title',
26878                     html : this.title
26879                 },
26880                 {
26881                     tag : 'span',
26882                     cls : 'roo-alert-text',
26883                     html : this.html
26884                 }
26885             ]
26886         };
26887         
26888         if(this.faicon){
26889             cfg.cn[0].cls += ' fa ' + this.faicon;
26890         }
26891         
26892         if(this.weight){
26893             cfg.cls += ' alert-' + this.weight;
26894         }
26895         
26896         return cfg;
26897     },
26898     
26899     initEvents: function() 
26900     {
26901         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26902     },
26903     
26904     setTitle : function(str)
26905     {
26906         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26907     },
26908     
26909     setText : function(str)
26910     {
26911         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26912     },
26913     
26914     setWeight : function(weight)
26915     {
26916         if(this.weight){
26917             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26918         }
26919         
26920         this.weight = weight;
26921         
26922         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26923     },
26924     
26925     setIcon : function(icon)
26926     {
26927         if(this.faicon){
26928             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26929         }
26930         
26931         this.faicon = icon;
26932         
26933         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26934     },
26935     
26936     hide: function() 
26937     {
26938         this.el.hide();   
26939     },
26940     
26941     show: function() 
26942     {  
26943         this.el.show();   
26944     }
26945     
26946 });
26947
26948  
26949 /*
26950 * Licence: LGPL
26951 */
26952
26953 /**
26954  * @class Roo.bootstrap.UploadCropbox
26955  * @extends Roo.bootstrap.Component
26956  * Bootstrap UploadCropbox class
26957  * @cfg {String} emptyText show when image has been loaded
26958  * @cfg {String} rotateNotify show when image too small to rotate
26959  * @cfg {Number} errorTimeout default 3000
26960  * @cfg {Number} minWidth default 300
26961  * @cfg {Number} minHeight default 300
26962  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26963  * @cfg {Boolean} isDocument (true|false) default false
26964  * @cfg {String} url action url
26965  * @cfg {String} paramName default 'imageUpload'
26966  * @cfg {String} method default POST
26967  * @cfg {Boolean} loadMask (true|false) default true
26968  * @cfg {Boolean} loadingText default 'Loading...'
26969  * 
26970  * @constructor
26971  * Create a new UploadCropbox
26972  * @param {Object} config The config object
26973  */
26974
26975 Roo.bootstrap.UploadCropbox = function(config){
26976     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26977     
26978     this.addEvents({
26979         /**
26980          * @event beforeselectfile
26981          * Fire before select file
26982          * @param {Roo.bootstrap.UploadCropbox} this
26983          */
26984         "beforeselectfile" : true,
26985         /**
26986          * @event initial
26987          * Fire after initEvent
26988          * @param {Roo.bootstrap.UploadCropbox} this
26989          */
26990         "initial" : true,
26991         /**
26992          * @event crop
26993          * Fire after initEvent
26994          * @param {Roo.bootstrap.UploadCropbox} this
26995          * @param {String} data
26996          */
26997         "crop" : true,
26998         /**
26999          * @event prepare
27000          * Fire when preparing the file data
27001          * @param {Roo.bootstrap.UploadCropbox} this
27002          * @param {Object} file
27003          */
27004         "prepare" : true,
27005         /**
27006          * @event exception
27007          * Fire when get exception
27008          * @param {Roo.bootstrap.UploadCropbox} this
27009          * @param {XMLHttpRequest} xhr
27010          */
27011         "exception" : true,
27012         /**
27013          * @event beforeloadcanvas
27014          * Fire before load the canvas
27015          * @param {Roo.bootstrap.UploadCropbox} this
27016          * @param {String} src
27017          */
27018         "beforeloadcanvas" : true,
27019         /**
27020          * @event trash
27021          * Fire when trash image
27022          * @param {Roo.bootstrap.UploadCropbox} this
27023          */
27024         "trash" : true,
27025         /**
27026          * @event download
27027          * Fire when download the image
27028          * @param {Roo.bootstrap.UploadCropbox} this
27029          */
27030         "download" : true,
27031         /**
27032          * @event footerbuttonclick
27033          * Fire when footerbuttonclick
27034          * @param {Roo.bootstrap.UploadCropbox} this
27035          * @param {String} type
27036          */
27037         "footerbuttonclick" : true,
27038         /**
27039          * @event resize
27040          * Fire when resize
27041          * @param {Roo.bootstrap.UploadCropbox} this
27042          */
27043         "resize" : true,
27044         /**
27045          * @event rotate
27046          * Fire when rotate the image
27047          * @param {Roo.bootstrap.UploadCropbox} this
27048          * @param {String} pos
27049          */
27050         "rotate" : true,
27051         /**
27052          * @event inspect
27053          * Fire when inspect the file
27054          * @param {Roo.bootstrap.UploadCropbox} this
27055          * @param {Object} file
27056          */
27057         "inspect" : true,
27058         /**
27059          * @event upload
27060          * Fire when xhr upload the file
27061          * @param {Roo.bootstrap.UploadCropbox} this
27062          * @param {Object} data
27063          */
27064         "upload" : true,
27065         /**
27066          * @event arrange
27067          * Fire when arrange the file data
27068          * @param {Roo.bootstrap.UploadCropbox} this
27069          * @param {Object} formData
27070          */
27071         "arrange" : true
27072     });
27073     
27074     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27075 };
27076
27077 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27078     
27079     emptyText : 'Click to upload image',
27080     rotateNotify : 'Image is too small to rotate',
27081     errorTimeout : 3000,
27082     scale : 0,
27083     baseScale : 1,
27084     rotate : 0,
27085     dragable : false,
27086     pinching : false,
27087     mouseX : 0,
27088     mouseY : 0,
27089     cropData : false,
27090     minWidth : 300,
27091     minHeight : 300,
27092     file : false,
27093     exif : {},
27094     baseRotate : 1,
27095     cropType : 'image/jpeg',
27096     buttons : false,
27097     canvasLoaded : false,
27098     isDocument : false,
27099     method : 'POST',
27100     paramName : 'imageUpload',
27101     loadMask : true,
27102     loadingText : 'Loading...',
27103     maskEl : false,
27104     
27105     getAutoCreate : function()
27106     {
27107         var cfg = {
27108             tag : 'div',
27109             cls : 'roo-upload-cropbox',
27110             cn : [
27111                 {
27112                     tag : 'input',
27113                     cls : 'roo-upload-cropbox-selector',
27114                     type : 'file'
27115                 },
27116                 {
27117                     tag : 'div',
27118                     cls : 'roo-upload-cropbox-body',
27119                     style : 'cursor:pointer',
27120                     cn : [
27121                         {
27122                             tag : 'div',
27123                             cls : 'roo-upload-cropbox-preview'
27124                         },
27125                         {
27126                             tag : 'div',
27127                             cls : 'roo-upload-cropbox-thumb'
27128                         },
27129                         {
27130                             tag : 'div',
27131                             cls : 'roo-upload-cropbox-empty-notify',
27132                             html : this.emptyText
27133                         },
27134                         {
27135                             tag : 'div',
27136                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27137                             html : this.rotateNotify
27138                         }
27139                     ]
27140                 },
27141                 {
27142                     tag : 'div',
27143                     cls : 'roo-upload-cropbox-footer',
27144                     cn : {
27145                         tag : 'div',
27146                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27147                         cn : []
27148                     }
27149                 }
27150             ]
27151         };
27152         
27153         return cfg;
27154     },
27155     
27156     onRender : function(ct, position)
27157     {
27158         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27159         
27160         if (this.buttons.length) {
27161             
27162             Roo.each(this.buttons, function(bb) {
27163                 
27164                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27165                 
27166                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27167                 
27168             }, this);
27169         }
27170         
27171         if(this.loadMask){
27172             this.maskEl = this.el;
27173         }
27174     },
27175     
27176     initEvents : function()
27177     {
27178         this.urlAPI = (window.createObjectURL && window) || 
27179                                 (window.URL && URL.revokeObjectURL && URL) || 
27180                                 (window.webkitURL && webkitURL);
27181                         
27182         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27183         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27184         
27185         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27186         this.selectorEl.hide();
27187         
27188         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27189         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27190         
27191         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27192         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27193         this.thumbEl.hide();
27194         
27195         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27196         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27197         
27198         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27199         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27200         this.errorEl.hide();
27201         
27202         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27203         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27204         this.footerEl.hide();
27205         
27206         this.setThumbBoxSize();
27207         
27208         this.bind();
27209         
27210         this.resize();
27211         
27212         this.fireEvent('initial', this);
27213     },
27214
27215     bind : function()
27216     {
27217         var _this = this;
27218         
27219         window.addEventListener("resize", function() { _this.resize(); } );
27220         
27221         this.bodyEl.on('click', this.beforeSelectFile, this);
27222         
27223         if(Roo.isTouch){
27224             this.bodyEl.on('touchstart', this.onTouchStart, this);
27225             this.bodyEl.on('touchmove', this.onTouchMove, this);
27226             this.bodyEl.on('touchend', this.onTouchEnd, this);
27227         }
27228         
27229         if(!Roo.isTouch){
27230             this.bodyEl.on('mousedown', this.onMouseDown, this);
27231             this.bodyEl.on('mousemove', this.onMouseMove, this);
27232             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27233             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27234             Roo.get(document).on('mouseup', this.onMouseUp, this);
27235         }
27236         
27237         this.selectorEl.on('change', this.onFileSelected, this);
27238     },
27239     
27240     reset : function()
27241     {    
27242         this.scale = 0;
27243         this.baseScale = 1;
27244         this.rotate = 0;
27245         this.baseRotate = 1;
27246         this.dragable = false;
27247         this.pinching = false;
27248         this.mouseX = 0;
27249         this.mouseY = 0;
27250         this.cropData = false;
27251         this.notifyEl.dom.innerHTML = this.emptyText;
27252         
27253         this.selectorEl.dom.value = '';
27254         
27255     },
27256     
27257     resize : function()
27258     {
27259         if(this.fireEvent('resize', this) != false){
27260             this.setThumbBoxPosition();
27261             this.setCanvasPosition();
27262         }
27263     },
27264     
27265     onFooterButtonClick : function(e, el, o, type)
27266     {
27267         switch (type) {
27268             case 'rotate-left' :
27269                 this.onRotateLeft(e);
27270                 break;
27271             case 'rotate-right' :
27272                 this.onRotateRight(e);
27273                 break;
27274             case 'picture' :
27275                 this.beforeSelectFile(e);
27276                 break;
27277             case 'trash' :
27278                 this.trash(e);
27279                 break;
27280             case 'crop' :
27281                 this.crop(e);
27282                 break;
27283             case 'download' :
27284                 this.download(e);
27285                 break;
27286             default :
27287                 break;
27288         }
27289         
27290         this.fireEvent('footerbuttonclick', this, type);
27291     },
27292     
27293     beforeSelectFile : function(e)
27294     {
27295         e.preventDefault();
27296         
27297         if(this.fireEvent('beforeselectfile', this) != false){
27298             this.selectorEl.dom.click();
27299         }
27300     },
27301     
27302     onFileSelected : function(e)
27303     {
27304         e.preventDefault();
27305         
27306         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27307             return;
27308         }
27309         
27310         var file = this.selectorEl.dom.files[0];
27311         
27312         if(this.fireEvent('inspect', this, file) != false){
27313             this.prepare(file);
27314         }
27315         
27316     },
27317     
27318     trash : function(e)
27319     {
27320         this.fireEvent('trash', this);
27321     },
27322     
27323     download : function(e)
27324     {
27325         this.fireEvent('download', this);
27326     },
27327     
27328     loadCanvas : function(src)
27329     {   
27330         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27331             
27332             this.reset();
27333             
27334             this.imageEl = document.createElement('img');
27335             
27336             var _this = this;
27337             
27338             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27339             
27340             this.imageEl.src = src;
27341         }
27342     },
27343     
27344     onLoadCanvas : function()
27345     {   
27346         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27347         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27348         
27349         this.bodyEl.un('click', this.beforeSelectFile, this);
27350         
27351         this.notifyEl.hide();
27352         this.thumbEl.show();
27353         this.footerEl.show();
27354         
27355         this.baseRotateLevel();
27356         
27357         if(this.isDocument){
27358             this.setThumbBoxSize();
27359         }
27360         
27361         this.setThumbBoxPosition();
27362         
27363         this.baseScaleLevel();
27364         
27365         this.draw();
27366         
27367         this.resize();
27368         
27369         this.canvasLoaded = true;
27370         
27371         if(this.loadMask){
27372             this.maskEl.unmask();
27373         }
27374         
27375     },
27376     
27377     setCanvasPosition : function()
27378     {   
27379         if(!this.canvasEl){
27380             return;
27381         }
27382         
27383         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27384         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27385         
27386         this.previewEl.setLeft(pw);
27387         this.previewEl.setTop(ph);
27388         
27389     },
27390     
27391     onMouseDown : function(e)
27392     {   
27393         e.stopEvent();
27394         
27395         this.dragable = true;
27396         this.pinching = false;
27397         
27398         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27399             this.dragable = false;
27400             return;
27401         }
27402         
27403         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27404         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27405         
27406     },
27407     
27408     onMouseMove : function(e)
27409     {   
27410         e.stopEvent();
27411         
27412         if(!this.canvasLoaded){
27413             return;
27414         }
27415         
27416         if (!this.dragable){
27417             return;
27418         }
27419         
27420         var minX = Math.ceil(this.thumbEl.getLeft(true));
27421         var minY = Math.ceil(this.thumbEl.getTop(true));
27422         
27423         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27424         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27425         
27426         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27427         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27428         
27429         x = x - this.mouseX;
27430         y = y - this.mouseY;
27431         
27432         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27433         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27434         
27435         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27436         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27437         
27438         this.previewEl.setLeft(bgX);
27439         this.previewEl.setTop(bgY);
27440         
27441         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27442         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27443     },
27444     
27445     onMouseUp : function(e)
27446     {   
27447         e.stopEvent();
27448         
27449         this.dragable = false;
27450     },
27451     
27452     onMouseWheel : function(e)
27453     {   
27454         e.stopEvent();
27455         
27456         this.startScale = this.scale;
27457         
27458         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27459         
27460         if(!this.zoomable()){
27461             this.scale = this.startScale;
27462             return;
27463         }
27464         
27465         this.draw();
27466         
27467         return;
27468     },
27469     
27470     zoomable : function()
27471     {
27472         var minScale = this.thumbEl.getWidth() / this.minWidth;
27473         
27474         if(this.minWidth < this.minHeight){
27475             minScale = this.thumbEl.getHeight() / this.minHeight;
27476         }
27477         
27478         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27479         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27480         
27481         if(
27482                 this.isDocument &&
27483                 (this.rotate == 0 || this.rotate == 180) && 
27484                 (
27485                     width > this.imageEl.OriginWidth || 
27486                     height > this.imageEl.OriginHeight ||
27487                     (width < this.minWidth && height < this.minHeight)
27488                 )
27489         ){
27490             return false;
27491         }
27492         
27493         if(
27494                 this.isDocument &&
27495                 (this.rotate == 90 || this.rotate == 270) && 
27496                 (
27497                     width > this.imageEl.OriginWidth || 
27498                     height > this.imageEl.OriginHeight ||
27499                     (width < this.minHeight && height < this.minWidth)
27500                 )
27501         ){
27502             return false;
27503         }
27504         
27505         if(
27506                 !this.isDocument &&
27507                 (this.rotate == 0 || this.rotate == 180) && 
27508                 (
27509                     width < this.minWidth || 
27510                     width > this.imageEl.OriginWidth || 
27511                     height < this.minHeight || 
27512                     height > this.imageEl.OriginHeight
27513                 )
27514         ){
27515             return false;
27516         }
27517         
27518         if(
27519                 !this.isDocument &&
27520                 (this.rotate == 90 || this.rotate == 270) && 
27521                 (
27522                     width < this.minHeight || 
27523                     width > this.imageEl.OriginWidth || 
27524                     height < this.minWidth || 
27525                     height > this.imageEl.OriginHeight
27526                 )
27527         ){
27528             return false;
27529         }
27530         
27531         return true;
27532         
27533     },
27534     
27535     onRotateLeft : function(e)
27536     {   
27537         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27538             
27539             var minScale = this.thumbEl.getWidth() / this.minWidth;
27540             
27541             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27542             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27543             
27544             this.startScale = this.scale;
27545             
27546             while (this.getScaleLevel() < minScale){
27547             
27548                 this.scale = this.scale + 1;
27549                 
27550                 if(!this.zoomable()){
27551                     break;
27552                 }
27553                 
27554                 if(
27555                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27556                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27557                 ){
27558                     continue;
27559                 }
27560                 
27561                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27562
27563                 this.draw();
27564                 
27565                 return;
27566             }
27567             
27568             this.scale = this.startScale;
27569             
27570             this.onRotateFail();
27571             
27572             return false;
27573         }
27574         
27575         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27576
27577         if(this.isDocument){
27578             this.setThumbBoxSize();
27579             this.setThumbBoxPosition();
27580             this.setCanvasPosition();
27581         }
27582         
27583         this.draw();
27584         
27585         this.fireEvent('rotate', this, 'left');
27586         
27587     },
27588     
27589     onRotateRight : function(e)
27590     {
27591         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27592             
27593             var minScale = this.thumbEl.getWidth() / this.minWidth;
27594         
27595             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27596             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27597             
27598             this.startScale = this.scale;
27599             
27600             while (this.getScaleLevel() < minScale){
27601             
27602                 this.scale = this.scale + 1;
27603                 
27604                 if(!this.zoomable()){
27605                     break;
27606                 }
27607                 
27608                 if(
27609                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27610                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27611                 ){
27612                     continue;
27613                 }
27614                 
27615                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27616
27617                 this.draw();
27618                 
27619                 return;
27620             }
27621             
27622             this.scale = this.startScale;
27623             
27624             this.onRotateFail();
27625             
27626             return false;
27627         }
27628         
27629         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27630
27631         if(this.isDocument){
27632             this.setThumbBoxSize();
27633             this.setThumbBoxPosition();
27634             this.setCanvasPosition();
27635         }
27636         
27637         this.draw();
27638         
27639         this.fireEvent('rotate', this, 'right');
27640     },
27641     
27642     onRotateFail : function()
27643     {
27644         this.errorEl.show(true);
27645         
27646         var _this = this;
27647         
27648         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27649     },
27650     
27651     draw : function()
27652     {
27653         this.previewEl.dom.innerHTML = '';
27654         
27655         var canvasEl = document.createElement("canvas");
27656         
27657         var contextEl = canvasEl.getContext("2d");
27658         
27659         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27660         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27661         var center = this.imageEl.OriginWidth / 2;
27662         
27663         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27664             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27665             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27666             center = this.imageEl.OriginHeight / 2;
27667         }
27668         
27669         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27670         
27671         contextEl.translate(center, center);
27672         contextEl.rotate(this.rotate * Math.PI / 180);
27673
27674         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27675         
27676         this.canvasEl = document.createElement("canvas");
27677         
27678         this.contextEl = this.canvasEl.getContext("2d");
27679         
27680         switch (this.rotate) {
27681             case 0 :
27682                 
27683                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27684                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27685                 
27686                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27687                 
27688                 break;
27689             case 90 : 
27690                 
27691                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27692                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27693                 
27694                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27695                     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);
27696                     break;
27697                 }
27698                 
27699                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27700                 
27701                 break;
27702             case 180 :
27703                 
27704                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27705                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27706                 
27707                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27708                     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);
27709                     break;
27710                 }
27711                 
27712                 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);
27713                 
27714                 break;
27715             case 270 :
27716                 
27717                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27718                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27719         
27720                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27721                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27722                     break;
27723                 }
27724                 
27725                 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);
27726                 
27727                 break;
27728             default : 
27729                 break;
27730         }
27731         
27732         this.previewEl.appendChild(this.canvasEl);
27733         
27734         this.setCanvasPosition();
27735     },
27736     
27737     crop : function()
27738     {
27739         if(!this.canvasLoaded){
27740             return;
27741         }
27742         
27743         var imageCanvas = document.createElement("canvas");
27744         
27745         var imageContext = imageCanvas.getContext("2d");
27746         
27747         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27748         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27749         
27750         var center = imageCanvas.width / 2;
27751         
27752         imageContext.translate(center, center);
27753         
27754         imageContext.rotate(this.rotate * Math.PI / 180);
27755         
27756         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27757         
27758         var canvas = document.createElement("canvas");
27759         
27760         var context = canvas.getContext("2d");
27761                 
27762         canvas.width = this.minWidth;
27763         canvas.height = this.minHeight;
27764
27765         switch (this.rotate) {
27766             case 0 :
27767                 
27768                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27769                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27770                 
27771                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27772                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27773                 
27774                 var targetWidth = this.minWidth - 2 * x;
27775                 var targetHeight = this.minHeight - 2 * y;
27776                 
27777                 var scale = 1;
27778                 
27779                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27780                     scale = targetWidth / width;
27781                 }
27782                 
27783                 if(x > 0 && y == 0){
27784                     scale = targetHeight / height;
27785                 }
27786                 
27787                 if(x > 0 && y > 0){
27788                     scale = targetWidth / width;
27789                     
27790                     if(width < height){
27791                         scale = targetHeight / height;
27792                     }
27793                 }
27794                 
27795                 context.scale(scale, scale);
27796                 
27797                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27798                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27799
27800                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27801                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27802
27803                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27804                 
27805                 break;
27806             case 90 : 
27807                 
27808                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27809                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27810                 
27811                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27812                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27813                 
27814                 var targetWidth = this.minWidth - 2 * x;
27815                 var targetHeight = this.minHeight - 2 * y;
27816                 
27817                 var scale = 1;
27818                 
27819                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27820                     scale = targetWidth / width;
27821                 }
27822                 
27823                 if(x > 0 && y == 0){
27824                     scale = targetHeight / height;
27825                 }
27826                 
27827                 if(x > 0 && y > 0){
27828                     scale = targetWidth / width;
27829                     
27830                     if(width < height){
27831                         scale = targetHeight / height;
27832                     }
27833                 }
27834                 
27835                 context.scale(scale, scale);
27836                 
27837                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27838                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27839
27840                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27841                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27842                 
27843                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27844                 
27845                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27846                 
27847                 break;
27848             case 180 :
27849                 
27850                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27851                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27852                 
27853                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27854                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27855                 
27856                 var targetWidth = this.minWidth - 2 * x;
27857                 var targetHeight = this.minHeight - 2 * y;
27858                 
27859                 var scale = 1;
27860                 
27861                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27862                     scale = targetWidth / width;
27863                 }
27864                 
27865                 if(x > 0 && y == 0){
27866                     scale = targetHeight / height;
27867                 }
27868                 
27869                 if(x > 0 && y > 0){
27870                     scale = targetWidth / width;
27871                     
27872                     if(width < height){
27873                         scale = targetHeight / height;
27874                     }
27875                 }
27876                 
27877                 context.scale(scale, scale);
27878                 
27879                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27880                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27881
27882                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27883                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27884
27885                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27886                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27887                 
27888                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27889                 
27890                 break;
27891             case 270 :
27892                 
27893                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27894                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27895                 
27896                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27897                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27898                 
27899                 var targetWidth = this.minWidth - 2 * x;
27900                 var targetHeight = this.minHeight - 2 * y;
27901                 
27902                 var scale = 1;
27903                 
27904                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27905                     scale = targetWidth / width;
27906                 }
27907                 
27908                 if(x > 0 && y == 0){
27909                     scale = targetHeight / height;
27910                 }
27911                 
27912                 if(x > 0 && y > 0){
27913                     scale = targetWidth / width;
27914                     
27915                     if(width < height){
27916                         scale = targetHeight / height;
27917                     }
27918                 }
27919                 
27920                 context.scale(scale, scale);
27921                 
27922                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27923                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27924
27925                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27926                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27927                 
27928                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27929                 
27930                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27931                 
27932                 break;
27933             default : 
27934                 break;
27935         }
27936         
27937         this.cropData = canvas.toDataURL(this.cropType);
27938         
27939         if(this.fireEvent('crop', this, this.cropData) !== false){
27940             this.process(this.file, this.cropData);
27941         }
27942         
27943         return;
27944         
27945     },
27946     
27947     setThumbBoxSize : function()
27948     {
27949         var width, height;
27950         
27951         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27952             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27953             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27954             
27955             this.minWidth = width;
27956             this.minHeight = height;
27957             
27958             if(this.rotate == 90 || this.rotate == 270){
27959                 this.minWidth = height;
27960                 this.minHeight = width;
27961             }
27962         }
27963         
27964         height = 300;
27965         width = Math.ceil(this.minWidth * height / this.minHeight);
27966         
27967         if(this.minWidth > this.minHeight){
27968             width = 300;
27969             height = Math.ceil(this.minHeight * width / this.minWidth);
27970         }
27971         
27972         this.thumbEl.setStyle({
27973             width : width + 'px',
27974             height : height + 'px'
27975         });
27976
27977         return;
27978             
27979     },
27980     
27981     setThumbBoxPosition : function()
27982     {
27983         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27984         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27985         
27986         this.thumbEl.setLeft(x);
27987         this.thumbEl.setTop(y);
27988         
27989     },
27990     
27991     baseRotateLevel : function()
27992     {
27993         this.baseRotate = 1;
27994         
27995         if(
27996                 typeof(this.exif) != 'undefined' &&
27997                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27998                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27999         ){
28000             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28001         }
28002         
28003         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28004         
28005     },
28006     
28007     baseScaleLevel : function()
28008     {
28009         var width, height;
28010         
28011         if(this.isDocument){
28012             
28013             if(this.baseRotate == 6 || this.baseRotate == 8){
28014             
28015                 height = this.thumbEl.getHeight();
28016                 this.baseScale = height / this.imageEl.OriginWidth;
28017
28018                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28019                     width = this.thumbEl.getWidth();
28020                     this.baseScale = width / this.imageEl.OriginHeight;
28021                 }
28022
28023                 return;
28024             }
28025
28026             height = this.thumbEl.getHeight();
28027             this.baseScale = height / this.imageEl.OriginHeight;
28028
28029             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28030                 width = this.thumbEl.getWidth();
28031                 this.baseScale = width / this.imageEl.OriginWidth;
28032             }
28033
28034             return;
28035         }
28036         
28037         if(this.baseRotate == 6 || this.baseRotate == 8){
28038             
28039             width = this.thumbEl.getHeight();
28040             this.baseScale = width / this.imageEl.OriginHeight;
28041             
28042             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28043                 height = this.thumbEl.getWidth();
28044                 this.baseScale = height / this.imageEl.OriginHeight;
28045             }
28046             
28047             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28048                 height = this.thumbEl.getWidth();
28049                 this.baseScale = height / this.imageEl.OriginHeight;
28050                 
28051                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28052                     width = this.thumbEl.getHeight();
28053                     this.baseScale = width / this.imageEl.OriginWidth;
28054                 }
28055             }
28056             
28057             return;
28058         }
28059         
28060         width = this.thumbEl.getWidth();
28061         this.baseScale = width / this.imageEl.OriginWidth;
28062         
28063         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28064             height = this.thumbEl.getHeight();
28065             this.baseScale = height / this.imageEl.OriginHeight;
28066         }
28067         
28068         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28069             
28070             height = this.thumbEl.getHeight();
28071             this.baseScale = height / this.imageEl.OriginHeight;
28072             
28073             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28074                 width = this.thumbEl.getWidth();
28075                 this.baseScale = width / this.imageEl.OriginWidth;
28076             }
28077             
28078         }
28079         
28080         return;
28081     },
28082     
28083     getScaleLevel : function()
28084     {
28085         return this.baseScale * Math.pow(1.1, this.scale);
28086     },
28087     
28088     onTouchStart : function(e)
28089     {
28090         if(!this.canvasLoaded){
28091             this.beforeSelectFile(e);
28092             return;
28093         }
28094         
28095         var touches = e.browserEvent.touches;
28096         
28097         if(!touches){
28098             return;
28099         }
28100         
28101         if(touches.length == 1){
28102             this.onMouseDown(e);
28103             return;
28104         }
28105         
28106         if(touches.length != 2){
28107             return;
28108         }
28109         
28110         var coords = [];
28111         
28112         for(var i = 0, finger; finger = touches[i]; i++){
28113             coords.push(finger.pageX, finger.pageY);
28114         }
28115         
28116         var x = Math.pow(coords[0] - coords[2], 2);
28117         var y = Math.pow(coords[1] - coords[3], 2);
28118         
28119         this.startDistance = Math.sqrt(x + y);
28120         
28121         this.startScale = this.scale;
28122         
28123         this.pinching = true;
28124         this.dragable = false;
28125         
28126     },
28127     
28128     onTouchMove : function(e)
28129     {
28130         if(!this.pinching && !this.dragable){
28131             return;
28132         }
28133         
28134         var touches = e.browserEvent.touches;
28135         
28136         if(!touches){
28137             return;
28138         }
28139         
28140         if(this.dragable){
28141             this.onMouseMove(e);
28142             return;
28143         }
28144         
28145         var coords = [];
28146         
28147         for(var i = 0, finger; finger = touches[i]; i++){
28148             coords.push(finger.pageX, finger.pageY);
28149         }
28150         
28151         var x = Math.pow(coords[0] - coords[2], 2);
28152         var y = Math.pow(coords[1] - coords[3], 2);
28153         
28154         this.endDistance = Math.sqrt(x + y);
28155         
28156         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28157         
28158         if(!this.zoomable()){
28159             this.scale = this.startScale;
28160             return;
28161         }
28162         
28163         this.draw();
28164         
28165     },
28166     
28167     onTouchEnd : function(e)
28168     {
28169         this.pinching = false;
28170         this.dragable = false;
28171         
28172     },
28173     
28174     process : function(file, crop)
28175     {
28176         if(this.loadMask){
28177             this.maskEl.mask(this.loadingText);
28178         }
28179         
28180         this.xhr = new XMLHttpRequest();
28181         
28182         file.xhr = this.xhr;
28183
28184         this.xhr.open(this.method, this.url, true);
28185         
28186         var headers = {
28187             "Accept": "application/json",
28188             "Cache-Control": "no-cache",
28189             "X-Requested-With": "XMLHttpRequest"
28190         };
28191         
28192         for (var headerName in headers) {
28193             var headerValue = headers[headerName];
28194             if (headerValue) {
28195                 this.xhr.setRequestHeader(headerName, headerValue);
28196             }
28197         }
28198         
28199         var _this = this;
28200         
28201         this.xhr.onload = function()
28202         {
28203             _this.xhrOnLoad(_this.xhr);
28204         }
28205         
28206         this.xhr.onerror = function()
28207         {
28208             _this.xhrOnError(_this.xhr);
28209         }
28210         
28211         var formData = new FormData();
28212
28213         formData.append('returnHTML', 'NO');
28214         
28215         if(crop){
28216             formData.append('crop', crop);
28217         }
28218         
28219         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28220             formData.append(this.paramName, file, file.name);
28221         }
28222         
28223         if(typeof(file.filename) != 'undefined'){
28224             formData.append('filename', file.filename);
28225         }
28226         
28227         if(typeof(file.mimetype) != 'undefined'){
28228             formData.append('mimetype', file.mimetype);
28229         }
28230         
28231         if(this.fireEvent('arrange', this, formData) != false){
28232             this.xhr.send(formData);
28233         };
28234     },
28235     
28236     xhrOnLoad : function(xhr)
28237     {
28238         if(this.loadMask){
28239             this.maskEl.unmask();
28240         }
28241         
28242         if (xhr.readyState !== 4) {
28243             this.fireEvent('exception', this, xhr);
28244             return;
28245         }
28246
28247         var response = Roo.decode(xhr.responseText);
28248         
28249         if(!response.success){
28250             this.fireEvent('exception', this, xhr);
28251             return;
28252         }
28253         
28254         var response = Roo.decode(xhr.responseText);
28255         
28256         this.fireEvent('upload', this, response);
28257         
28258     },
28259     
28260     xhrOnError : function()
28261     {
28262         if(this.loadMask){
28263             this.maskEl.unmask();
28264         }
28265         
28266         Roo.log('xhr on error');
28267         
28268         var response = Roo.decode(xhr.responseText);
28269           
28270         Roo.log(response);
28271         
28272     },
28273     
28274     prepare : function(file)
28275     {   
28276         if(this.loadMask){
28277             this.maskEl.mask(this.loadingText);
28278         }
28279         
28280         this.file = false;
28281         this.exif = {};
28282         
28283         if(typeof(file) === 'string'){
28284             this.loadCanvas(file);
28285             return;
28286         }
28287         
28288         if(!file || !this.urlAPI){
28289             return;
28290         }
28291         
28292         this.file = file;
28293         this.cropType = file.type;
28294         
28295         var _this = this;
28296         
28297         if(this.fireEvent('prepare', this, this.file) != false){
28298             
28299             var reader = new FileReader();
28300             
28301             reader.onload = function (e) {
28302                 if (e.target.error) {
28303                     Roo.log(e.target.error);
28304                     return;
28305                 }
28306                 
28307                 var buffer = e.target.result,
28308                     dataView = new DataView(buffer),
28309                     offset = 2,
28310                     maxOffset = dataView.byteLength - 4,
28311                     markerBytes,
28312                     markerLength;
28313                 
28314                 if (dataView.getUint16(0) === 0xffd8) {
28315                     while (offset < maxOffset) {
28316                         markerBytes = dataView.getUint16(offset);
28317                         
28318                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28319                             markerLength = dataView.getUint16(offset + 2) + 2;
28320                             if (offset + markerLength > dataView.byteLength) {
28321                                 Roo.log('Invalid meta data: Invalid segment size.');
28322                                 break;
28323                             }
28324                             
28325                             if(markerBytes == 0xffe1){
28326                                 _this.parseExifData(
28327                                     dataView,
28328                                     offset,
28329                                     markerLength
28330                                 );
28331                             }
28332                             
28333                             offset += markerLength;
28334                             
28335                             continue;
28336                         }
28337                         
28338                         break;
28339                     }
28340                     
28341                 }
28342                 
28343                 var url = _this.urlAPI.createObjectURL(_this.file);
28344                 
28345                 _this.loadCanvas(url);
28346                 
28347                 return;
28348             }
28349             
28350             reader.readAsArrayBuffer(this.file);
28351             
28352         }
28353         
28354     },
28355     
28356     parseExifData : function(dataView, offset, length)
28357     {
28358         var tiffOffset = offset + 10,
28359             littleEndian,
28360             dirOffset;
28361     
28362         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28363             // No Exif data, might be XMP data instead
28364             return;
28365         }
28366         
28367         // Check for the ASCII code for "Exif" (0x45786966):
28368         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28369             // No Exif data, might be XMP data instead
28370             return;
28371         }
28372         if (tiffOffset + 8 > dataView.byteLength) {
28373             Roo.log('Invalid Exif data: Invalid segment size.');
28374             return;
28375         }
28376         // Check for the two null bytes:
28377         if (dataView.getUint16(offset + 8) !== 0x0000) {
28378             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28379             return;
28380         }
28381         // Check the byte alignment:
28382         switch (dataView.getUint16(tiffOffset)) {
28383         case 0x4949:
28384             littleEndian = true;
28385             break;
28386         case 0x4D4D:
28387             littleEndian = false;
28388             break;
28389         default:
28390             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28391             return;
28392         }
28393         // Check for the TIFF tag marker (0x002A):
28394         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28395             Roo.log('Invalid Exif data: Missing TIFF marker.');
28396             return;
28397         }
28398         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28399         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28400         
28401         this.parseExifTags(
28402             dataView,
28403             tiffOffset,
28404             tiffOffset + dirOffset,
28405             littleEndian
28406         );
28407     },
28408     
28409     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28410     {
28411         var tagsNumber,
28412             dirEndOffset,
28413             i;
28414         if (dirOffset + 6 > dataView.byteLength) {
28415             Roo.log('Invalid Exif data: Invalid directory offset.');
28416             return;
28417         }
28418         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28419         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28420         if (dirEndOffset + 4 > dataView.byteLength) {
28421             Roo.log('Invalid Exif data: Invalid directory size.');
28422             return;
28423         }
28424         for (i = 0; i < tagsNumber; i += 1) {
28425             this.parseExifTag(
28426                 dataView,
28427                 tiffOffset,
28428                 dirOffset + 2 + 12 * i, // tag offset
28429                 littleEndian
28430             );
28431         }
28432         // Return the offset to the next directory:
28433         return dataView.getUint32(dirEndOffset, littleEndian);
28434     },
28435     
28436     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28437     {
28438         var tag = dataView.getUint16(offset, littleEndian);
28439         
28440         this.exif[tag] = this.getExifValue(
28441             dataView,
28442             tiffOffset,
28443             offset,
28444             dataView.getUint16(offset + 2, littleEndian), // tag type
28445             dataView.getUint32(offset + 4, littleEndian), // tag length
28446             littleEndian
28447         );
28448     },
28449     
28450     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28451     {
28452         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28453             tagSize,
28454             dataOffset,
28455             values,
28456             i,
28457             str,
28458             c;
28459     
28460         if (!tagType) {
28461             Roo.log('Invalid Exif data: Invalid tag type.');
28462             return;
28463         }
28464         
28465         tagSize = tagType.size * length;
28466         // Determine if the value is contained in the dataOffset bytes,
28467         // or if the value at the dataOffset is a pointer to the actual data:
28468         dataOffset = tagSize > 4 ?
28469                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28470         if (dataOffset + tagSize > dataView.byteLength) {
28471             Roo.log('Invalid Exif data: Invalid data offset.');
28472             return;
28473         }
28474         if (length === 1) {
28475             return tagType.getValue(dataView, dataOffset, littleEndian);
28476         }
28477         values = [];
28478         for (i = 0; i < length; i += 1) {
28479             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28480         }
28481         
28482         if (tagType.ascii) {
28483             str = '';
28484             // Concatenate the chars:
28485             for (i = 0; i < values.length; i += 1) {
28486                 c = values[i];
28487                 // Ignore the terminating NULL byte(s):
28488                 if (c === '\u0000') {
28489                     break;
28490                 }
28491                 str += c;
28492             }
28493             return str;
28494         }
28495         return values;
28496     }
28497     
28498 });
28499
28500 Roo.apply(Roo.bootstrap.UploadCropbox, {
28501     tags : {
28502         'Orientation': 0x0112
28503     },
28504     
28505     Orientation: {
28506             1: 0, //'top-left',
28507 //            2: 'top-right',
28508             3: 180, //'bottom-right',
28509 //            4: 'bottom-left',
28510 //            5: 'left-top',
28511             6: 90, //'right-top',
28512 //            7: 'right-bottom',
28513             8: 270 //'left-bottom'
28514     },
28515     
28516     exifTagTypes : {
28517         // byte, 8-bit unsigned int:
28518         1: {
28519             getValue: function (dataView, dataOffset) {
28520                 return dataView.getUint8(dataOffset);
28521             },
28522             size: 1
28523         },
28524         // ascii, 8-bit byte:
28525         2: {
28526             getValue: function (dataView, dataOffset) {
28527                 return String.fromCharCode(dataView.getUint8(dataOffset));
28528             },
28529             size: 1,
28530             ascii: true
28531         },
28532         // short, 16 bit int:
28533         3: {
28534             getValue: function (dataView, dataOffset, littleEndian) {
28535                 return dataView.getUint16(dataOffset, littleEndian);
28536             },
28537             size: 2
28538         },
28539         // long, 32 bit int:
28540         4: {
28541             getValue: function (dataView, dataOffset, littleEndian) {
28542                 return dataView.getUint32(dataOffset, littleEndian);
28543             },
28544             size: 4
28545         },
28546         // rational = two long values, first is numerator, second is denominator:
28547         5: {
28548             getValue: function (dataView, dataOffset, littleEndian) {
28549                 return dataView.getUint32(dataOffset, littleEndian) /
28550                     dataView.getUint32(dataOffset + 4, littleEndian);
28551             },
28552             size: 8
28553         },
28554         // slong, 32 bit signed int:
28555         9: {
28556             getValue: function (dataView, dataOffset, littleEndian) {
28557                 return dataView.getInt32(dataOffset, littleEndian);
28558             },
28559             size: 4
28560         },
28561         // srational, two slongs, first is numerator, second is denominator:
28562         10: {
28563             getValue: function (dataView, dataOffset, littleEndian) {
28564                 return dataView.getInt32(dataOffset, littleEndian) /
28565                     dataView.getInt32(dataOffset + 4, littleEndian);
28566             },
28567             size: 8
28568         }
28569     },
28570     
28571     footer : {
28572         STANDARD : [
28573             {
28574                 tag : 'div',
28575                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28576                 action : 'rotate-left',
28577                 cn : [
28578                     {
28579                         tag : 'button',
28580                         cls : 'btn btn-default',
28581                         html : '<i class="fa fa-undo"></i>'
28582                     }
28583                 ]
28584             },
28585             {
28586                 tag : 'div',
28587                 cls : 'btn-group roo-upload-cropbox-picture',
28588                 action : 'picture',
28589                 cn : [
28590                     {
28591                         tag : 'button',
28592                         cls : 'btn btn-default',
28593                         html : '<i class="fa fa-picture-o"></i>'
28594                     }
28595                 ]
28596             },
28597             {
28598                 tag : 'div',
28599                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28600                 action : 'rotate-right',
28601                 cn : [
28602                     {
28603                         tag : 'button',
28604                         cls : 'btn btn-default',
28605                         html : '<i class="fa fa-repeat"></i>'
28606                     }
28607                 ]
28608             }
28609         ],
28610         DOCUMENT : [
28611             {
28612                 tag : 'div',
28613                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28614                 action : 'rotate-left',
28615                 cn : [
28616                     {
28617                         tag : 'button',
28618                         cls : 'btn btn-default',
28619                         html : '<i class="fa fa-undo"></i>'
28620                     }
28621                 ]
28622             },
28623             {
28624                 tag : 'div',
28625                 cls : 'btn-group roo-upload-cropbox-download',
28626                 action : 'download',
28627                 cn : [
28628                     {
28629                         tag : 'button',
28630                         cls : 'btn btn-default',
28631                         html : '<i class="fa fa-download"></i>'
28632                     }
28633                 ]
28634             },
28635             {
28636                 tag : 'div',
28637                 cls : 'btn-group roo-upload-cropbox-crop',
28638                 action : 'crop',
28639                 cn : [
28640                     {
28641                         tag : 'button',
28642                         cls : 'btn btn-default',
28643                         html : '<i class="fa fa-crop"></i>'
28644                     }
28645                 ]
28646             },
28647             {
28648                 tag : 'div',
28649                 cls : 'btn-group roo-upload-cropbox-trash',
28650                 action : 'trash',
28651                 cn : [
28652                     {
28653                         tag : 'button',
28654                         cls : 'btn btn-default',
28655                         html : '<i class="fa fa-trash"></i>'
28656                     }
28657                 ]
28658             },
28659             {
28660                 tag : 'div',
28661                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28662                 action : 'rotate-right',
28663                 cn : [
28664                     {
28665                         tag : 'button',
28666                         cls : 'btn btn-default',
28667                         html : '<i class="fa fa-repeat"></i>'
28668                     }
28669                 ]
28670             }
28671         ],
28672         ROTATOR : [
28673             {
28674                 tag : 'div',
28675                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28676                 action : 'rotate-left',
28677                 cn : [
28678                     {
28679                         tag : 'button',
28680                         cls : 'btn btn-default',
28681                         html : '<i class="fa fa-undo"></i>'
28682                     }
28683                 ]
28684             },
28685             {
28686                 tag : 'div',
28687                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28688                 action : 'rotate-right',
28689                 cn : [
28690                     {
28691                         tag : 'button',
28692                         cls : 'btn btn-default',
28693                         html : '<i class="fa fa-repeat"></i>'
28694                     }
28695                 ]
28696             }
28697         ]
28698     }
28699 });
28700
28701 /*
28702 * Licence: LGPL
28703 */
28704
28705 /**
28706  * @class Roo.bootstrap.DocumentManager
28707  * @extends Roo.bootstrap.Component
28708  * Bootstrap DocumentManager class
28709  * @cfg {String} paramName default 'imageUpload'
28710  * @cfg {String} toolTipName default 'filename'
28711  * @cfg {String} method default POST
28712  * @cfg {String} url action url
28713  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28714  * @cfg {Boolean} multiple multiple upload default true
28715  * @cfg {Number} thumbSize default 300
28716  * @cfg {String} fieldLabel
28717  * @cfg {Number} labelWidth default 4
28718  * @cfg {String} labelAlign (left|top) default left
28719  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28720 * @cfg {Number} labellg set the width of label (1-12)
28721  * @cfg {Number} labelmd set the width of label (1-12)
28722  * @cfg {Number} labelsm set the width of label (1-12)
28723  * @cfg {Number} labelxs set the width of label (1-12)
28724  * 
28725  * @constructor
28726  * Create a new DocumentManager
28727  * @param {Object} config The config object
28728  */
28729
28730 Roo.bootstrap.DocumentManager = function(config){
28731     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28732     
28733     this.files = [];
28734     this.delegates = [];
28735     
28736     this.addEvents({
28737         /**
28738          * @event initial
28739          * Fire when initial the DocumentManager
28740          * @param {Roo.bootstrap.DocumentManager} this
28741          */
28742         "initial" : true,
28743         /**
28744          * @event inspect
28745          * inspect selected file
28746          * @param {Roo.bootstrap.DocumentManager} this
28747          * @param {File} file
28748          */
28749         "inspect" : true,
28750         /**
28751          * @event exception
28752          * Fire when xhr load exception
28753          * @param {Roo.bootstrap.DocumentManager} this
28754          * @param {XMLHttpRequest} xhr
28755          */
28756         "exception" : true,
28757         /**
28758          * @event afterupload
28759          * Fire when xhr load exception
28760          * @param {Roo.bootstrap.DocumentManager} this
28761          * @param {XMLHttpRequest} xhr
28762          */
28763         "afterupload" : true,
28764         /**
28765          * @event prepare
28766          * prepare the form data
28767          * @param {Roo.bootstrap.DocumentManager} this
28768          * @param {Object} formData
28769          */
28770         "prepare" : true,
28771         /**
28772          * @event remove
28773          * Fire when remove the file
28774          * @param {Roo.bootstrap.DocumentManager} this
28775          * @param {Object} file
28776          */
28777         "remove" : true,
28778         /**
28779          * @event refresh
28780          * Fire after refresh the file
28781          * @param {Roo.bootstrap.DocumentManager} this
28782          */
28783         "refresh" : true,
28784         /**
28785          * @event click
28786          * Fire after click the image
28787          * @param {Roo.bootstrap.DocumentManager} this
28788          * @param {Object} file
28789          */
28790         "click" : true,
28791         /**
28792          * @event edit
28793          * Fire when upload a image and editable set to true
28794          * @param {Roo.bootstrap.DocumentManager} this
28795          * @param {Object} file
28796          */
28797         "edit" : true,
28798         /**
28799          * @event beforeselectfile
28800          * Fire before select file
28801          * @param {Roo.bootstrap.DocumentManager} this
28802          */
28803         "beforeselectfile" : true,
28804         /**
28805          * @event process
28806          * Fire before process file
28807          * @param {Roo.bootstrap.DocumentManager} this
28808          * @param {Object} file
28809          */
28810         "process" : true,
28811         /**
28812          * @event previewrendered
28813          * Fire when preview rendered
28814          * @param {Roo.bootstrap.DocumentManager} this
28815          * @param {Object} file
28816          */
28817         "previewrendered" : true,
28818         /**
28819          */
28820         "previewResize" : true
28821         
28822     });
28823 };
28824
28825 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28826     
28827     boxes : 0,
28828     inputName : '',
28829     thumbSize : 300,
28830     multiple : true,
28831     files : false,
28832     method : 'POST',
28833     url : '',
28834     paramName : 'imageUpload',
28835     toolTipName : 'filename',
28836     fieldLabel : '',
28837     labelWidth : 4,
28838     labelAlign : 'left',
28839     editable : true,
28840     delegates : false,
28841     xhr : false, 
28842     
28843     labellg : 0,
28844     labelmd : 0,
28845     labelsm : 0,
28846     labelxs : 0,
28847     
28848     getAutoCreate : function()
28849     {   
28850         var managerWidget = {
28851             tag : 'div',
28852             cls : 'roo-document-manager',
28853             cn : [
28854                 {
28855                     tag : 'input',
28856                     cls : 'roo-document-manager-selector',
28857                     type : 'file'
28858                 },
28859                 {
28860                     tag : 'div',
28861                     cls : 'roo-document-manager-uploader',
28862                     cn : [
28863                         {
28864                             tag : 'div',
28865                             cls : 'roo-document-manager-upload-btn',
28866                             html : '<i class="fa fa-plus"></i>'
28867                         }
28868                     ]
28869                     
28870                 }
28871             ]
28872         };
28873         
28874         var content = [
28875             {
28876                 tag : 'div',
28877                 cls : 'column col-md-12',
28878                 cn : managerWidget
28879             }
28880         ];
28881         
28882         if(this.fieldLabel.length){
28883             
28884             content = [
28885                 {
28886                     tag : 'div',
28887                     cls : 'column col-md-12',
28888                     html : this.fieldLabel
28889                 },
28890                 {
28891                     tag : 'div',
28892                     cls : 'column col-md-12',
28893                     cn : managerWidget
28894                 }
28895             ];
28896
28897             if(this.labelAlign == 'left'){
28898                 content = [
28899                     {
28900                         tag : 'div',
28901                         cls : 'column',
28902                         html : this.fieldLabel
28903                     },
28904                     {
28905                         tag : 'div',
28906                         cls : 'column',
28907                         cn : managerWidget
28908                     }
28909                 ];
28910                 
28911                 if(this.labelWidth > 12){
28912                     content[0].style = "width: " + this.labelWidth + 'px';
28913                 }
28914
28915                 if(this.labelWidth < 13 && this.labelmd == 0){
28916                     this.labelmd = this.labelWidth;
28917                 }
28918
28919                 if(this.labellg > 0){
28920                     content[0].cls += ' col-lg-' + this.labellg;
28921                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28922                 }
28923
28924                 if(this.labelmd > 0){
28925                     content[0].cls += ' col-md-' + this.labelmd;
28926                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28927                 }
28928
28929                 if(this.labelsm > 0){
28930                     content[0].cls += ' col-sm-' + this.labelsm;
28931                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28932                 }
28933
28934                 if(this.labelxs > 0){
28935                     content[0].cls += ' col-xs-' + this.labelxs;
28936                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28937                 }
28938                 
28939             }
28940         }
28941         
28942         var cfg = {
28943             tag : 'div',
28944             cls : 'row clearfix',
28945             cn : content
28946         };
28947         
28948         return cfg;
28949         
28950     },
28951     
28952     initEvents : function()
28953     {
28954         this.managerEl = this.el.select('.roo-document-manager', true).first();
28955         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28956         
28957         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28958         this.selectorEl.hide();
28959         
28960         if(this.multiple){
28961             this.selectorEl.attr('multiple', 'multiple');
28962         }
28963         
28964         this.selectorEl.on('change', this.onFileSelected, this);
28965         
28966         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28967         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28968         
28969         this.uploader.on('click', this.onUploaderClick, this);
28970         
28971         this.renderProgressDialog();
28972         
28973         var _this = this;
28974         
28975         window.addEventListener("resize", function() { _this.refresh(); } );
28976         
28977         this.fireEvent('initial', this);
28978     },
28979     
28980     renderProgressDialog : function()
28981     {
28982         var _this = this;
28983         
28984         this.progressDialog = new Roo.bootstrap.Modal({
28985             cls : 'roo-document-manager-progress-dialog',
28986             allow_close : false,
28987             title : '',
28988             buttons : [
28989                 {
28990                     name  :'cancel',
28991                     weight : 'danger',
28992                     html : 'Cancel'
28993                 }
28994             ], 
28995             listeners : { 
28996                 btnclick : function() {
28997                     _this.uploadCancel();
28998                     this.hide();
28999                 }
29000             }
29001         });
29002          
29003         this.progressDialog.render(Roo.get(document.body));
29004          
29005         this.progress = new Roo.bootstrap.Progress({
29006             cls : 'roo-document-manager-progress',
29007             active : true,
29008             striped : true
29009         });
29010         
29011         this.progress.render(this.progressDialog.getChildContainer());
29012         
29013         this.progressBar = new Roo.bootstrap.ProgressBar({
29014             cls : 'roo-document-manager-progress-bar',
29015             aria_valuenow : 0,
29016             aria_valuemin : 0,
29017             aria_valuemax : 12,
29018             panel : 'success'
29019         });
29020         
29021         this.progressBar.render(this.progress.getChildContainer());
29022     },
29023     
29024     onUploaderClick : function(e)
29025     {
29026         e.preventDefault();
29027      
29028         if(this.fireEvent('beforeselectfile', this) != false){
29029             this.selectorEl.dom.click();
29030         }
29031         
29032     },
29033     
29034     onFileSelected : function(e)
29035     {
29036         e.preventDefault();
29037         
29038         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29039             return;
29040         }
29041         
29042         Roo.each(this.selectorEl.dom.files, function(file){
29043             if(this.fireEvent('inspect', this, file) != false){
29044                 this.files.push(file);
29045             }
29046         }, this);
29047         
29048         this.queue();
29049         
29050     },
29051     
29052     queue : function()
29053     {
29054         this.selectorEl.dom.value = '';
29055         
29056         if(!this.files || !this.files.length){
29057             return;
29058         }
29059         
29060         if(this.boxes > 0 && this.files.length > this.boxes){
29061             this.files = this.files.slice(0, this.boxes);
29062         }
29063         
29064         this.uploader.show();
29065         
29066         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29067             this.uploader.hide();
29068         }
29069         
29070         var _this = this;
29071         
29072         var files = [];
29073         
29074         var docs = [];
29075         
29076         Roo.each(this.files, function(file){
29077             
29078             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29079                 var f = this.renderPreview(file);
29080                 files.push(f);
29081                 return;
29082             }
29083             
29084             if(file.type.indexOf('image') != -1){
29085                 this.delegates.push(
29086                     (function(){
29087                         _this.process(file);
29088                     }).createDelegate(this)
29089                 );
29090         
29091                 return;
29092             }
29093             
29094             docs.push(
29095                 (function(){
29096                     _this.process(file);
29097                 }).createDelegate(this)
29098             );
29099             
29100         }, this);
29101         
29102         this.files = files;
29103         
29104         this.delegates = this.delegates.concat(docs);
29105         
29106         if(!this.delegates.length){
29107             this.refresh();
29108             return;
29109         }
29110         
29111         this.progressBar.aria_valuemax = this.delegates.length;
29112         
29113         this.arrange();
29114         
29115         return;
29116     },
29117     
29118     arrange : function()
29119     {
29120         if(!this.delegates.length){
29121             this.progressDialog.hide();
29122             this.refresh();
29123             return;
29124         }
29125         
29126         var delegate = this.delegates.shift();
29127         
29128         this.progressDialog.show();
29129         
29130         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29131         
29132         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29133         
29134         delegate();
29135     },
29136     
29137     refresh : function()
29138     {
29139         this.uploader.show();
29140         
29141         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29142             this.uploader.hide();
29143         }
29144         
29145         Roo.isTouch ? this.closable(false) : this.closable(true);
29146         
29147         this.fireEvent('refresh', this);
29148     },
29149     
29150     onRemove : function(e, el, o)
29151     {
29152         e.preventDefault();
29153         
29154         this.fireEvent('remove', this, o);
29155         
29156     },
29157     
29158     remove : function(o)
29159     {
29160         var files = [];
29161         
29162         Roo.each(this.files, function(file){
29163             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29164                 files.push(file);
29165                 return;
29166             }
29167
29168             o.target.remove();
29169
29170         }, this);
29171         
29172         this.files = files;
29173         
29174         this.refresh();
29175     },
29176     
29177     clear : function()
29178     {
29179         Roo.each(this.files, function(file){
29180             if(!file.target){
29181                 return;
29182             }
29183             
29184             file.target.remove();
29185
29186         }, this);
29187         
29188         this.files = [];
29189         
29190         this.refresh();
29191     },
29192     
29193     onClick : function(e, el, o)
29194     {
29195         e.preventDefault();
29196         
29197         this.fireEvent('click', this, o);
29198         
29199     },
29200     
29201     closable : function(closable)
29202     {
29203         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29204             
29205             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29206             
29207             if(closable){
29208                 el.show();
29209                 return;
29210             }
29211             
29212             el.hide();
29213             
29214         }, this);
29215     },
29216     
29217     xhrOnLoad : function(xhr)
29218     {
29219         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29220             el.remove();
29221         }, this);
29222         
29223         if (xhr.readyState !== 4) {
29224             this.arrange();
29225             this.fireEvent('exception', this, xhr);
29226             return;
29227         }
29228
29229         var response = Roo.decode(xhr.responseText);
29230         
29231         if(!response.success){
29232             this.arrange();
29233             this.fireEvent('exception', this, xhr);
29234             return;
29235         }
29236         
29237         var file = this.renderPreview(response.data);
29238         
29239         this.files.push(file);
29240         
29241         this.arrange();
29242         
29243         this.fireEvent('afterupload', this, xhr);
29244         
29245     },
29246     
29247     xhrOnError : function(xhr)
29248     {
29249         Roo.log('xhr on error');
29250         
29251         var response = Roo.decode(xhr.responseText);
29252           
29253         Roo.log(response);
29254         
29255         this.arrange();
29256     },
29257     
29258     process : function(file)
29259     {
29260         if(this.fireEvent('process', this, file) !== false){
29261             if(this.editable && file.type.indexOf('image') != -1){
29262                 this.fireEvent('edit', this, file);
29263                 return;
29264             }
29265
29266             this.uploadStart(file, false);
29267
29268             return;
29269         }
29270         
29271     },
29272     
29273     uploadStart : function(file, crop)
29274     {
29275         this.xhr = new XMLHttpRequest();
29276         
29277         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29278             this.arrange();
29279             return;
29280         }
29281         
29282         file.xhr = this.xhr;
29283             
29284         this.managerEl.createChild({
29285             tag : 'div',
29286             cls : 'roo-document-manager-loading',
29287             cn : [
29288                 {
29289                     tag : 'div',
29290                     tooltip : file.name,
29291                     cls : 'roo-document-manager-thumb',
29292                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29293                 }
29294             ]
29295
29296         });
29297
29298         this.xhr.open(this.method, this.url, true);
29299         
29300         var headers = {
29301             "Accept": "application/json",
29302             "Cache-Control": "no-cache",
29303             "X-Requested-With": "XMLHttpRequest"
29304         };
29305         
29306         for (var headerName in headers) {
29307             var headerValue = headers[headerName];
29308             if (headerValue) {
29309                 this.xhr.setRequestHeader(headerName, headerValue);
29310             }
29311         }
29312         
29313         var _this = this;
29314         
29315         this.xhr.onload = function()
29316         {
29317             _this.xhrOnLoad(_this.xhr);
29318         }
29319         
29320         this.xhr.onerror = function()
29321         {
29322             _this.xhrOnError(_this.xhr);
29323         }
29324         
29325         var formData = new FormData();
29326
29327         formData.append('returnHTML', 'NO');
29328         
29329         if(crop){
29330             formData.append('crop', crop);
29331         }
29332         
29333         formData.append(this.paramName, file, file.name);
29334         
29335         var options = {
29336             file : file, 
29337             manually : false
29338         };
29339         
29340         if(this.fireEvent('prepare', this, formData, options) != false){
29341             
29342             if(options.manually){
29343                 return;
29344             }
29345             
29346             this.xhr.send(formData);
29347             return;
29348         };
29349         
29350         this.uploadCancel();
29351     },
29352     
29353     uploadCancel : function()
29354     {
29355         if (this.xhr) {
29356             this.xhr.abort();
29357         }
29358         
29359         this.delegates = [];
29360         
29361         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29362             el.remove();
29363         }, this);
29364         
29365         this.arrange();
29366     },
29367     
29368     renderPreview : function(file)
29369     {
29370         if(typeof(file.target) != 'undefined' && file.target){
29371             return file;
29372         }
29373         
29374         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29375         
29376         var previewEl = this.managerEl.createChild({
29377             tag : 'div',
29378             cls : 'roo-document-manager-preview',
29379             cn : [
29380                 {
29381                     tag : 'div',
29382                     tooltip : file[this.toolTipName],
29383                     cls : 'roo-document-manager-thumb',
29384                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29385                 },
29386                 {
29387                     tag : 'button',
29388                     cls : 'close',
29389                     html : '<i class="fa fa-times-circle"></i>'
29390                 }
29391             ]
29392         });
29393
29394         var close = previewEl.select('button.close', true).first();
29395
29396         close.on('click', this.onRemove, this, file);
29397
29398         file.target = previewEl;
29399
29400         var image = previewEl.select('img', true).first();
29401         
29402         var _this = this;
29403         
29404         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29405         
29406         image.on('click', this.onClick, this, file);
29407         
29408         this.fireEvent('previewrendered', this, file);
29409         
29410         return file;
29411         
29412     },
29413     
29414     onPreviewLoad : function(file, image)
29415     {
29416         if(typeof(file.target) == 'undefined' || !file.target){
29417             return;
29418         }
29419         
29420         var width = image.dom.naturalWidth || image.dom.width;
29421         var height = image.dom.naturalHeight || image.dom.height;
29422         
29423         if(!this.previewResize) {
29424             return;
29425         }
29426         
29427         if(width > height){
29428             file.target.addClass('wide');
29429             return;
29430         }
29431         
29432         file.target.addClass('tall');
29433         return;
29434         
29435     },
29436     
29437     uploadFromSource : function(file, crop)
29438     {
29439         this.xhr = new XMLHttpRequest();
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         formData.append('crop', crop);
29487         
29488         if(typeof(file.filename) != 'undefined'){
29489             formData.append('filename', file.filename);
29490         }
29491         
29492         if(typeof(file.mimetype) != 'undefined'){
29493             formData.append('mimetype', file.mimetype);
29494         }
29495         
29496         Roo.log(formData);
29497         
29498         if(this.fireEvent('prepare', this, formData) != false){
29499             this.xhr.send(formData);
29500         };
29501     }
29502 });
29503
29504 /*
29505 * Licence: LGPL
29506 */
29507
29508 /**
29509  * @class Roo.bootstrap.DocumentViewer
29510  * @extends Roo.bootstrap.Component
29511  * Bootstrap DocumentViewer class
29512  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29513  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29514  * 
29515  * @constructor
29516  * Create a new DocumentViewer
29517  * @param {Object} config The config object
29518  */
29519
29520 Roo.bootstrap.DocumentViewer = function(config){
29521     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29522     
29523     this.addEvents({
29524         /**
29525          * @event initial
29526          * Fire after initEvent
29527          * @param {Roo.bootstrap.DocumentViewer} this
29528          */
29529         "initial" : true,
29530         /**
29531          * @event click
29532          * Fire after click
29533          * @param {Roo.bootstrap.DocumentViewer} this
29534          */
29535         "click" : true,
29536         /**
29537          * @event download
29538          * Fire after download button
29539          * @param {Roo.bootstrap.DocumentViewer} this
29540          */
29541         "download" : true,
29542         /**
29543          * @event trash
29544          * Fire after trash button
29545          * @param {Roo.bootstrap.DocumentViewer} this
29546          */
29547         "trash" : true
29548         
29549     });
29550 };
29551
29552 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29553     
29554     showDownload : true,
29555     
29556     showTrash : true,
29557     
29558     getAutoCreate : function()
29559     {
29560         var cfg = {
29561             tag : 'div',
29562             cls : 'roo-document-viewer',
29563             cn : [
29564                 {
29565                     tag : 'div',
29566                     cls : 'roo-document-viewer-body',
29567                     cn : [
29568                         {
29569                             tag : 'div',
29570                             cls : 'roo-document-viewer-thumb',
29571                             cn : [
29572                                 {
29573                                     tag : 'img',
29574                                     cls : 'roo-document-viewer-image'
29575                                 }
29576                             ]
29577                         }
29578                     ]
29579                 },
29580                 {
29581                     tag : 'div',
29582                     cls : 'roo-document-viewer-footer',
29583                     cn : {
29584                         tag : 'div',
29585                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29586                         cn : [
29587                             {
29588                                 tag : 'div',
29589                                 cls : 'btn-group roo-document-viewer-download',
29590                                 cn : [
29591                                     {
29592                                         tag : 'button',
29593                                         cls : 'btn btn-default',
29594                                         html : '<i class="fa fa-download"></i>'
29595                                     }
29596                                 ]
29597                             },
29598                             {
29599                                 tag : 'div',
29600                                 cls : 'btn-group roo-document-viewer-trash',
29601                                 cn : [
29602                                     {
29603                                         tag : 'button',
29604                                         cls : 'btn btn-default',
29605                                         html : '<i class="fa fa-trash"></i>'
29606                                     }
29607                                 ]
29608                             }
29609                         ]
29610                     }
29611                 }
29612             ]
29613         };
29614         
29615         return cfg;
29616     },
29617     
29618     initEvents : function()
29619     {
29620         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29621         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29622         
29623         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29624         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29625         
29626         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29627         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29628         
29629         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29630         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29631         
29632         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29633         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29634         
29635         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29636         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29637         
29638         this.bodyEl.on('click', this.onClick, this);
29639         this.downloadBtn.on('click', this.onDownload, this);
29640         this.trashBtn.on('click', this.onTrash, this);
29641         
29642         this.downloadBtn.hide();
29643         this.trashBtn.hide();
29644         
29645         if(this.showDownload){
29646             this.downloadBtn.show();
29647         }
29648         
29649         if(this.showTrash){
29650             this.trashBtn.show();
29651         }
29652         
29653         if(!this.showDownload && !this.showTrash) {
29654             this.footerEl.hide();
29655         }
29656         
29657     },
29658     
29659     initial : function()
29660     {
29661         this.fireEvent('initial', this);
29662         
29663     },
29664     
29665     onClick : function(e)
29666     {
29667         e.preventDefault();
29668         
29669         this.fireEvent('click', this);
29670     },
29671     
29672     onDownload : function(e)
29673     {
29674         e.preventDefault();
29675         
29676         this.fireEvent('download', this);
29677     },
29678     
29679     onTrash : function(e)
29680     {
29681         e.preventDefault();
29682         
29683         this.fireEvent('trash', this);
29684     }
29685     
29686 });
29687 /*
29688  * - LGPL
29689  *
29690  * nav progress bar
29691  * 
29692  */
29693
29694 /**
29695  * @class Roo.bootstrap.NavProgressBar
29696  * @extends Roo.bootstrap.Component
29697  * Bootstrap NavProgressBar class
29698  * 
29699  * @constructor
29700  * Create a new nav progress bar
29701  * @param {Object} config The config object
29702  */
29703
29704 Roo.bootstrap.NavProgressBar = function(config){
29705     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29706
29707     this.bullets = this.bullets || [];
29708    
29709 //    Roo.bootstrap.NavProgressBar.register(this);
29710      this.addEvents({
29711         /**
29712              * @event changed
29713              * Fires when the active item changes
29714              * @param {Roo.bootstrap.NavProgressBar} this
29715              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29716              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29717          */
29718         'changed': true
29719      });
29720     
29721 };
29722
29723 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29724     
29725     bullets : [],
29726     barItems : [],
29727     
29728     getAutoCreate : function()
29729     {
29730         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29731         
29732         cfg = {
29733             tag : 'div',
29734             cls : 'roo-navigation-bar-group',
29735             cn : [
29736                 {
29737                     tag : 'div',
29738                     cls : 'roo-navigation-top-bar'
29739                 },
29740                 {
29741                     tag : 'div',
29742                     cls : 'roo-navigation-bullets-bar',
29743                     cn : [
29744                         {
29745                             tag : 'ul',
29746                             cls : 'roo-navigation-bar'
29747                         }
29748                     ]
29749                 },
29750                 
29751                 {
29752                     tag : 'div',
29753                     cls : 'roo-navigation-bottom-bar'
29754                 }
29755             ]
29756             
29757         };
29758         
29759         return cfg;
29760         
29761     },
29762     
29763     initEvents: function() 
29764     {
29765         
29766     },
29767     
29768     onRender : function(ct, position) 
29769     {
29770         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29771         
29772         if(this.bullets.length){
29773             Roo.each(this.bullets, function(b){
29774                this.addItem(b);
29775             }, this);
29776         }
29777         
29778         this.format();
29779         
29780     },
29781     
29782     addItem : function(cfg)
29783     {
29784         var item = new Roo.bootstrap.NavProgressItem(cfg);
29785         
29786         item.parentId = this.id;
29787         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29788         
29789         if(cfg.html){
29790             var top = new Roo.bootstrap.Element({
29791                 tag : 'div',
29792                 cls : 'roo-navigation-bar-text'
29793             });
29794             
29795             var bottom = new Roo.bootstrap.Element({
29796                 tag : 'div',
29797                 cls : 'roo-navigation-bar-text'
29798             });
29799             
29800             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29801             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29802             
29803             var topText = new Roo.bootstrap.Element({
29804                 tag : 'span',
29805                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29806             });
29807             
29808             var bottomText = new Roo.bootstrap.Element({
29809                 tag : 'span',
29810                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29811             });
29812             
29813             topText.onRender(top.el, null);
29814             bottomText.onRender(bottom.el, null);
29815             
29816             item.topEl = top;
29817             item.bottomEl = bottom;
29818         }
29819         
29820         this.barItems.push(item);
29821         
29822         return item;
29823     },
29824     
29825     getActive : function()
29826     {
29827         var active = false;
29828         
29829         Roo.each(this.barItems, function(v){
29830             
29831             if (!v.isActive()) {
29832                 return;
29833             }
29834             
29835             active = v;
29836             return false;
29837             
29838         });
29839         
29840         return active;
29841     },
29842     
29843     setActiveItem : function(item)
29844     {
29845         var prev = false;
29846         
29847         Roo.each(this.barItems, function(v){
29848             if (v.rid == item.rid) {
29849                 return ;
29850             }
29851             
29852             if (v.isActive()) {
29853                 v.setActive(false);
29854                 prev = v;
29855             }
29856         });
29857
29858         item.setActive(true);
29859         
29860         this.fireEvent('changed', this, item, prev);
29861     },
29862     
29863     getBarItem: function(rid)
29864     {
29865         var ret = false;
29866         
29867         Roo.each(this.barItems, function(e) {
29868             if (e.rid != rid) {
29869                 return;
29870             }
29871             
29872             ret =  e;
29873             return false;
29874         });
29875         
29876         return ret;
29877     },
29878     
29879     indexOfItem : function(item)
29880     {
29881         var index = false;
29882         
29883         Roo.each(this.barItems, function(v, i){
29884             
29885             if (v.rid != item.rid) {
29886                 return;
29887             }
29888             
29889             index = i;
29890             return false
29891         });
29892         
29893         return index;
29894     },
29895     
29896     setActiveNext : function()
29897     {
29898         var i = this.indexOfItem(this.getActive());
29899         
29900         if (i > this.barItems.length) {
29901             return;
29902         }
29903         
29904         this.setActiveItem(this.barItems[i+1]);
29905     },
29906     
29907     setActivePrev : function()
29908     {
29909         var i = this.indexOfItem(this.getActive());
29910         
29911         if (i  < 1) {
29912             return;
29913         }
29914         
29915         this.setActiveItem(this.barItems[i-1]);
29916     },
29917     
29918     format : function()
29919     {
29920         if(!this.barItems.length){
29921             return;
29922         }
29923      
29924         var width = 100 / this.barItems.length;
29925         
29926         Roo.each(this.barItems, function(i){
29927             i.el.setStyle('width', width + '%');
29928             i.topEl.el.setStyle('width', width + '%');
29929             i.bottomEl.el.setStyle('width', width + '%');
29930         }, this);
29931         
29932     }
29933     
29934 });
29935 /*
29936  * - LGPL
29937  *
29938  * Nav Progress Item
29939  * 
29940  */
29941
29942 /**
29943  * @class Roo.bootstrap.NavProgressItem
29944  * @extends Roo.bootstrap.Component
29945  * Bootstrap NavProgressItem class
29946  * @cfg {String} rid the reference id
29947  * @cfg {Boolean} active (true|false) Is item active default false
29948  * @cfg {Boolean} disabled (true|false) Is item active default false
29949  * @cfg {String} html
29950  * @cfg {String} position (top|bottom) text position default bottom
29951  * @cfg {String} icon show icon instead of number
29952  * 
29953  * @constructor
29954  * Create a new NavProgressItem
29955  * @param {Object} config The config object
29956  */
29957 Roo.bootstrap.NavProgressItem = function(config){
29958     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29959     this.addEvents({
29960         // raw events
29961         /**
29962          * @event click
29963          * The raw click event for the entire grid.
29964          * @param {Roo.bootstrap.NavProgressItem} this
29965          * @param {Roo.EventObject} e
29966          */
29967         "click" : true
29968     });
29969    
29970 };
29971
29972 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29973     
29974     rid : '',
29975     active : false,
29976     disabled : false,
29977     html : '',
29978     position : 'bottom',
29979     icon : false,
29980     
29981     getAutoCreate : function()
29982     {
29983         var iconCls = 'roo-navigation-bar-item-icon';
29984         
29985         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29986         
29987         var cfg = {
29988             tag: 'li',
29989             cls: 'roo-navigation-bar-item',
29990             cn : [
29991                 {
29992                     tag : 'i',
29993                     cls : iconCls
29994                 }
29995             ]
29996         };
29997         
29998         if(this.active){
29999             cfg.cls += ' active';
30000         }
30001         if(this.disabled){
30002             cfg.cls += ' disabled';
30003         }
30004         
30005         return cfg;
30006     },
30007     
30008     disable : function()
30009     {
30010         this.setDisabled(true);
30011     },
30012     
30013     enable : function()
30014     {
30015         this.setDisabled(false);
30016     },
30017     
30018     initEvents: function() 
30019     {
30020         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30021         
30022         this.iconEl.on('click', this.onClick, this);
30023     },
30024     
30025     onClick : function(e)
30026     {
30027         e.preventDefault();
30028         
30029         if(this.disabled){
30030             return;
30031         }
30032         
30033         if(this.fireEvent('click', this, e) === false){
30034             return;
30035         };
30036         
30037         this.parent().setActiveItem(this);
30038     },
30039     
30040     isActive: function () 
30041     {
30042         return this.active;
30043     },
30044     
30045     setActive : function(state)
30046     {
30047         if(this.active == state){
30048             return;
30049         }
30050         
30051         this.active = state;
30052         
30053         if (state) {
30054             this.el.addClass('active');
30055             return;
30056         }
30057         
30058         this.el.removeClass('active');
30059         
30060         return;
30061     },
30062     
30063     setDisabled : function(state)
30064     {
30065         if(this.disabled == state){
30066             return;
30067         }
30068         
30069         this.disabled = state;
30070         
30071         if (state) {
30072             this.el.addClass('disabled');
30073             return;
30074         }
30075         
30076         this.el.removeClass('disabled');
30077     },
30078     
30079     tooltipEl : function()
30080     {
30081         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30082     }
30083 });
30084  
30085
30086  /*
30087  * - LGPL
30088  *
30089  * FieldLabel
30090  * 
30091  */
30092
30093 /**
30094  * @class Roo.bootstrap.FieldLabel
30095  * @extends Roo.bootstrap.Component
30096  * Bootstrap FieldLabel class
30097  * @cfg {String} html contents of the element
30098  * @cfg {String} tag tag of the element default label
30099  * @cfg {String} cls class of the element
30100  * @cfg {String} target label target 
30101  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30102  * @cfg {String} invalidClass default "text-warning"
30103  * @cfg {String} validClass default "text-success"
30104  * @cfg {String} iconTooltip default "This field is required"
30105  * @cfg {String} indicatorpos (left|right) default left
30106  * 
30107  * @constructor
30108  * Create a new FieldLabel
30109  * @param {Object} config The config object
30110  */
30111
30112 Roo.bootstrap.FieldLabel = function(config){
30113     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30114     
30115     this.addEvents({
30116             /**
30117              * @event invalid
30118              * Fires after the field has been marked as invalid.
30119              * @param {Roo.form.FieldLabel} this
30120              * @param {String} msg The validation message
30121              */
30122             invalid : true,
30123             /**
30124              * @event valid
30125              * Fires after the field has been validated with no errors.
30126              * @param {Roo.form.FieldLabel} this
30127              */
30128             valid : true
30129         });
30130 };
30131
30132 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30133     
30134     tag: 'label',
30135     cls: '',
30136     html: '',
30137     target: '',
30138     allowBlank : true,
30139     invalidClass : 'has-warning',
30140     validClass : 'has-success',
30141     iconTooltip : 'This field is required',
30142     indicatorpos : 'left',
30143     
30144     getAutoCreate : function(){
30145         
30146         var cls = "";
30147         if (!this.allowBlank) {
30148             cls  = "visible";
30149         }
30150         
30151         var cfg = {
30152             tag : this.tag,
30153             cls : 'roo-bootstrap-field-label ' + this.cls,
30154             for : this.target,
30155             cn : [
30156                 {
30157                     tag : 'i',
30158                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30159                     tooltip : this.iconTooltip
30160                 },
30161                 {
30162                     tag : 'span',
30163                     html : this.html
30164                 }
30165             ] 
30166         };
30167         
30168         if(this.indicatorpos == 'right'){
30169             var cfg = {
30170                 tag : this.tag,
30171                 cls : 'roo-bootstrap-field-label ' + this.cls,
30172                 for : this.target,
30173                 cn : [
30174                     {
30175                         tag : 'span',
30176                         html : this.html
30177                     },
30178                     {
30179                         tag : 'i',
30180                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30181                         tooltip : this.iconTooltip
30182                     }
30183                 ] 
30184             };
30185         }
30186         
30187         return cfg;
30188     },
30189     
30190     initEvents: function() 
30191     {
30192         Roo.bootstrap.Element.superclass.initEvents.call(this);
30193         
30194         this.indicator = this.indicatorEl();
30195         
30196         if(this.indicator){
30197             this.indicator.removeClass('visible');
30198             this.indicator.addClass('invisible');
30199         }
30200         
30201         Roo.bootstrap.FieldLabel.register(this);
30202     },
30203     
30204     indicatorEl : function()
30205     {
30206         var indicator = this.el.select('i.roo-required-indicator',true).first();
30207         
30208         if(!indicator){
30209             return false;
30210         }
30211         
30212         return indicator;
30213         
30214     },
30215     
30216     /**
30217      * Mark this field as valid
30218      */
30219     markValid : function()
30220     {
30221         if(this.indicator){
30222             this.indicator.removeClass('visible');
30223             this.indicator.addClass('invisible');
30224         }
30225         
30226         this.el.removeClass(this.invalidClass);
30227         
30228         this.el.addClass(this.validClass);
30229         
30230         this.fireEvent('valid', this);
30231     },
30232     
30233     /**
30234      * Mark this field as invalid
30235      * @param {String} msg The validation message
30236      */
30237     markInvalid : function(msg)
30238     {
30239         if(this.indicator){
30240             this.indicator.removeClass('invisible');
30241             this.indicator.addClass('visible');
30242         }
30243         
30244         this.el.removeClass(this.validClass);
30245         
30246         this.el.addClass(this.invalidClass);
30247         
30248         this.fireEvent('invalid', this, msg);
30249     }
30250     
30251    
30252 });
30253
30254 Roo.apply(Roo.bootstrap.FieldLabel, {
30255     
30256     groups: {},
30257     
30258      /**
30259     * register a FieldLabel Group
30260     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30261     */
30262     register : function(label)
30263     {
30264         if(this.groups.hasOwnProperty(label.target)){
30265             return;
30266         }
30267      
30268         this.groups[label.target] = label;
30269         
30270     },
30271     /**
30272     * fetch a FieldLabel Group based on the target
30273     * @param {string} target
30274     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30275     */
30276     get: function(target) {
30277         if (typeof(this.groups[target]) == 'undefined') {
30278             return false;
30279         }
30280         
30281         return this.groups[target] ;
30282     }
30283 });
30284
30285  
30286
30287  /*
30288  * - LGPL
30289  *
30290  * page DateSplitField.
30291  * 
30292  */
30293
30294
30295 /**
30296  * @class Roo.bootstrap.DateSplitField
30297  * @extends Roo.bootstrap.Component
30298  * Bootstrap DateSplitField class
30299  * @cfg {string} fieldLabel - the label associated
30300  * @cfg {Number} labelWidth set the width of label (0-12)
30301  * @cfg {String} labelAlign (top|left)
30302  * @cfg {Boolean} dayAllowBlank (true|false) default false
30303  * @cfg {Boolean} monthAllowBlank (true|false) default false
30304  * @cfg {Boolean} yearAllowBlank (true|false) default false
30305  * @cfg {string} dayPlaceholder 
30306  * @cfg {string} monthPlaceholder
30307  * @cfg {string} yearPlaceholder
30308  * @cfg {string} dayFormat default 'd'
30309  * @cfg {string} monthFormat default 'm'
30310  * @cfg {string} yearFormat default 'Y'
30311  * @cfg {Number} labellg set the width of label (1-12)
30312  * @cfg {Number} labelmd set the width of label (1-12)
30313  * @cfg {Number} labelsm set the width of label (1-12)
30314  * @cfg {Number} labelxs set the width of label (1-12)
30315
30316  *     
30317  * @constructor
30318  * Create a new DateSplitField
30319  * @param {Object} config The config object
30320  */
30321
30322 Roo.bootstrap.DateSplitField = function(config){
30323     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30324     
30325     this.addEvents({
30326         // raw events
30327          /**
30328          * @event years
30329          * getting the data of years
30330          * @param {Roo.bootstrap.DateSplitField} this
30331          * @param {Object} years
30332          */
30333         "years" : true,
30334         /**
30335          * @event days
30336          * getting the data of days
30337          * @param {Roo.bootstrap.DateSplitField} this
30338          * @param {Object} days
30339          */
30340         "days" : true,
30341         /**
30342          * @event invalid
30343          * Fires after the field has been marked as invalid.
30344          * @param {Roo.form.Field} this
30345          * @param {String} msg The validation message
30346          */
30347         invalid : true,
30348        /**
30349          * @event valid
30350          * Fires after the field has been validated with no errors.
30351          * @param {Roo.form.Field} this
30352          */
30353         valid : true
30354     });
30355 };
30356
30357 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30358     
30359     fieldLabel : '',
30360     labelAlign : 'top',
30361     labelWidth : 3,
30362     dayAllowBlank : false,
30363     monthAllowBlank : false,
30364     yearAllowBlank : false,
30365     dayPlaceholder : '',
30366     monthPlaceholder : '',
30367     yearPlaceholder : '',
30368     dayFormat : 'd',
30369     monthFormat : 'm',
30370     yearFormat : 'Y',
30371     isFormField : true,
30372     labellg : 0,
30373     labelmd : 0,
30374     labelsm : 0,
30375     labelxs : 0,
30376     
30377     getAutoCreate : function()
30378     {
30379         var cfg = {
30380             tag : 'div',
30381             cls : 'row roo-date-split-field-group',
30382             cn : [
30383                 {
30384                     tag : 'input',
30385                     type : 'hidden',
30386                     cls : 'form-hidden-field roo-date-split-field-group-value',
30387                     name : this.name
30388                 }
30389             ]
30390         };
30391         
30392         var labelCls = 'col-md-12';
30393         var contentCls = 'col-md-4';
30394         
30395         if(this.fieldLabel){
30396             
30397             var label = {
30398                 tag : 'div',
30399                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30400                 cn : [
30401                     {
30402                         tag : 'label',
30403                         html : this.fieldLabel
30404                     }
30405                 ]
30406             };
30407             
30408             if(this.labelAlign == 'left'){
30409             
30410                 if(this.labelWidth > 12){
30411                     label.style = "width: " + this.labelWidth + 'px';
30412                 }
30413
30414                 if(this.labelWidth < 13 && this.labelmd == 0){
30415                     this.labelmd = this.labelWidth;
30416                 }
30417
30418                 if(this.labellg > 0){
30419                     labelCls = ' col-lg-' + this.labellg;
30420                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30421                 }
30422
30423                 if(this.labelmd > 0){
30424                     labelCls = ' col-md-' + this.labelmd;
30425                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30426                 }
30427
30428                 if(this.labelsm > 0){
30429                     labelCls = ' col-sm-' + this.labelsm;
30430                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30431                 }
30432
30433                 if(this.labelxs > 0){
30434                     labelCls = ' col-xs-' + this.labelxs;
30435                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30436                 }
30437             }
30438             
30439             label.cls += ' ' + labelCls;
30440             
30441             cfg.cn.push(label);
30442         }
30443         
30444         Roo.each(['day', 'month', 'year'], function(t){
30445             cfg.cn.push({
30446                 tag : 'div',
30447                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30448             });
30449         }, this);
30450         
30451         return cfg;
30452     },
30453     
30454     inputEl: function ()
30455     {
30456         return this.el.select('.roo-date-split-field-group-value', true).first();
30457     },
30458     
30459     onRender : function(ct, position) 
30460     {
30461         var _this = this;
30462         
30463         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30464         
30465         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30466         
30467         this.dayField = new Roo.bootstrap.ComboBox({
30468             allowBlank : this.dayAllowBlank,
30469             alwaysQuery : true,
30470             displayField : 'value',
30471             editable : false,
30472             fieldLabel : '',
30473             forceSelection : true,
30474             mode : 'local',
30475             placeholder : this.dayPlaceholder,
30476             selectOnFocus : true,
30477             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30478             triggerAction : 'all',
30479             typeAhead : true,
30480             valueField : 'value',
30481             store : new Roo.data.SimpleStore({
30482                 data : (function() {    
30483                     var days = [];
30484                     _this.fireEvent('days', _this, days);
30485                     return days;
30486                 })(),
30487                 fields : [ 'value' ]
30488             }),
30489             listeners : {
30490                 select : function (_self, record, index)
30491                 {
30492                     _this.setValue(_this.getValue());
30493                 }
30494             }
30495         });
30496
30497         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30498         
30499         this.monthField = new Roo.bootstrap.MonthField({
30500             after : '<i class=\"fa fa-calendar\"></i>',
30501             allowBlank : this.monthAllowBlank,
30502             placeholder : this.monthPlaceholder,
30503             readOnly : true,
30504             listeners : {
30505                 render : function (_self)
30506                 {
30507                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30508                         e.preventDefault();
30509                         _self.focus();
30510                     });
30511                 },
30512                 select : function (_self, oldvalue, newvalue)
30513                 {
30514                     _this.setValue(_this.getValue());
30515                 }
30516             }
30517         });
30518         
30519         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30520         
30521         this.yearField = new Roo.bootstrap.ComboBox({
30522             allowBlank : this.yearAllowBlank,
30523             alwaysQuery : true,
30524             displayField : 'value',
30525             editable : false,
30526             fieldLabel : '',
30527             forceSelection : true,
30528             mode : 'local',
30529             placeholder : this.yearPlaceholder,
30530             selectOnFocus : true,
30531             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30532             triggerAction : 'all',
30533             typeAhead : true,
30534             valueField : 'value',
30535             store : new Roo.data.SimpleStore({
30536                 data : (function() {
30537                     var years = [];
30538                     _this.fireEvent('years', _this, years);
30539                     return years;
30540                 })(),
30541                 fields : [ 'value' ]
30542             }),
30543             listeners : {
30544                 select : function (_self, record, index)
30545                 {
30546                     _this.setValue(_this.getValue());
30547                 }
30548             }
30549         });
30550
30551         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30552     },
30553     
30554     setValue : function(v, format)
30555     {
30556         this.inputEl.dom.value = v;
30557         
30558         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30559         
30560         var d = Date.parseDate(v, f);
30561         
30562         if(!d){
30563             this.validate();
30564             return;
30565         }
30566         
30567         this.setDay(d.format(this.dayFormat));
30568         this.setMonth(d.format(this.monthFormat));
30569         this.setYear(d.format(this.yearFormat));
30570         
30571         this.validate();
30572         
30573         return;
30574     },
30575     
30576     setDay : function(v)
30577     {
30578         this.dayField.setValue(v);
30579         this.inputEl.dom.value = this.getValue();
30580         this.validate();
30581         return;
30582     },
30583     
30584     setMonth : function(v)
30585     {
30586         this.monthField.setValue(v, true);
30587         this.inputEl.dom.value = this.getValue();
30588         this.validate();
30589         return;
30590     },
30591     
30592     setYear : function(v)
30593     {
30594         this.yearField.setValue(v);
30595         this.inputEl.dom.value = this.getValue();
30596         this.validate();
30597         return;
30598     },
30599     
30600     getDay : function()
30601     {
30602         return this.dayField.getValue();
30603     },
30604     
30605     getMonth : function()
30606     {
30607         return this.monthField.getValue();
30608     },
30609     
30610     getYear : function()
30611     {
30612         return this.yearField.getValue();
30613     },
30614     
30615     getValue : function()
30616     {
30617         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30618         
30619         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30620         
30621         return date;
30622     },
30623     
30624     reset : function()
30625     {
30626         this.setDay('');
30627         this.setMonth('');
30628         this.setYear('');
30629         this.inputEl.dom.value = '';
30630         this.validate();
30631         return;
30632     },
30633     
30634     validate : function()
30635     {
30636         var d = this.dayField.validate();
30637         var m = this.monthField.validate();
30638         var y = this.yearField.validate();
30639         
30640         var valid = true;
30641         
30642         if(
30643                 (!this.dayAllowBlank && !d) ||
30644                 (!this.monthAllowBlank && !m) ||
30645                 (!this.yearAllowBlank && !y)
30646         ){
30647             valid = false;
30648         }
30649         
30650         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30651             return valid;
30652         }
30653         
30654         if(valid){
30655             this.markValid();
30656             return valid;
30657         }
30658         
30659         this.markInvalid();
30660         
30661         return valid;
30662     },
30663     
30664     markValid : function()
30665     {
30666         
30667         var label = this.el.select('label', true).first();
30668         var icon = this.el.select('i.fa-star', true).first();
30669
30670         if(label && icon){
30671             icon.remove();
30672         }
30673         
30674         this.fireEvent('valid', this);
30675     },
30676     
30677      /**
30678      * Mark this field as invalid
30679      * @param {String} msg The validation message
30680      */
30681     markInvalid : function(msg)
30682     {
30683         
30684         var label = this.el.select('label', true).first();
30685         var icon = this.el.select('i.fa-star', true).first();
30686
30687         if(label && !icon){
30688             this.el.select('.roo-date-split-field-label', true).createChild({
30689                 tag : 'i',
30690                 cls : 'text-danger fa fa-lg fa-star',
30691                 tooltip : 'This field is required',
30692                 style : 'margin-right:5px;'
30693             }, label, true);
30694         }
30695         
30696         this.fireEvent('invalid', this, msg);
30697     },
30698     
30699     clearInvalid : function()
30700     {
30701         var label = this.el.select('label', true).first();
30702         var icon = this.el.select('i.fa-star', true).first();
30703
30704         if(label && icon){
30705             icon.remove();
30706         }
30707         
30708         this.fireEvent('valid', this);
30709     },
30710     
30711     getName: function()
30712     {
30713         return this.name;
30714     }
30715     
30716 });
30717
30718  /**
30719  *
30720  * This is based on 
30721  * http://masonry.desandro.com
30722  *
30723  * The idea is to render all the bricks based on vertical width...
30724  *
30725  * The original code extends 'outlayer' - we might need to use that....
30726  * 
30727  */
30728
30729
30730 /**
30731  * @class Roo.bootstrap.LayoutMasonry
30732  * @extends Roo.bootstrap.Component
30733  * Bootstrap Layout Masonry class
30734  * 
30735  * @constructor
30736  * Create a new Element
30737  * @param {Object} config The config object
30738  */
30739
30740 Roo.bootstrap.LayoutMasonry = function(config){
30741     
30742     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30743     
30744     this.bricks = [];
30745     
30746     Roo.bootstrap.LayoutMasonry.register(this);
30747     
30748     this.addEvents({
30749         // raw events
30750         /**
30751          * @event layout
30752          * Fire after layout the items
30753          * @param {Roo.bootstrap.LayoutMasonry} this
30754          * @param {Roo.EventObject} e
30755          */
30756         "layout" : true
30757     });
30758     
30759 };
30760
30761 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30762     
30763     /**
30764      * @cfg {Boolean} isLayoutInstant = no animation?
30765      */   
30766     isLayoutInstant : false, // needed?
30767    
30768     /**
30769      * @cfg {Number} boxWidth  width of the columns
30770      */   
30771     boxWidth : 450,
30772     
30773       /**
30774      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30775      */   
30776     boxHeight : 0,
30777     
30778     /**
30779      * @cfg {Number} padWidth padding below box..
30780      */   
30781     padWidth : 10, 
30782     
30783     /**
30784      * @cfg {Number} gutter gutter width..
30785      */   
30786     gutter : 10,
30787     
30788      /**
30789      * @cfg {Number} maxCols maximum number of columns
30790      */   
30791     
30792     maxCols: 0,
30793     
30794     /**
30795      * @cfg {Boolean} isAutoInitial defalut true
30796      */   
30797     isAutoInitial : true, 
30798     
30799     containerWidth: 0,
30800     
30801     /**
30802      * @cfg {Boolean} isHorizontal defalut false
30803      */   
30804     isHorizontal : false, 
30805
30806     currentSize : null,
30807     
30808     tag: 'div',
30809     
30810     cls: '',
30811     
30812     bricks: null, //CompositeElement
30813     
30814     cols : 1,
30815     
30816     _isLayoutInited : false,
30817     
30818 //    isAlternative : false, // only use for vertical layout...
30819     
30820     /**
30821      * @cfg {Number} alternativePadWidth padding below box..
30822      */   
30823     alternativePadWidth : 50,
30824     
30825     selectedBrick : [],
30826     
30827     getAutoCreate : function(){
30828         
30829         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30830         
30831         var cfg = {
30832             tag: this.tag,
30833             cls: 'blog-masonary-wrapper ' + this.cls,
30834             cn : {
30835                 cls : 'mas-boxes masonary'
30836             }
30837         };
30838         
30839         return cfg;
30840     },
30841     
30842     getChildContainer: function( )
30843     {
30844         if (this.boxesEl) {
30845             return this.boxesEl;
30846         }
30847         
30848         this.boxesEl = this.el.select('.mas-boxes').first();
30849         
30850         return this.boxesEl;
30851     },
30852     
30853     
30854     initEvents : function()
30855     {
30856         var _this = this;
30857         
30858         if(this.isAutoInitial){
30859             Roo.log('hook children rendered');
30860             this.on('childrenrendered', function() {
30861                 Roo.log('children rendered');
30862                 _this.initial();
30863             } ,this);
30864         }
30865     },
30866     
30867     initial : function()
30868     {
30869         this.selectedBrick = [];
30870         
30871         this.currentSize = this.el.getBox(true);
30872         
30873         Roo.EventManager.onWindowResize(this.resize, this); 
30874
30875         if(!this.isAutoInitial){
30876             this.layout();
30877             return;
30878         }
30879         
30880         this.layout();
30881         
30882         return;
30883         //this.layout.defer(500,this);
30884         
30885     },
30886     
30887     resize : function()
30888     {
30889         var cs = this.el.getBox(true);
30890         
30891         if (
30892                 this.currentSize.width == cs.width && 
30893                 this.currentSize.x == cs.x && 
30894                 this.currentSize.height == cs.height && 
30895                 this.currentSize.y == cs.y 
30896         ) {
30897             Roo.log("no change in with or X or Y");
30898             return;
30899         }
30900         
30901         this.currentSize = cs;
30902         
30903         this.layout();
30904         
30905     },
30906     
30907     layout : function()
30908     {   
30909         this._resetLayout();
30910         
30911         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30912         
30913         this.layoutItems( isInstant );
30914       
30915         this._isLayoutInited = true;
30916         
30917         this.fireEvent('layout', this);
30918         
30919     },
30920     
30921     _resetLayout : function()
30922     {
30923         if(this.isHorizontal){
30924             this.horizontalMeasureColumns();
30925             return;
30926         }
30927         
30928         this.verticalMeasureColumns();
30929         
30930     },
30931     
30932     verticalMeasureColumns : function()
30933     {
30934         this.getContainerWidth();
30935         
30936 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30937 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30938 //            return;
30939 //        }
30940         
30941         var boxWidth = this.boxWidth + this.padWidth;
30942         
30943         if(this.containerWidth < this.boxWidth){
30944             boxWidth = this.containerWidth
30945         }
30946         
30947         var containerWidth = this.containerWidth;
30948         
30949         var cols = Math.floor(containerWidth / boxWidth);
30950         
30951         this.cols = Math.max( cols, 1 );
30952         
30953         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30954         
30955         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30956         
30957         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30958         
30959         this.colWidth = boxWidth + avail - this.padWidth;
30960         
30961         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30962         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30963     },
30964     
30965     horizontalMeasureColumns : function()
30966     {
30967         this.getContainerWidth();
30968         
30969         var boxWidth = this.boxWidth;
30970         
30971         if(this.containerWidth < boxWidth){
30972             boxWidth = this.containerWidth;
30973         }
30974         
30975         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30976         
30977         this.el.setHeight(boxWidth);
30978         
30979     },
30980     
30981     getContainerWidth : function()
30982     {
30983         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30984     },
30985     
30986     layoutItems : function( isInstant )
30987     {
30988         Roo.log(this.bricks);
30989         
30990         var items = Roo.apply([], this.bricks);
30991         
30992         if(this.isHorizontal){
30993             this._horizontalLayoutItems( items , isInstant );
30994             return;
30995         }
30996         
30997 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30998 //            this._verticalAlternativeLayoutItems( items , isInstant );
30999 //            return;
31000 //        }
31001         
31002         this._verticalLayoutItems( items , isInstant );
31003         
31004     },
31005     
31006     _verticalLayoutItems : function ( items , isInstant)
31007     {
31008         if ( !items || !items.length ) {
31009             return;
31010         }
31011         
31012         var standard = [
31013             ['xs', 'xs', 'xs', 'tall'],
31014             ['xs', 'xs', 'tall'],
31015             ['xs', 'xs', 'sm'],
31016             ['xs', 'xs', 'xs'],
31017             ['xs', 'tall'],
31018             ['xs', 'sm'],
31019             ['xs', 'xs'],
31020             ['xs'],
31021             
31022             ['sm', 'xs', 'xs'],
31023             ['sm', 'xs'],
31024             ['sm'],
31025             
31026             ['tall', 'xs', 'xs', 'xs'],
31027             ['tall', 'xs', 'xs'],
31028             ['tall', 'xs'],
31029             ['tall']
31030             
31031         ];
31032         
31033         var queue = [];
31034         
31035         var boxes = [];
31036         
31037         var box = [];
31038         
31039         Roo.each(items, function(item, k){
31040             
31041             switch (item.size) {
31042                 // these layouts take up a full box,
31043                 case 'md' :
31044                 case 'md-left' :
31045                 case 'md-right' :
31046                 case 'wide' :
31047                     
31048                     if(box.length){
31049                         boxes.push(box);
31050                         box = [];
31051                     }
31052                     
31053                     boxes.push([item]);
31054                     
31055                     break;
31056                     
31057                 case 'xs' :
31058                 case 'sm' :
31059                 case 'tall' :
31060                     
31061                     box.push(item);
31062                     
31063                     break;
31064                 default :
31065                     break;
31066                     
31067             }
31068             
31069         }, this);
31070         
31071         if(box.length){
31072             boxes.push(box);
31073             box = [];
31074         }
31075         
31076         var filterPattern = function(box, length)
31077         {
31078             if(!box.length){
31079                 return;
31080             }
31081             
31082             var match = false;
31083             
31084             var pattern = box.slice(0, length);
31085             
31086             var format = [];
31087             
31088             Roo.each(pattern, function(i){
31089                 format.push(i.size);
31090             }, this);
31091             
31092             Roo.each(standard, function(s){
31093                 
31094                 if(String(s) != String(format)){
31095                     return;
31096                 }
31097                 
31098                 match = true;
31099                 return false;
31100                 
31101             }, this);
31102             
31103             if(!match && length == 1){
31104                 return;
31105             }
31106             
31107             if(!match){
31108                 filterPattern(box, length - 1);
31109                 return;
31110             }
31111                 
31112             queue.push(pattern);
31113
31114             box = box.slice(length, box.length);
31115
31116             filterPattern(box, 4);
31117
31118             return;
31119             
31120         }
31121         
31122         Roo.each(boxes, function(box, k){
31123             
31124             if(!box.length){
31125                 return;
31126             }
31127             
31128             if(box.length == 1){
31129                 queue.push(box);
31130                 return;
31131             }
31132             
31133             filterPattern(box, 4);
31134             
31135         }, this);
31136         
31137         this._processVerticalLayoutQueue( queue, isInstant );
31138         
31139     },
31140     
31141 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31142 //    {
31143 //        if ( !items || !items.length ) {
31144 //            return;
31145 //        }
31146 //
31147 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31148 //        
31149 //    },
31150     
31151     _horizontalLayoutItems : function ( items , isInstant)
31152     {
31153         if ( !items || !items.length || items.length < 3) {
31154             return;
31155         }
31156         
31157         items.reverse();
31158         
31159         var eItems = items.slice(0, 3);
31160         
31161         items = items.slice(3, items.length);
31162         
31163         var standard = [
31164             ['xs', 'xs', 'xs', 'wide'],
31165             ['xs', 'xs', 'wide'],
31166             ['xs', 'xs', 'sm'],
31167             ['xs', 'xs', 'xs'],
31168             ['xs', 'wide'],
31169             ['xs', 'sm'],
31170             ['xs', 'xs'],
31171             ['xs'],
31172             
31173             ['sm', 'xs', 'xs'],
31174             ['sm', 'xs'],
31175             ['sm'],
31176             
31177             ['wide', 'xs', 'xs', 'xs'],
31178             ['wide', 'xs', 'xs'],
31179             ['wide', 'xs'],
31180             ['wide'],
31181             
31182             ['wide-thin']
31183         ];
31184         
31185         var queue = [];
31186         
31187         var boxes = [];
31188         
31189         var box = [];
31190         
31191         Roo.each(items, function(item, k){
31192             
31193             switch (item.size) {
31194                 case 'md' :
31195                 case 'md-left' :
31196                 case 'md-right' :
31197                 case 'tall' :
31198                     
31199                     if(box.length){
31200                         boxes.push(box);
31201                         box = [];
31202                     }
31203                     
31204                     boxes.push([item]);
31205                     
31206                     break;
31207                     
31208                 case 'xs' :
31209                 case 'sm' :
31210                 case 'wide' :
31211                 case 'wide-thin' :
31212                     
31213                     box.push(item);
31214                     
31215                     break;
31216                 default :
31217                     break;
31218                     
31219             }
31220             
31221         }, this);
31222         
31223         if(box.length){
31224             boxes.push(box);
31225             box = [];
31226         }
31227         
31228         var filterPattern = function(box, length)
31229         {
31230             if(!box.length){
31231                 return;
31232             }
31233             
31234             var match = false;
31235             
31236             var pattern = box.slice(0, length);
31237             
31238             var format = [];
31239             
31240             Roo.each(pattern, function(i){
31241                 format.push(i.size);
31242             }, this);
31243             
31244             Roo.each(standard, function(s){
31245                 
31246                 if(String(s) != String(format)){
31247                     return;
31248                 }
31249                 
31250                 match = true;
31251                 return false;
31252                 
31253             }, this);
31254             
31255             if(!match && length == 1){
31256                 return;
31257             }
31258             
31259             if(!match){
31260                 filterPattern(box, length - 1);
31261                 return;
31262             }
31263                 
31264             queue.push(pattern);
31265
31266             box = box.slice(length, box.length);
31267
31268             filterPattern(box, 4);
31269
31270             return;
31271             
31272         }
31273         
31274         Roo.each(boxes, function(box, k){
31275             
31276             if(!box.length){
31277                 return;
31278             }
31279             
31280             if(box.length == 1){
31281                 queue.push(box);
31282                 return;
31283             }
31284             
31285             filterPattern(box, 4);
31286             
31287         }, this);
31288         
31289         
31290         var prune = [];
31291         
31292         var pos = this.el.getBox(true);
31293         
31294         var minX = pos.x;
31295         
31296         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31297         
31298         var hit_end = false;
31299         
31300         Roo.each(queue, function(box){
31301             
31302             if(hit_end){
31303                 
31304                 Roo.each(box, function(b){
31305                 
31306                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31307                     b.el.hide();
31308
31309                 }, this);
31310
31311                 return;
31312             }
31313             
31314             var mx = 0;
31315             
31316             Roo.each(box, function(b){
31317                 
31318                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31319                 b.el.show();
31320
31321                 mx = Math.max(mx, b.x);
31322                 
31323             }, this);
31324             
31325             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31326             
31327             if(maxX < minX){
31328                 
31329                 Roo.each(box, function(b){
31330                 
31331                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31332                     b.el.hide();
31333                     
31334                 }, this);
31335                 
31336                 hit_end = true;
31337                 
31338                 return;
31339             }
31340             
31341             prune.push(box);
31342             
31343         }, this);
31344         
31345         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31346     },
31347     
31348     /** Sets position of item in DOM
31349     * @param {Element} item
31350     * @param {Number} x - horizontal position
31351     * @param {Number} y - vertical position
31352     * @param {Boolean} isInstant - disables transitions
31353     */
31354     _processVerticalLayoutQueue : function( queue, isInstant )
31355     {
31356         var pos = this.el.getBox(true);
31357         var x = pos.x;
31358         var y = pos.y;
31359         var maxY = [];
31360         
31361         for (var i = 0; i < this.cols; i++){
31362             maxY[i] = pos.y;
31363         }
31364         
31365         Roo.each(queue, function(box, k){
31366             
31367             var col = k % this.cols;
31368             
31369             Roo.each(box, function(b,kk){
31370                 
31371                 b.el.position('absolute');
31372                 
31373                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31374                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31375                 
31376                 if(b.size == 'md-left' || b.size == 'md-right'){
31377                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31378                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31379                 }
31380                 
31381                 b.el.setWidth(width);
31382                 b.el.setHeight(height);
31383                 // iframe?
31384                 b.el.select('iframe',true).setSize(width,height);
31385                 
31386             }, this);
31387             
31388             for (var i = 0; i < this.cols; i++){
31389                 
31390                 if(maxY[i] < maxY[col]){
31391                     col = i;
31392                     continue;
31393                 }
31394                 
31395                 col = Math.min(col, i);
31396                 
31397             }
31398             
31399             x = pos.x + col * (this.colWidth + this.padWidth);
31400             
31401             y = maxY[col];
31402             
31403             var positions = [];
31404             
31405             switch (box.length){
31406                 case 1 :
31407                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31408                     break;
31409                 case 2 :
31410                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31411                     break;
31412                 case 3 :
31413                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31414                     break;
31415                 case 4 :
31416                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31417                     break;
31418                 default :
31419                     break;
31420             }
31421             
31422             Roo.each(box, function(b,kk){
31423                 
31424                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31425                 
31426                 var sz = b.el.getSize();
31427                 
31428                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31429                 
31430             }, this);
31431             
31432         }, this);
31433         
31434         var mY = 0;
31435         
31436         for (var i = 0; i < this.cols; i++){
31437             mY = Math.max(mY, maxY[i]);
31438         }
31439         
31440         this.el.setHeight(mY - pos.y);
31441         
31442     },
31443     
31444 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31445 //    {
31446 //        var pos = this.el.getBox(true);
31447 //        var x = pos.x;
31448 //        var y = pos.y;
31449 //        var maxX = pos.right;
31450 //        
31451 //        var maxHeight = 0;
31452 //        
31453 //        Roo.each(items, function(item, k){
31454 //            
31455 //            var c = k % 2;
31456 //            
31457 //            item.el.position('absolute');
31458 //                
31459 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31460 //
31461 //            item.el.setWidth(width);
31462 //
31463 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31464 //
31465 //            item.el.setHeight(height);
31466 //            
31467 //            if(c == 0){
31468 //                item.el.setXY([x, y], isInstant ? false : true);
31469 //            } else {
31470 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31471 //            }
31472 //            
31473 //            y = y + height + this.alternativePadWidth;
31474 //            
31475 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31476 //            
31477 //        }, this);
31478 //        
31479 //        this.el.setHeight(maxHeight);
31480 //        
31481 //    },
31482     
31483     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31484     {
31485         var pos = this.el.getBox(true);
31486         
31487         var minX = pos.x;
31488         var minY = pos.y;
31489         
31490         var maxX = pos.right;
31491         
31492         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31493         
31494         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31495         
31496         Roo.each(queue, function(box, k){
31497             
31498             Roo.each(box, function(b, kk){
31499                 
31500                 b.el.position('absolute');
31501                 
31502                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31503                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31504                 
31505                 if(b.size == 'md-left' || b.size == 'md-right'){
31506                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31507                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31508                 }
31509                 
31510                 b.el.setWidth(width);
31511                 b.el.setHeight(height);
31512                 
31513             }, this);
31514             
31515             if(!box.length){
31516                 return;
31517             }
31518             
31519             var positions = [];
31520             
31521             switch (box.length){
31522                 case 1 :
31523                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31524                     break;
31525                 case 2 :
31526                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31527                     break;
31528                 case 3 :
31529                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31530                     break;
31531                 case 4 :
31532                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31533                     break;
31534                 default :
31535                     break;
31536             }
31537             
31538             Roo.each(box, function(b,kk){
31539                 
31540                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31541                 
31542                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31543                 
31544             }, this);
31545             
31546         }, this);
31547         
31548     },
31549     
31550     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31551     {
31552         Roo.each(eItems, function(b,k){
31553             
31554             b.size = (k == 0) ? 'sm' : 'xs';
31555             b.x = (k == 0) ? 2 : 1;
31556             b.y = (k == 0) ? 2 : 1;
31557             
31558             b.el.position('absolute');
31559             
31560             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31561                 
31562             b.el.setWidth(width);
31563             
31564             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31565             
31566             b.el.setHeight(height);
31567             
31568         }, this);
31569
31570         var positions = [];
31571         
31572         positions.push({
31573             x : maxX - this.unitWidth * 2 - this.gutter,
31574             y : minY
31575         });
31576         
31577         positions.push({
31578             x : maxX - this.unitWidth,
31579             y : minY + (this.unitWidth + this.gutter) * 2
31580         });
31581         
31582         positions.push({
31583             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31584             y : minY
31585         });
31586         
31587         Roo.each(eItems, function(b,k){
31588             
31589             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31590
31591         }, this);
31592         
31593     },
31594     
31595     getVerticalOneBoxColPositions : function(x, y, box)
31596     {
31597         var pos = [];
31598         
31599         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31600         
31601         if(box[0].size == 'md-left'){
31602             rand = 0;
31603         }
31604         
31605         if(box[0].size == 'md-right'){
31606             rand = 1;
31607         }
31608         
31609         pos.push({
31610             x : x + (this.unitWidth + this.gutter) * rand,
31611             y : y
31612         });
31613         
31614         return pos;
31615     },
31616     
31617     getVerticalTwoBoxColPositions : function(x, y, box)
31618     {
31619         var pos = [];
31620         
31621         if(box[0].size == 'xs'){
31622             
31623             pos.push({
31624                 x : x,
31625                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31626             });
31627
31628             pos.push({
31629                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31630                 y : y
31631             });
31632             
31633             return pos;
31634             
31635         }
31636         
31637         pos.push({
31638             x : x,
31639             y : y
31640         });
31641
31642         pos.push({
31643             x : x + (this.unitWidth + this.gutter) * 2,
31644             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31645         });
31646         
31647         return pos;
31648         
31649     },
31650     
31651     getVerticalThreeBoxColPositions : function(x, y, box)
31652     {
31653         var pos = [];
31654         
31655         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31656             
31657             pos.push({
31658                 x : x,
31659                 y : y
31660             });
31661
31662             pos.push({
31663                 x : x + (this.unitWidth + this.gutter) * 1,
31664                 y : y
31665             });
31666             
31667             pos.push({
31668                 x : x + (this.unitWidth + this.gutter) * 2,
31669                 y : y
31670             });
31671             
31672             return pos;
31673             
31674         }
31675         
31676         if(box[0].size == 'xs' && box[1].size == 'xs'){
31677             
31678             pos.push({
31679                 x : x,
31680                 y : y
31681             });
31682
31683             pos.push({
31684                 x : x,
31685                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31686             });
31687             
31688             pos.push({
31689                 x : x + (this.unitWidth + this.gutter) * 1,
31690                 y : y
31691             });
31692             
31693             return pos;
31694             
31695         }
31696         
31697         pos.push({
31698             x : x,
31699             y : y
31700         });
31701
31702         pos.push({
31703             x : x + (this.unitWidth + this.gutter) * 2,
31704             y : y
31705         });
31706
31707         pos.push({
31708             x : x + (this.unitWidth + this.gutter) * 2,
31709             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31710         });
31711             
31712         return pos;
31713         
31714     },
31715     
31716     getVerticalFourBoxColPositions : function(x, y, box)
31717     {
31718         var pos = [];
31719         
31720         if(box[0].size == 'xs'){
31721             
31722             pos.push({
31723                 x : x,
31724                 y : y
31725             });
31726
31727             pos.push({
31728                 x : x,
31729                 y : y + (this.unitHeight + this.gutter) * 1
31730             });
31731             
31732             pos.push({
31733                 x : x,
31734                 y : y + (this.unitHeight + this.gutter) * 2
31735             });
31736             
31737             pos.push({
31738                 x : x + (this.unitWidth + this.gutter) * 1,
31739                 y : y
31740             });
31741             
31742             return pos;
31743             
31744         }
31745         
31746         pos.push({
31747             x : x,
31748             y : y
31749         });
31750
31751         pos.push({
31752             x : x + (this.unitWidth + this.gutter) * 2,
31753             y : y
31754         });
31755
31756         pos.push({
31757             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31758             y : y + (this.unitHeight + this.gutter) * 1
31759         });
31760
31761         pos.push({
31762             x : x + (this.unitWidth + this.gutter) * 2,
31763             y : y + (this.unitWidth + this.gutter) * 2
31764         });
31765
31766         return pos;
31767         
31768     },
31769     
31770     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31771     {
31772         var pos = [];
31773         
31774         if(box[0].size == 'md-left'){
31775             pos.push({
31776                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31777                 y : minY
31778             });
31779             
31780             return pos;
31781         }
31782         
31783         if(box[0].size == 'md-right'){
31784             pos.push({
31785                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31786                 y : minY + (this.unitWidth + this.gutter) * 1
31787             });
31788             
31789             return pos;
31790         }
31791         
31792         var rand = Math.floor(Math.random() * (4 - box[0].y));
31793         
31794         pos.push({
31795             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31796             y : minY + (this.unitWidth + this.gutter) * rand
31797         });
31798         
31799         return pos;
31800         
31801     },
31802     
31803     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31804     {
31805         var pos = [];
31806         
31807         if(box[0].size == 'xs'){
31808             
31809             pos.push({
31810                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31811                 y : minY
31812             });
31813
31814             pos.push({
31815                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31816                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31817             });
31818             
31819             return pos;
31820             
31821         }
31822         
31823         pos.push({
31824             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31825             y : minY
31826         });
31827
31828         pos.push({
31829             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31830             y : minY + (this.unitWidth + this.gutter) * 2
31831         });
31832         
31833         return pos;
31834         
31835     },
31836     
31837     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31838     {
31839         var pos = [];
31840         
31841         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31842             
31843             pos.push({
31844                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31845                 y : minY
31846             });
31847
31848             pos.push({
31849                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31850                 y : minY + (this.unitWidth + this.gutter) * 1
31851             });
31852             
31853             pos.push({
31854                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31855                 y : minY + (this.unitWidth + this.gutter) * 2
31856             });
31857             
31858             return pos;
31859             
31860         }
31861         
31862         if(box[0].size == 'xs' && box[1].size == 'xs'){
31863             
31864             pos.push({
31865                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31866                 y : minY
31867             });
31868
31869             pos.push({
31870                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31871                 y : minY
31872             });
31873             
31874             pos.push({
31875                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31876                 y : minY + (this.unitWidth + this.gutter) * 1
31877             });
31878             
31879             return pos;
31880             
31881         }
31882         
31883         pos.push({
31884             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31885             y : minY
31886         });
31887
31888         pos.push({
31889             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31890             y : minY + (this.unitWidth + this.gutter) * 2
31891         });
31892
31893         pos.push({
31894             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31895             y : minY + (this.unitWidth + this.gutter) * 2
31896         });
31897             
31898         return pos;
31899         
31900     },
31901     
31902     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31903     {
31904         var pos = [];
31905         
31906         if(box[0].size == 'xs'){
31907             
31908             pos.push({
31909                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31910                 y : minY
31911             });
31912
31913             pos.push({
31914                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31915                 y : minY
31916             });
31917             
31918             pos.push({
31919                 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),
31920                 y : minY
31921             });
31922             
31923             pos.push({
31924                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31925                 y : minY + (this.unitWidth + this.gutter) * 1
31926             });
31927             
31928             return pos;
31929             
31930         }
31931         
31932         pos.push({
31933             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31934             y : minY
31935         });
31936         
31937         pos.push({
31938             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31939             y : minY + (this.unitWidth + this.gutter) * 2
31940         });
31941         
31942         pos.push({
31943             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31944             y : minY + (this.unitWidth + this.gutter) * 2
31945         });
31946         
31947         pos.push({
31948             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),
31949             y : minY + (this.unitWidth + this.gutter) * 2
31950         });
31951
31952         return pos;
31953         
31954     },
31955     
31956     /**
31957     * remove a Masonry Brick
31958     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31959     */
31960     removeBrick : function(brick_id)
31961     {
31962         if (!brick_id) {
31963             return;
31964         }
31965         
31966         for (var i = 0; i<this.bricks.length; i++) {
31967             if (this.bricks[i].id == brick_id) {
31968                 this.bricks.splice(i,1);
31969                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31970                 this.initial();
31971             }
31972         }
31973     },
31974     
31975     /**
31976     * adds a Masonry Brick
31977     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31978     */
31979     addBrick : function(cfg)
31980     {
31981         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31982         //this.register(cn);
31983         cn.parentId = this.id;
31984         cn.onRender(this.el, null);
31985         return cn;
31986     },
31987     
31988     /**
31989     * register a Masonry Brick
31990     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31991     */
31992     
31993     register : function(brick)
31994     {
31995         this.bricks.push(brick);
31996         brick.masonryId = this.id;
31997     },
31998     
31999     /**
32000     * clear all the Masonry Brick
32001     */
32002     clearAll : function()
32003     {
32004         this.bricks = [];
32005         //this.getChildContainer().dom.innerHTML = "";
32006         this.el.dom.innerHTML = '';
32007     },
32008     
32009     getSelected : function()
32010     {
32011         if (!this.selectedBrick) {
32012             return false;
32013         }
32014         
32015         return this.selectedBrick;
32016     }
32017 });
32018
32019 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32020     
32021     groups: {},
32022      /**
32023     * register a Masonry Layout
32024     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32025     */
32026     
32027     register : function(layout)
32028     {
32029         this.groups[layout.id] = layout;
32030     },
32031     /**
32032     * fetch a  Masonry Layout based on the masonry layout ID
32033     * @param {string} the masonry layout to add
32034     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32035     */
32036     
32037     get: function(layout_id) {
32038         if (typeof(this.groups[layout_id]) == 'undefined') {
32039             return false;
32040         }
32041         return this.groups[layout_id] ;
32042     }
32043     
32044     
32045     
32046 });
32047
32048  
32049
32050  /**
32051  *
32052  * This is based on 
32053  * http://masonry.desandro.com
32054  *
32055  * The idea is to render all the bricks based on vertical width...
32056  *
32057  * The original code extends 'outlayer' - we might need to use that....
32058  * 
32059  */
32060
32061
32062 /**
32063  * @class Roo.bootstrap.LayoutMasonryAuto
32064  * @extends Roo.bootstrap.Component
32065  * Bootstrap Layout Masonry class
32066  * 
32067  * @constructor
32068  * Create a new Element
32069  * @param {Object} config The config object
32070  */
32071
32072 Roo.bootstrap.LayoutMasonryAuto = function(config){
32073     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32074 };
32075
32076 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32077     
32078       /**
32079      * @cfg {Boolean} isFitWidth  - resize the width..
32080      */   
32081     isFitWidth : false,  // options..
32082     /**
32083      * @cfg {Boolean} isOriginLeft = left align?
32084      */   
32085     isOriginLeft : true,
32086     /**
32087      * @cfg {Boolean} isOriginTop = top align?
32088      */   
32089     isOriginTop : false,
32090     /**
32091      * @cfg {Boolean} isLayoutInstant = no animation?
32092      */   
32093     isLayoutInstant : false, // needed?
32094     /**
32095      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32096      */   
32097     isResizingContainer : true,
32098     /**
32099      * @cfg {Number} columnWidth  width of the columns 
32100      */   
32101     
32102     columnWidth : 0,
32103     
32104     /**
32105      * @cfg {Number} maxCols maximum number of columns
32106      */   
32107     
32108     maxCols: 0,
32109     /**
32110      * @cfg {Number} padHeight padding below box..
32111      */   
32112     
32113     padHeight : 10, 
32114     
32115     /**
32116      * @cfg {Boolean} isAutoInitial defalut true
32117      */   
32118     
32119     isAutoInitial : true, 
32120     
32121     // private?
32122     gutter : 0,
32123     
32124     containerWidth: 0,
32125     initialColumnWidth : 0,
32126     currentSize : null,
32127     
32128     colYs : null, // array.
32129     maxY : 0,
32130     padWidth: 10,
32131     
32132     
32133     tag: 'div',
32134     cls: '',
32135     bricks: null, //CompositeElement
32136     cols : 0, // array?
32137     // element : null, // wrapped now this.el
32138     _isLayoutInited : null, 
32139     
32140     
32141     getAutoCreate : function(){
32142         
32143         var cfg = {
32144             tag: this.tag,
32145             cls: 'blog-masonary-wrapper ' + this.cls,
32146             cn : {
32147                 cls : 'mas-boxes masonary'
32148             }
32149         };
32150         
32151         return cfg;
32152     },
32153     
32154     getChildContainer: function( )
32155     {
32156         if (this.boxesEl) {
32157             return this.boxesEl;
32158         }
32159         
32160         this.boxesEl = this.el.select('.mas-boxes').first();
32161         
32162         return this.boxesEl;
32163     },
32164     
32165     
32166     initEvents : function()
32167     {
32168         var _this = this;
32169         
32170         if(this.isAutoInitial){
32171             Roo.log('hook children rendered');
32172             this.on('childrenrendered', function() {
32173                 Roo.log('children rendered');
32174                 _this.initial();
32175             } ,this);
32176         }
32177         
32178     },
32179     
32180     initial : function()
32181     {
32182         this.reloadItems();
32183
32184         this.currentSize = this.el.getBox(true);
32185
32186         /// was window resize... - let's see if this works..
32187         Roo.EventManager.onWindowResize(this.resize, this); 
32188
32189         if(!this.isAutoInitial){
32190             this.layout();
32191             return;
32192         }
32193         
32194         this.layout.defer(500,this);
32195     },
32196     
32197     reloadItems: function()
32198     {
32199         this.bricks = this.el.select('.masonry-brick', true);
32200         
32201         this.bricks.each(function(b) {
32202             //Roo.log(b.getSize());
32203             if (!b.attr('originalwidth')) {
32204                 b.attr('originalwidth',  b.getSize().width);
32205             }
32206             
32207         });
32208         
32209         Roo.log(this.bricks.elements.length);
32210     },
32211     
32212     resize : function()
32213     {
32214         Roo.log('resize');
32215         var cs = this.el.getBox(true);
32216         
32217         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32218             Roo.log("no change in with or X");
32219             return;
32220         }
32221         this.currentSize = cs;
32222         this.layout();
32223     },
32224     
32225     layout : function()
32226     {
32227          Roo.log('layout');
32228         this._resetLayout();
32229         //this._manageStamps();
32230       
32231         // don't animate first layout
32232         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32233         this.layoutItems( isInstant );
32234       
32235         // flag for initalized
32236         this._isLayoutInited = true;
32237     },
32238     
32239     layoutItems : function( isInstant )
32240     {
32241         //var items = this._getItemsForLayout( this.items );
32242         // original code supports filtering layout items.. we just ignore it..
32243         
32244         this._layoutItems( this.bricks , isInstant );
32245       
32246         this._postLayout();
32247     },
32248     _layoutItems : function ( items , isInstant)
32249     {
32250        //this.fireEvent( 'layout', this, items );
32251     
32252
32253         if ( !items || !items.elements.length ) {
32254           // no items, emit event with empty array
32255             return;
32256         }
32257
32258         var queue = [];
32259         items.each(function(item) {
32260             Roo.log("layout item");
32261             Roo.log(item);
32262             // get x/y object from method
32263             var position = this._getItemLayoutPosition( item );
32264             // enqueue
32265             position.item = item;
32266             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32267             queue.push( position );
32268         }, this);
32269       
32270         this._processLayoutQueue( queue );
32271     },
32272     /** Sets position of item in DOM
32273     * @param {Element} item
32274     * @param {Number} x - horizontal position
32275     * @param {Number} y - vertical position
32276     * @param {Boolean} isInstant - disables transitions
32277     */
32278     _processLayoutQueue : function( queue )
32279     {
32280         for ( var i=0, len = queue.length; i < len; i++ ) {
32281             var obj = queue[i];
32282             obj.item.position('absolute');
32283             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32284         }
32285     },
32286       
32287     
32288     /**
32289     * Any logic you want to do after each layout,
32290     * i.e. size the container
32291     */
32292     _postLayout : function()
32293     {
32294         this.resizeContainer();
32295     },
32296     
32297     resizeContainer : function()
32298     {
32299         if ( !this.isResizingContainer ) {
32300             return;
32301         }
32302         var size = this._getContainerSize();
32303         if ( size ) {
32304             this.el.setSize(size.width,size.height);
32305             this.boxesEl.setSize(size.width,size.height);
32306         }
32307     },
32308     
32309     
32310     
32311     _resetLayout : function()
32312     {
32313         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32314         this.colWidth = this.el.getWidth();
32315         //this.gutter = this.el.getWidth(); 
32316         
32317         this.measureColumns();
32318
32319         // reset column Y
32320         var i = this.cols;
32321         this.colYs = [];
32322         while (i--) {
32323             this.colYs.push( 0 );
32324         }
32325     
32326         this.maxY = 0;
32327     },
32328
32329     measureColumns : function()
32330     {
32331         this.getContainerWidth();
32332       // if columnWidth is 0, default to outerWidth of first item
32333         if ( !this.columnWidth ) {
32334             var firstItem = this.bricks.first();
32335             Roo.log(firstItem);
32336             this.columnWidth  = this.containerWidth;
32337             if (firstItem && firstItem.attr('originalwidth') ) {
32338                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32339             }
32340             // columnWidth fall back to item of first element
32341             Roo.log("set column width?");
32342                         this.initialColumnWidth = this.columnWidth  ;
32343
32344             // if first elem has no width, default to size of container
32345             
32346         }
32347         
32348         
32349         if (this.initialColumnWidth) {
32350             this.columnWidth = this.initialColumnWidth;
32351         }
32352         
32353         
32354             
32355         // column width is fixed at the top - however if container width get's smaller we should
32356         // reduce it...
32357         
32358         // this bit calcs how man columns..
32359             
32360         var columnWidth = this.columnWidth += this.gutter;
32361       
32362         // calculate columns
32363         var containerWidth = this.containerWidth + this.gutter;
32364         
32365         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32366         // fix rounding errors, typically with gutters
32367         var excess = columnWidth - containerWidth % columnWidth;
32368         
32369         
32370         // if overshoot is less than a pixel, round up, otherwise floor it
32371         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32372         cols = Math[ mathMethod ]( cols );
32373         this.cols = Math.max( cols, 1 );
32374         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32375         
32376          // padding positioning..
32377         var totalColWidth = this.cols * this.columnWidth;
32378         var padavail = this.containerWidth - totalColWidth;
32379         // so for 2 columns - we need 3 'pads'
32380         
32381         var padNeeded = (1+this.cols) * this.padWidth;
32382         
32383         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32384         
32385         this.columnWidth += padExtra
32386         //this.padWidth = Math.floor(padavail /  ( this.cols));
32387         
32388         // adjust colum width so that padding is fixed??
32389         
32390         // we have 3 columns ... total = width * 3
32391         // we have X left over... that should be used by 
32392         
32393         //if (this.expandC) {
32394             
32395         //}
32396         
32397         
32398         
32399     },
32400     
32401     getContainerWidth : function()
32402     {
32403        /* // container is parent if fit width
32404         var container = this.isFitWidth ? this.element.parentNode : this.element;
32405         // check that this.size and size are there
32406         // IE8 triggers resize on body size change, so they might not be
32407         
32408         var size = getSize( container );  //FIXME
32409         this.containerWidth = size && size.innerWidth; //FIXME
32410         */
32411          
32412         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32413         
32414     },
32415     
32416     _getItemLayoutPosition : function( item )  // what is item?
32417     {
32418         // we resize the item to our columnWidth..
32419       
32420         item.setWidth(this.columnWidth);
32421         item.autoBoxAdjust  = false;
32422         
32423         var sz = item.getSize();
32424  
32425         // how many columns does this brick span
32426         var remainder = this.containerWidth % this.columnWidth;
32427         
32428         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32429         // round if off by 1 pixel, otherwise use ceil
32430         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32431         colSpan = Math.min( colSpan, this.cols );
32432         
32433         // normally this should be '1' as we dont' currently allow multi width columns..
32434         
32435         var colGroup = this._getColGroup( colSpan );
32436         // get the minimum Y value from the columns
32437         var minimumY = Math.min.apply( Math, colGroup );
32438         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32439         
32440         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32441          
32442         // position the brick
32443         var position = {
32444             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32445             y: this.currentSize.y + minimumY + this.padHeight
32446         };
32447         
32448         Roo.log(position);
32449         // apply setHeight to necessary columns
32450         var setHeight = minimumY + sz.height + this.padHeight;
32451         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32452         
32453         var setSpan = this.cols + 1 - colGroup.length;
32454         for ( var i = 0; i < setSpan; i++ ) {
32455           this.colYs[ shortColIndex + i ] = setHeight ;
32456         }
32457       
32458         return position;
32459     },
32460     
32461     /**
32462      * @param {Number} colSpan - number of columns the element spans
32463      * @returns {Array} colGroup
32464      */
32465     _getColGroup : function( colSpan )
32466     {
32467         if ( colSpan < 2 ) {
32468           // if brick spans only one column, use all the column Ys
32469           return this.colYs;
32470         }
32471       
32472         var colGroup = [];
32473         // how many different places could this brick fit horizontally
32474         var groupCount = this.cols + 1 - colSpan;
32475         // for each group potential horizontal position
32476         for ( var i = 0; i < groupCount; i++ ) {
32477           // make an array of colY values for that one group
32478           var groupColYs = this.colYs.slice( i, i + colSpan );
32479           // and get the max value of the array
32480           colGroup[i] = Math.max.apply( Math, groupColYs );
32481         }
32482         return colGroup;
32483     },
32484     /*
32485     _manageStamp : function( stamp )
32486     {
32487         var stampSize =  stamp.getSize();
32488         var offset = stamp.getBox();
32489         // get the columns that this stamp affects
32490         var firstX = this.isOriginLeft ? offset.x : offset.right;
32491         var lastX = firstX + stampSize.width;
32492         var firstCol = Math.floor( firstX / this.columnWidth );
32493         firstCol = Math.max( 0, firstCol );
32494         
32495         var lastCol = Math.floor( lastX / this.columnWidth );
32496         // lastCol should not go over if multiple of columnWidth #425
32497         lastCol -= lastX % this.columnWidth ? 0 : 1;
32498         lastCol = Math.min( this.cols - 1, lastCol );
32499         
32500         // set colYs to bottom of the stamp
32501         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32502             stampSize.height;
32503             
32504         for ( var i = firstCol; i <= lastCol; i++ ) {
32505           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32506         }
32507     },
32508     */
32509     
32510     _getContainerSize : function()
32511     {
32512         this.maxY = Math.max.apply( Math, this.colYs );
32513         var size = {
32514             height: this.maxY
32515         };
32516       
32517         if ( this.isFitWidth ) {
32518             size.width = this._getContainerFitWidth();
32519         }
32520       
32521         return size;
32522     },
32523     
32524     _getContainerFitWidth : function()
32525     {
32526         var unusedCols = 0;
32527         // count unused columns
32528         var i = this.cols;
32529         while ( --i ) {
32530           if ( this.colYs[i] !== 0 ) {
32531             break;
32532           }
32533           unusedCols++;
32534         }
32535         // fit container to columns that have been used
32536         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32537     },
32538     
32539     needsResizeLayout : function()
32540     {
32541         var previousWidth = this.containerWidth;
32542         this.getContainerWidth();
32543         return previousWidth !== this.containerWidth;
32544     }
32545  
32546 });
32547
32548  
32549
32550  /*
32551  * - LGPL
32552  *
32553  * element
32554  * 
32555  */
32556
32557 /**
32558  * @class Roo.bootstrap.MasonryBrick
32559  * @extends Roo.bootstrap.Component
32560  * Bootstrap MasonryBrick class
32561  * 
32562  * @constructor
32563  * Create a new MasonryBrick
32564  * @param {Object} config The config object
32565  */
32566
32567 Roo.bootstrap.MasonryBrick = function(config){
32568     
32569     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32570     
32571     Roo.bootstrap.MasonryBrick.register(this);
32572     
32573     this.addEvents({
32574         // raw events
32575         /**
32576          * @event click
32577          * When a MasonryBrick is clcik
32578          * @param {Roo.bootstrap.MasonryBrick} this
32579          * @param {Roo.EventObject} e
32580          */
32581         "click" : true
32582     });
32583 };
32584
32585 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32586     
32587     /**
32588      * @cfg {String} title
32589      */   
32590     title : '',
32591     /**
32592      * @cfg {String} html
32593      */   
32594     html : '',
32595     /**
32596      * @cfg {String} bgimage
32597      */   
32598     bgimage : '',
32599     /**
32600      * @cfg {String} videourl
32601      */   
32602     videourl : '',
32603     /**
32604      * @cfg {String} cls
32605      */   
32606     cls : '',
32607     /**
32608      * @cfg {String} href
32609      */   
32610     href : '',
32611     /**
32612      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32613      */   
32614     size : 'xs',
32615     
32616     /**
32617      * @cfg {String} placetitle (center|bottom)
32618      */   
32619     placetitle : '',
32620     
32621     /**
32622      * @cfg {Boolean} isFitContainer defalut true
32623      */   
32624     isFitContainer : true, 
32625     
32626     /**
32627      * @cfg {Boolean} preventDefault defalut false
32628      */   
32629     preventDefault : false, 
32630     
32631     /**
32632      * @cfg {Boolean} inverse defalut false
32633      */   
32634     maskInverse : false, 
32635     
32636     getAutoCreate : function()
32637     {
32638         if(!this.isFitContainer){
32639             return this.getSplitAutoCreate();
32640         }
32641         
32642         var cls = 'masonry-brick masonry-brick-full';
32643         
32644         if(this.href.length){
32645             cls += ' masonry-brick-link';
32646         }
32647         
32648         if(this.bgimage.length){
32649             cls += ' masonry-brick-image';
32650         }
32651         
32652         if(this.maskInverse){
32653             cls += ' mask-inverse';
32654         }
32655         
32656         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32657             cls += ' enable-mask';
32658         }
32659         
32660         if(this.size){
32661             cls += ' masonry-' + this.size + '-brick';
32662         }
32663         
32664         if(this.placetitle.length){
32665             
32666             switch (this.placetitle) {
32667                 case 'center' :
32668                     cls += ' masonry-center-title';
32669                     break;
32670                 case 'bottom' :
32671                     cls += ' masonry-bottom-title';
32672                     break;
32673                 default:
32674                     break;
32675             }
32676             
32677         } else {
32678             if(!this.html.length && !this.bgimage.length){
32679                 cls += ' masonry-center-title';
32680             }
32681
32682             if(!this.html.length && this.bgimage.length){
32683                 cls += ' masonry-bottom-title';
32684             }
32685         }
32686         
32687         if(this.cls){
32688             cls += ' ' + this.cls;
32689         }
32690         
32691         var cfg = {
32692             tag: (this.href.length) ? 'a' : 'div',
32693             cls: cls,
32694             cn: [
32695                 {
32696                     tag: 'div',
32697                     cls: 'masonry-brick-mask'
32698                 },
32699                 {
32700                     tag: 'div',
32701                     cls: 'masonry-brick-paragraph',
32702                     cn: []
32703                 }
32704             ]
32705         };
32706         
32707         if(this.href.length){
32708             cfg.href = this.href;
32709         }
32710         
32711         var cn = cfg.cn[1].cn;
32712         
32713         if(this.title.length){
32714             cn.push({
32715                 tag: 'h4',
32716                 cls: 'masonry-brick-title',
32717                 html: this.title
32718             });
32719         }
32720         
32721         if(this.html.length){
32722             cn.push({
32723                 tag: 'p',
32724                 cls: 'masonry-brick-text',
32725                 html: this.html
32726             });
32727         }
32728         
32729         if (!this.title.length && !this.html.length) {
32730             cfg.cn[1].cls += ' hide';
32731         }
32732         
32733         if(this.bgimage.length){
32734             cfg.cn.push({
32735                 tag: 'img',
32736                 cls: 'masonry-brick-image-view',
32737                 src: this.bgimage
32738             });
32739         }
32740         
32741         if(this.videourl.length){
32742             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32743             // youtube support only?
32744             cfg.cn.push({
32745                 tag: 'iframe',
32746                 cls: 'masonry-brick-image-view',
32747                 src: vurl,
32748                 frameborder : 0,
32749                 allowfullscreen : true
32750             });
32751         }
32752         
32753         return cfg;
32754         
32755     },
32756     
32757     getSplitAutoCreate : function()
32758     {
32759         var cls = 'masonry-brick masonry-brick-split';
32760         
32761         if(this.href.length){
32762             cls += ' masonry-brick-link';
32763         }
32764         
32765         if(this.bgimage.length){
32766             cls += ' masonry-brick-image';
32767         }
32768         
32769         if(this.size){
32770             cls += ' masonry-' + this.size + '-brick';
32771         }
32772         
32773         switch (this.placetitle) {
32774             case 'center' :
32775                 cls += ' masonry-center-title';
32776                 break;
32777             case 'bottom' :
32778                 cls += ' masonry-bottom-title';
32779                 break;
32780             default:
32781                 if(!this.bgimage.length){
32782                     cls += ' masonry-center-title';
32783                 }
32784
32785                 if(this.bgimage.length){
32786                     cls += ' masonry-bottom-title';
32787                 }
32788                 break;
32789         }
32790         
32791         if(this.cls){
32792             cls += ' ' + this.cls;
32793         }
32794         
32795         var cfg = {
32796             tag: (this.href.length) ? 'a' : 'div',
32797             cls: cls,
32798             cn: [
32799                 {
32800                     tag: 'div',
32801                     cls: 'masonry-brick-split-head',
32802                     cn: [
32803                         {
32804                             tag: 'div',
32805                             cls: 'masonry-brick-paragraph',
32806                             cn: []
32807                         }
32808                     ]
32809                 },
32810                 {
32811                     tag: 'div',
32812                     cls: 'masonry-brick-split-body',
32813                     cn: []
32814                 }
32815             ]
32816         };
32817         
32818         if(this.href.length){
32819             cfg.href = this.href;
32820         }
32821         
32822         if(this.title.length){
32823             cfg.cn[0].cn[0].cn.push({
32824                 tag: 'h4',
32825                 cls: 'masonry-brick-title',
32826                 html: this.title
32827             });
32828         }
32829         
32830         if(this.html.length){
32831             cfg.cn[1].cn.push({
32832                 tag: 'p',
32833                 cls: 'masonry-brick-text',
32834                 html: this.html
32835             });
32836         }
32837
32838         if(this.bgimage.length){
32839             cfg.cn[0].cn.push({
32840                 tag: 'img',
32841                 cls: 'masonry-brick-image-view',
32842                 src: this.bgimage
32843             });
32844         }
32845         
32846         if(this.videourl.length){
32847             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32848             // youtube support only?
32849             cfg.cn[0].cn.cn.push({
32850                 tag: 'iframe',
32851                 cls: 'masonry-brick-image-view',
32852                 src: vurl,
32853                 frameborder : 0,
32854                 allowfullscreen : true
32855             });
32856         }
32857         
32858         return cfg;
32859     },
32860     
32861     initEvents: function() 
32862     {
32863         switch (this.size) {
32864             case 'xs' :
32865                 this.x = 1;
32866                 this.y = 1;
32867                 break;
32868             case 'sm' :
32869                 this.x = 2;
32870                 this.y = 2;
32871                 break;
32872             case 'md' :
32873             case 'md-left' :
32874             case 'md-right' :
32875                 this.x = 3;
32876                 this.y = 3;
32877                 break;
32878             case 'tall' :
32879                 this.x = 2;
32880                 this.y = 3;
32881                 break;
32882             case 'wide' :
32883                 this.x = 3;
32884                 this.y = 2;
32885                 break;
32886             case 'wide-thin' :
32887                 this.x = 3;
32888                 this.y = 1;
32889                 break;
32890                         
32891             default :
32892                 break;
32893         }
32894         
32895         if(Roo.isTouch){
32896             this.el.on('touchstart', this.onTouchStart, this);
32897             this.el.on('touchmove', this.onTouchMove, this);
32898             this.el.on('touchend', this.onTouchEnd, this);
32899             this.el.on('contextmenu', this.onContextMenu, this);
32900         } else {
32901             this.el.on('mouseenter'  ,this.enter, this);
32902             this.el.on('mouseleave', this.leave, this);
32903             this.el.on('click', this.onClick, this);
32904         }
32905         
32906         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32907             this.parent().bricks.push(this);   
32908         }
32909         
32910     },
32911     
32912     onClick: function(e, el)
32913     {
32914         var time = this.endTimer - this.startTimer;
32915         // Roo.log(e.preventDefault());
32916         if(Roo.isTouch){
32917             if(time > 1000){
32918                 e.preventDefault();
32919                 return;
32920             }
32921         }
32922         
32923         if(!this.preventDefault){
32924             return;
32925         }
32926         
32927         e.preventDefault();
32928         
32929         if (this.activeClass != '') {
32930             this.selectBrick();
32931         }
32932         
32933         this.fireEvent('click', this, e);
32934     },
32935     
32936     enter: function(e, el)
32937     {
32938         e.preventDefault();
32939         
32940         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32941             return;
32942         }
32943         
32944         if(this.bgimage.length && this.html.length){
32945             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32946         }
32947     },
32948     
32949     leave: function(e, el)
32950     {
32951         e.preventDefault();
32952         
32953         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32954             return;
32955         }
32956         
32957         if(this.bgimage.length && this.html.length){
32958             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32959         }
32960     },
32961     
32962     onTouchStart: function(e, el)
32963     {
32964 //        e.preventDefault();
32965         
32966         this.touchmoved = false;
32967         
32968         if(!this.isFitContainer){
32969             return;
32970         }
32971         
32972         if(!this.bgimage.length || !this.html.length){
32973             return;
32974         }
32975         
32976         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32977         
32978         this.timer = new Date().getTime();
32979         
32980     },
32981     
32982     onTouchMove: function(e, el)
32983     {
32984         this.touchmoved = true;
32985     },
32986     
32987     onContextMenu : function(e,el)
32988     {
32989         e.preventDefault();
32990         e.stopPropagation();
32991         return false;
32992     },
32993     
32994     onTouchEnd: function(e, el)
32995     {
32996 //        e.preventDefault();
32997         
32998         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32999         
33000             this.leave(e,el);
33001             
33002             return;
33003         }
33004         
33005         if(!this.bgimage.length || !this.html.length){
33006             
33007             if(this.href.length){
33008                 window.location.href = this.href;
33009             }
33010             
33011             return;
33012         }
33013         
33014         if(!this.isFitContainer){
33015             return;
33016         }
33017         
33018         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33019         
33020         window.location.href = this.href;
33021     },
33022     
33023     //selection on single brick only
33024     selectBrick : function() {
33025         
33026         if (!this.parentId) {
33027             return;
33028         }
33029         
33030         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33031         var index = m.selectedBrick.indexOf(this.id);
33032         
33033         if ( index > -1) {
33034             m.selectedBrick.splice(index,1);
33035             this.el.removeClass(this.activeClass);
33036             return;
33037         }
33038         
33039         for(var i = 0; i < m.selectedBrick.length; i++) {
33040             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33041             b.el.removeClass(b.activeClass);
33042         }
33043         
33044         m.selectedBrick = [];
33045         
33046         m.selectedBrick.push(this.id);
33047         this.el.addClass(this.activeClass);
33048         return;
33049     },
33050     
33051     isSelected : function(){
33052         return this.el.hasClass(this.activeClass);
33053         
33054     }
33055 });
33056
33057 Roo.apply(Roo.bootstrap.MasonryBrick, {
33058     
33059     //groups: {},
33060     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33061      /**
33062     * register a Masonry Brick
33063     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33064     */
33065     
33066     register : function(brick)
33067     {
33068         //this.groups[brick.id] = brick;
33069         this.groups.add(brick.id, brick);
33070     },
33071     /**
33072     * fetch a  masonry brick based on the masonry brick ID
33073     * @param {string} the masonry brick to add
33074     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33075     */
33076     
33077     get: function(brick_id) 
33078     {
33079         // if (typeof(this.groups[brick_id]) == 'undefined') {
33080         //     return false;
33081         // }
33082         // return this.groups[brick_id] ;
33083         
33084         if(this.groups.key(brick_id)) {
33085             return this.groups.key(brick_id);
33086         }
33087         
33088         return false;
33089     }
33090     
33091     
33092     
33093 });
33094
33095  /*
33096  * - LGPL
33097  *
33098  * element
33099  * 
33100  */
33101
33102 /**
33103  * @class Roo.bootstrap.Brick
33104  * @extends Roo.bootstrap.Component
33105  * Bootstrap Brick class
33106  * 
33107  * @constructor
33108  * Create a new Brick
33109  * @param {Object} config The config object
33110  */
33111
33112 Roo.bootstrap.Brick = function(config){
33113     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33114     
33115     this.addEvents({
33116         // raw events
33117         /**
33118          * @event click
33119          * When a Brick is click
33120          * @param {Roo.bootstrap.Brick} this
33121          * @param {Roo.EventObject} e
33122          */
33123         "click" : true
33124     });
33125 };
33126
33127 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33128     
33129     /**
33130      * @cfg {String} title
33131      */   
33132     title : '',
33133     /**
33134      * @cfg {String} html
33135      */   
33136     html : '',
33137     /**
33138      * @cfg {String} bgimage
33139      */   
33140     bgimage : '',
33141     /**
33142      * @cfg {String} cls
33143      */   
33144     cls : '',
33145     /**
33146      * @cfg {String} href
33147      */   
33148     href : '',
33149     /**
33150      * @cfg {String} video
33151      */   
33152     video : '',
33153     /**
33154      * @cfg {Boolean} square
33155      */   
33156     square : true,
33157     
33158     getAutoCreate : function()
33159     {
33160         var cls = 'roo-brick';
33161         
33162         if(this.href.length){
33163             cls += ' roo-brick-link';
33164         }
33165         
33166         if(this.bgimage.length){
33167             cls += ' roo-brick-image';
33168         }
33169         
33170         if(!this.html.length && !this.bgimage.length){
33171             cls += ' roo-brick-center-title';
33172         }
33173         
33174         if(!this.html.length && this.bgimage.length){
33175             cls += ' roo-brick-bottom-title';
33176         }
33177         
33178         if(this.cls){
33179             cls += ' ' + this.cls;
33180         }
33181         
33182         var cfg = {
33183             tag: (this.href.length) ? 'a' : 'div',
33184             cls: cls,
33185             cn: [
33186                 {
33187                     tag: 'div',
33188                     cls: 'roo-brick-paragraph',
33189                     cn: []
33190                 }
33191             ]
33192         };
33193         
33194         if(this.href.length){
33195             cfg.href = this.href;
33196         }
33197         
33198         var cn = cfg.cn[0].cn;
33199         
33200         if(this.title.length){
33201             cn.push({
33202                 tag: 'h4',
33203                 cls: 'roo-brick-title',
33204                 html: this.title
33205             });
33206         }
33207         
33208         if(this.html.length){
33209             cn.push({
33210                 tag: 'p',
33211                 cls: 'roo-brick-text',
33212                 html: this.html
33213             });
33214         } else {
33215             cn.cls += ' hide';
33216         }
33217         
33218         if(this.bgimage.length){
33219             cfg.cn.push({
33220                 tag: 'img',
33221                 cls: 'roo-brick-image-view',
33222                 src: this.bgimage
33223             });
33224         }
33225         
33226         return cfg;
33227     },
33228     
33229     initEvents: function() 
33230     {
33231         if(this.title.length || this.html.length){
33232             this.el.on('mouseenter'  ,this.enter, this);
33233             this.el.on('mouseleave', this.leave, this);
33234         }
33235         
33236         Roo.EventManager.onWindowResize(this.resize, this); 
33237         
33238         if(this.bgimage.length){
33239             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33240             this.imageEl.on('load', this.onImageLoad, this);
33241             return;
33242         }
33243         
33244         this.resize();
33245     },
33246     
33247     onImageLoad : function()
33248     {
33249         this.resize();
33250     },
33251     
33252     resize : function()
33253     {
33254         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33255         
33256         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33257         
33258         if(this.bgimage.length){
33259             var image = this.el.select('.roo-brick-image-view', true).first();
33260             
33261             image.setWidth(paragraph.getWidth());
33262             
33263             if(this.square){
33264                 image.setHeight(paragraph.getWidth());
33265             }
33266             
33267             this.el.setHeight(image.getHeight());
33268             paragraph.setHeight(image.getHeight());
33269             
33270         }
33271         
33272     },
33273     
33274     enter: function(e, el)
33275     {
33276         e.preventDefault();
33277         
33278         if(this.bgimage.length){
33279             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33280             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33281         }
33282     },
33283     
33284     leave: function(e, el)
33285     {
33286         e.preventDefault();
33287         
33288         if(this.bgimage.length){
33289             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33290             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33291         }
33292     }
33293     
33294 });
33295
33296  
33297
33298  /*
33299  * - LGPL
33300  *
33301  * Number field 
33302  */
33303
33304 /**
33305  * @class Roo.bootstrap.NumberField
33306  * @extends Roo.bootstrap.Input
33307  * Bootstrap NumberField class
33308  * 
33309  * 
33310  * 
33311  * 
33312  * @constructor
33313  * Create a new NumberField
33314  * @param {Object} config The config object
33315  */
33316
33317 Roo.bootstrap.NumberField = function(config){
33318     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33319 };
33320
33321 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33322     
33323     /**
33324      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33325      */
33326     allowDecimals : true,
33327     /**
33328      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33329      */
33330     decimalSeparator : ".",
33331     /**
33332      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33333      */
33334     decimalPrecision : 2,
33335     /**
33336      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33337      */
33338     allowNegative : true,
33339     
33340     /**
33341      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33342      */
33343     allowZero: true,
33344     /**
33345      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33346      */
33347     minValue : Number.NEGATIVE_INFINITY,
33348     /**
33349      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33350      */
33351     maxValue : Number.MAX_VALUE,
33352     /**
33353      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33354      */
33355     minText : "The minimum value for this field is {0}",
33356     /**
33357      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33358      */
33359     maxText : "The maximum value for this field is {0}",
33360     /**
33361      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33362      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33363      */
33364     nanText : "{0} is not a valid number",
33365     /**
33366      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33367      */
33368     thousandsDelimiter : false,
33369     /**
33370      * @cfg {String} valueAlign alignment of value
33371      */
33372     valueAlign : "left",
33373
33374     getAutoCreate : function()
33375     {
33376         var hiddenInput = {
33377             tag: 'input',
33378             type: 'hidden',
33379             id: Roo.id(),
33380             cls: 'hidden-number-input'
33381         };
33382         
33383         if (this.name) {
33384             hiddenInput.name = this.name;
33385         }
33386         
33387         this.name = '';
33388         
33389         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33390         
33391         this.name = hiddenInput.name;
33392         
33393         if(cfg.cn.length > 0) {
33394             cfg.cn.push(hiddenInput);
33395         }
33396         
33397         return cfg;
33398     },
33399
33400     // private
33401     initEvents : function()
33402     {   
33403         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33404         
33405         var allowed = "0123456789";
33406         
33407         if(this.allowDecimals){
33408             allowed += this.decimalSeparator;
33409         }
33410         
33411         if(this.allowNegative){
33412             allowed += "-";
33413         }
33414         
33415         if(this.thousandsDelimiter) {
33416             allowed += ",";
33417         }
33418         
33419         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33420         
33421         var keyPress = function(e){
33422             
33423             var k = e.getKey();
33424             
33425             var c = e.getCharCode();
33426             
33427             if(
33428                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33429                     allowed.indexOf(String.fromCharCode(c)) === -1
33430             ){
33431                 e.stopEvent();
33432                 return;
33433             }
33434             
33435             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33436                 return;
33437             }
33438             
33439             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33440                 e.stopEvent();
33441             }
33442         };
33443         
33444         this.el.on("keypress", keyPress, this);
33445     },
33446     
33447     validateValue : function(value)
33448     {
33449         
33450         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33451             return false;
33452         }
33453         
33454         var num = this.parseValue(value);
33455         
33456         if(isNaN(num)){
33457             this.markInvalid(String.format(this.nanText, value));
33458             return false;
33459         }
33460         
33461         if(num < this.minValue){
33462             this.markInvalid(String.format(this.minText, this.minValue));
33463             return false;
33464         }
33465         
33466         if(num > this.maxValue){
33467             this.markInvalid(String.format(this.maxText, this.maxValue));
33468             return false;
33469         }
33470         
33471         return true;
33472     },
33473
33474     getValue : function()
33475     {
33476         var v = this.hiddenEl().getValue();
33477         
33478         return this.fixPrecision(this.parseValue(v));
33479     },
33480
33481     parseValue : function(value)
33482     {
33483         if(this.thousandsDelimiter) {
33484             value += "";
33485             r = new RegExp(",", "g");
33486             value = value.replace(r, "");
33487         }
33488         
33489         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33490         return isNaN(value) ? '' : value;
33491     },
33492
33493     fixPrecision : function(value)
33494     {
33495         if(this.thousandsDelimiter) {
33496             value += "";
33497             r = new RegExp(",", "g");
33498             value = value.replace(r, "");
33499         }
33500         
33501         var nan = isNaN(value);
33502         
33503         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33504             return nan ? '' : value;
33505         }
33506         return parseFloat(value).toFixed(this.decimalPrecision);
33507     },
33508
33509     setValue : function(v)
33510     {
33511         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33512         
33513         this.value = v;
33514         
33515         if(this.rendered){
33516             
33517             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33518             
33519             this.inputEl().dom.value = (v == '') ? '' :
33520                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33521             
33522             if(!this.allowZero && v === '0') {
33523                 this.hiddenEl().dom.value = '';
33524                 this.inputEl().dom.value = '';
33525             }
33526             
33527             this.validate();
33528         }
33529     },
33530
33531     decimalPrecisionFcn : function(v)
33532     {
33533         return Math.floor(v);
33534     },
33535
33536     beforeBlur : function()
33537     {
33538         var v = this.parseValue(this.getRawValue());
33539         
33540         if(v || v === 0 || v === ''){
33541             this.setValue(v);
33542         }
33543     },
33544     
33545     hiddenEl : function()
33546     {
33547         return this.el.select('input.hidden-number-input',true).first();
33548     }
33549     
33550 });
33551
33552  
33553
33554 /*
33555 * Licence: LGPL
33556 */
33557
33558 /**
33559  * @class Roo.bootstrap.DocumentSlider
33560  * @extends Roo.bootstrap.Component
33561  * Bootstrap DocumentSlider class
33562  * 
33563  * @constructor
33564  * Create a new DocumentViewer
33565  * @param {Object} config The config object
33566  */
33567
33568 Roo.bootstrap.DocumentSlider = function(config){
33569     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33570     
33571     this.files = [];
33572     
33573     this.addEvents({
33574         /**
33575          * @event initial
33576          * Fire after initEvent
33577          * @param {Roo.bootstrap.DocumentSlider} this
33578          */
33579         "initial" : true,
33580         /**
33581          * @event update
33582          * Fire after update
33583          * @param {Roo.bootstrap.DocumentSlider} this
33584          */
33585         "update" : true,
33586         /**
33587          * @event click
33588          * Fire after click
33589          * @param {Roo.bootstrap.DocumentSlider} this
33590          */
33591         "click" : true
33592     });
33593 };
33594
33595 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33596     
33597     files : false,
33598     
33599     indicator : 0,
33600     
33601     getAutoCreate : function()
33602     {
33603         var cfg = {
33604             tag : 'div',
33605             cls : 'roo-document-slider',
33606             cn : [
33607                 {
33608                     tag : 'div',
33609                     cls : 'roo-document-slider-header',
33610                     cn : [
33611                         {
33612                             tag : 'div',
33613                             cls : 'roo-document-slider-header-title'
33614                         }
33615                     ]
33616                 },
33617                 {
33618                     tag : 'div',
33619                     cls : 'roo-document-slider-body',
33620                     cn : [
33621                         {
33622                             tag : 'div',
33623                             cls : 'roo-document-slider-prev',
33624                             cn : [
33625                                 {
33626                                     tag : 'i',
33627                                     cls : 'fa fa-chevron-left'
33628                                 }
33629                             ]
33630                         },
33631                         {
33632                             tag : 'div',
33633                             cls : 'roo-document-slider-thumb',
33634                             cn : [
33635                                 {
33636                                     tag : 'img',
33637                                     cls : 'roo-document-slider-image'
33638                                 }
33639                             ]
33640                         },
33641                         {
33642                             tag : 'div',
33643                             cls : 'roo-document-slider-next',
33644                             cn : [
33645                                 {
33646                                     tag : 'i',
33647                                     cls : 'fa fa-chevron-right'
33648                                 }
33649                             ]
33650                         }
33651                     ]
33652                 }
33653             ]
33654         };
33655         
33656         return cfg;
33657     },
33658     
33659     initEvents : function()
33660     {
33661         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33662         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33663         
33664         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33665         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33666         
33667         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33668         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33669         
33670         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33671         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33672         
33673         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33674         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33675         
33676         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33677         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33678         
33679         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33680         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33681         
33682         this.thumbEl.on('click', this.onClick, this);
33683         
33684         this.prevIndicator.on('click', this.prev, this);
33685         
33686         this.nextIndicator.on('click', this.next, this);
33687         
33688     },
33689     
33690     initial : function()
33691     {
33692         if(this.files.length){
33693             this.indicator = 1;
33694             this.update()
33695         }
33696         
33697         this.fireEvent('initial', this);
33698     },
33699     
33700     update : function()
33701     {
33702         this.imageEl.attr('src', this.files[this.indicator - 1]);
33703         
33704         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33705         
33706         this.prevIndicator.show();
33707         
33708         if(this.indicator == 1){
33709             this.prevIndicator.hide();
33710         }
33711         
33712         this.nextIndicator.show();
33713         
33714         if(this.indicator == this.files.length){
33715             this.nextIndicator.hide();
33716         }
33717         
33718         this.thumbEl.scrollTo('top');
33719         
33720         this.fireEvent('update', this);
33721     },
33722     
33723     onClick : function(e)
33724     {
33725         e.preventDefault();
33726         
33727         this.fireEvent('click', this);
33728     },
33729     
33730     prev : function(e)
33731     {
33732         e.preventDefault();
33733         
33734         this.indicator = Math.max(1, this.indicator - 1);
33735         
33736         this.update();
33737     },
33738     
33739     next : function(e)
33740     {
33741         e.preventDefault();
33742         
33743         this.indicator = Math.min(this.files.length, this.indicator + 1);
33744         
33745         this.update();
33746     }
33747 });
33748 /*
33749  * - LGPL
33750  *
33751  * RadioSet
33752  *
33753  *
33754  */
33755
33756 /**
33757  * @class Roo.bootstrap.RadioSet
33758  * @extends Roo.bootstrap.Input
33759  * Bootstrap RadioSet class
33760  * @cfg {String} indicatorpos (left|right) default left
33761  * @cfg {Boolean} inline (true|false) inline the element (default true)
33762  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33763  * @constructor
33764  * Create a new RadioSet
33765  * @param {Object} config The config object
33766  */
33767
33768 Roo.bootstrap.RadioSet = function(config){
33769     
33770     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33771     
33772     this.radioes = [];
33773     
33774     Roo.bootstrap.RadioSet.register(this);
33775     
33776     this.addEvents({
33777         /**
33778         * @event check
33779         * Fires when the element is checked or unchecked.
33780         * @param {Roo.bootstrap.RadioSet} this This radio
33781         * @param {Roo.bootstrap.Radio} item The checked item
33782         */
33783        check : true,
33784        /**
33785         * @event click
33786         * Fires when the element is click.
33787         * @param {Roo.bootstrap.RadioSet} this This radio set
33788         * @param {Roo.bootstrap.Radio} item The checked item
33789         * @param {Roo.EventObject} e The event object
33790         */
33791        click : true
33792     });
33793     
33794 };
33795
33796 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33797
33798     radioes : false,
33799     
33800     inline : true,
33801     
33802     weight : '',
33803     
33804     indicatorpos : 'left',
33805     
33806     getAutoCreate : function()
33807     {
33808         var label = {
33809             tag : 'label',
33810             cls : 'roo-radio-set-label',
33811             cn : [
33812                 {
33813                     tag : 'span',
33814                     html : this.fieldLabel
33815                 }
33816             ]
33817         };
33818         
33819         if(this.indicatorpos == 'left'){
33820             label.cn.unshift({
33821                 tag : 'i',
33822                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33823                 tooltip : 'This field is required'
33824             });
33825         } else {
33826             label.cn.push({
33827                 tag : 'i',
33828                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33829                 tooltip : 'This field is required'
33830             });
33831         }
33832         
33833         var items = {
33834             tag : 'div',
33835             cls : 'roo-radio-set-items'
33836         };
33837         
33838         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33839         
33840         if (align === 'left' && this.fieldLabel.length) {
33841             
33842             items = {
33843                 cls : "roo-radio-set-right", 
33844                 cn: [
33845                     items
33846                 ]
33847             };
33848             
33849             if(this.labelWidth > 12){
33850                 label.style = "width: " + this.labelWidth + 'px';
33851             }
33852             
33853             if(this.labelWidth < 13 && this.labelmd == 0){
33854                 this.labelmd = this.labelWidth;
33855             }
33856             
33857             if(this.labellg > 0){
33858                 label.cls += ' col-lg-' + this.labellg;
33859                 items.cls += ' col-lg-' + (12 - this.labellg);
33860             }
33861             
33862             if(this.labelmd > 0){
33863                 label.cls += ' col-md-' + this.labelmd;
33864                 items.cls += ' col-md-' + (12 - this.labelmd);
33865             }
33866             
33867             if(this.labelsm > 0){
33868                 label.cls += ' col-sm-' + this.labelsm;
33869                 items.cls += ' col-sm-' + (12 - this.labelsm);
33870             }
33871             
33872             if(this.labelxs > 0){
33873                 label.cls += ' col-xs-' + this.labelxs;
33874                 items.cls += ' col-xs-' + (12 - this.labelxs);
33875             }
33876         }
33877         
33878         var cfg = {
33879             tag : 'div',
33880             cls : 'roo-radio-set',
33881             cn : [
33882                 {
33883                     tag : 'input',
33884                     cls : 'roo-radio-set-input',
33885                     type : 'hidden',
33886                     name : this.name,
33887                     value : this.value ? this.value :  ''
33888                 },
33889                 label,
33890                 items
33891             ]
33892         };
33893         
33894         if(this.weight.length){
33895             cfg.cls += ' roo-radio-' + this.weight;
33896         }
33897         
33898         if(this.inline) {
33899             cfg.cls += ' roo-radio-set-inline';
33900         }
33901         
33902         var settings=this;
33903         ['xs','sm','md','lg'].map(function(size){
33904             if (settings[size]) {
33905                 cfg.cls += ' col-' + size + '-' + settings[size];
33906             }
33907         });
33908         
33909         return cfg;
33910         
33911     },
33912
33913     initEvents : function()
33914     {
33915         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33916         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33917         
33918         if(!this.fieldLabel.length){
33919             this.labelEl.hide();
33920         }
33921         
33922         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33923         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33924         
33925         this.indicator = this.indicatorEl();
33926         
33927         if(this.indicator){
33928             this.indicator.addClass('invisible');
33929         }
33930         
33931         this.originalValue = this.getValue();
33932         
33933     },
33934     
33935     inputEl: function ()
33936     {
33937         return this.el.select('.roo-radio-set-input', true).first();
33938     },
33939     
33940     getChildContainer : function()
33941     {
33942         return this.itemsEl;
33943     },
33944     
33945     register : function(item)
33946     {
33947         this.radioes.push(item);
33948         
33949     },
33950     
33951     validate : function()
33952     {   
33953         if(this.getVisibilityEl().hasClass('hidden')){
33954             return true;
33955         }
33956         
33957         var valid = false;
33958         
33959         Roo.each(this.radioes, function(i){
33960             if(!i.checked){
33961                 return;
33962             }
33963             
33964             valid = true;
33965             return false;
33966         });
33967         
33968         if(this.allowBlank) {
33969             return true;
33970         }
33971         
33972         if(this.disabled || valid){
33973             this.markValid();
33974             return true;
33975         }
33976         
33977         this.markInvalid();
33978         return false;
33979         
33980     },
33981     
33982     markValid : function()
33983     {
33984         if(this.labelEl.isVisible(true)){
33985             this.indicatorEl().removeClass('visible');
33986             this.indicatorEl().addClass('invisible');
33987         }
33988         
33989         this.el.removeClass([this.invalidClass, this.validClass]);
33990         this.el.addClass(this.validClass);
33991         
33992         this.fireEvent('valid', this);
33993     },
33994     
33995     markInvalid : function(msg)
33996     {
33997         if(this.allowBlank || this.disabled){
33998             return;
33999         }
34000         
34001         if(this.labelEl.isVisible(true)){
34002             this.indicatorEl().removeClass('invisible');
34003             this.indicatorEl().addClass('visible');
34004         }
34005         
34006         this.el.removeClass([this.invalidClass, this.validClass]);
34007         this.el.addClass(this.invalidClass);
34008         
34009         this.fireEvent('invalid', this, msg);
34010         
34011     },
34012     
34013     setValue : function(v, suppressEvent)
34014     {   
34015         if(this.value === v){
34016             return;
34017         }
34018         
34019         this.value = v;
34020         
34021         if(this.rendered){
34022             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34023         }
34024         
34025         Roo.each(this.radioes, function(i){
34026             i.checked = false;
34027             i.el.removeClass('checked');
34028         });
34029         
34030         Roo.each(this.radioes, function(i){
34031             
34032             if(i.value === v || i.value.toString() === v.toString()){
34033                 i.checked = true;
34034                 i.el.addClass('checked');
34035                 
34036                 if(suppressEvent !== true){
34037                     this.fireEvent('check', this, i);
34038                 }
34039                 
34040                 return false;
34041             }
34042             
34043         }, this);
34044         
34045         this.validate();
34046     },
34047     
34048     clearInvalid : function(){
34049         
34050         if(!this.el || this.preventMark){
34051             return;
34052         }
34053         
34054         this.el.removeClass([this.invalidClass]);
34055         
34056         this.fireEvent('valid', this);
34057     }
34058     
34059 });
34060
34061 Roo.apply(Roo.bootstrap.RadioSet, {
34062     
34063     groups: {},
34064     
34065     register : function(set)
34066     {
34067         this.groups[set.name] = set;
34068     },
34069     
34070     get: function(name) 
34071     {
34072         if (typeof(this.groups[name]) == 'undefined') {
34073             return false;
34074         }
34075         
34076         return this.groups[name] ;
34077     }
34078     
34079 });
34080 /*
34081  * Based on:
34082  * Ext JS Library 1.1.1
34083  * Copyright(c) 2006-2007, Ext JS, LLC.
34084  *
34085  * Originally Released Under LGPL - original licence link has changed is not relivant.
34086  *
34087  * Fork - LGPL
34088  * <script type="text/javascript">
34089  */
34090
34091
34092 /**
34093  * @class Roo.bootstrap.SplitBar
34094  * @extends Roo.util.Observable
34095  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34096  * <br><br>
34097  * Usage:
34098  * <pre><code>
34099 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34100                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34101 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34102 split.minSize = 100;
34103 split.maxSize = 600;
34104 split.animate = true;
34105 split.on('moved', splitterMoved);
34106 </code></pre>
34107  * @constructor
34108  * Create a new SplitBar
34109  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34110  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34111  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34112  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34113                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34114                         position of the SplitBar).
34115  */
34116 Roo.bootstrap.SplitBar = function(cfg){
34117     
34118     /** @private */
34119     
34120     //{
34121     //  dragElement : elm
34122     //  resizingElement: el,
34123         // optional..
34124     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34125     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34126         // existingProxy ???
34127     //}
34128     
34129     this.el = Roo.get(cfg.dragElement, true);
34130     this.el.dom.unselectable = "on";
34131     /** @private */
34132     this.resizingEl = Roo.get(cfg.resizingElement, true);
34133
34134     /**
34135      * @private
34136      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34137      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34138      * @type Number
34139      */
34140     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34141     
34142     /**
34143      * The minimum size of the resizing element. (Defaults to 0)
34144      * @type Number
34145      */
34146     this.minSize = 0;
34147     
34148     /**
34149      * The maximum size of the resizing element. (Defaults to 2000)
34150      * @type Number
34151      */
34152     this.maxSize = 2000;
34153     
34154     /**
34155      * Whether to animate the transition to the new size
34156      * @type Boolean
34157      */
34158     this.animate = false;
34159     
34160     /**
34161      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34162      * @type Boolean
34163      */
34164     this.useShim = false;
34165     
34166     /** @private */
34167     this.shim = null;
34168     
34169     if(!cfg.existingProxy){
34170         /** @private */
34171         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34172     }else{
34173         this.proxy = Roo.get(cfg.existingProxy).dom;
34174     }
34175     /** @private */
34176     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34177     
34178     /** @private */
34179     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34180     
34181     /** @private */
34182     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34183     
34184     /** @private */
34185     this.dragSpecs = {};
34186     
34187     /**
34188      * @private The adapter to use to positon and resize elements
34189      */
34190     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34191     this.adapter.init(this);
34192     
34193     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34194         /** @private */
34195         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34196         this.el.addClass("roo-splitbar-h");
34197     }else{
34198         /** @private */
34199         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34200         this.el.addClass("roo-splitbar-v");
34201     }
34202     
34203     this.addEvents({
34204         /**
34205          * @event resize
34206          * Fires when the splitter is moved (alias for {@link #event-moved})
34207          * @param {Roo.bootstrap.SplitBar} this
34208          * @param {Number} newSize the new width or height
34209          */
34210         "resize" : true,
34211         /**
34212          * @event moved
34213          * Fires when the splitter is moved
34214          * @param {Roo.bootstrap.SplitBar} this
34215          * @param {Number} newSize the new width or height
34216          */
34217         "moved" : true,
34218         /**
34219          * @event beforeresize
34220          * Fires before the splitter is dragged
34221          * @param {Roo.bootstrap.SplitBar} this
34222          */
34223         "beforeresize" : true,
34224
34225         "beforeapply" : true
34226     });
34227
34228     Roo.util.Observable.call(this);
34229 };
34230
34231 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34232     onStartProxyDrag : function(x, y){
34233         this.fireEvent("beforeresize", this);
34234         if(!this.overlay){
34235             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34236             o.unselectable();
34237             o.enableDisplayMode("block");
34238             // all splitbars share the same overlay
34239             Roo.bootstrap.SplitBar.prototype.overlay = o;
34240         }
34241         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34242         this.overlay.show();
34243         Roo.get(this.proxy).setDisplayed("block");
34244         var size = this.adapter.getElementSize(this);
34245         this.activeMinSize = this.getMinimumSize();;
34246         this.activeMaxSize = this.getMaximumSize();;
34247         var c1 = size - this.activeMinSize;
34248         var c2 = Math.max(this.activeMaxSize - size, 0);
34249         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34250             this.dd.resetConstraints();
34251             this.dd.setXConstraint(
34252                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34253                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34254             );
34255             this.dd.setYConstraint(0, 0);
34256         }else{
34257             this.dd.resetConstraints();
34258             this.dd.setXConstraint(0, 0);
34259             this.dd.setYConstraint(
34260                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34261                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34262             );
34263          }
34264         this.dragSpecs.startSize = size;
34265         this.dragSpecs.startPoint = [x, y];
34266         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34267     },
34268     
34269     /** 
34270      * @private Called after the drag operation by the DDProxy
34271      */
34272     onEndProxyDrag : function(e){
34273         Roo.get(this.proxy).setDisplayed(false);
34274         var endPoint = Roo.lib.Event.getXY(e);
34275         if(this.overlay){
34276             this.overlay.hide();
34277         }
34278         var newSize;
34279         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34280             newSize = this.dragSpecs.startSize + 
34281                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34282                     endPoint[0] - this.dragSpecs.startPoint[0] :
34283                     this.dragSpecs.startPoint[0] - endPoint[0]
34284                 );
34285         }else{
34286             newSize = this.dragSpecs.startSize + 
34287                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34288                     endPoint[1] - this.dragSpecs.startPoint[1] :
34289                     this.dragSpecs.startPoint[1] - endPoint[1]
34290                 );
34291         }
34292         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34293         if(newSize != this.dragSpecs.startSize){
34294             if(this.fireEvent('beforeapply', this, newSize) !== false){
34295                 this.adapter.setElementSize(this, newSize);
34296                 this.fireEvent("moved", this, newSize);
34297                 this.fireEvent("resize", this, newSize);
34298             }
34299         }
34300     },
34301     
34302     /**
34303      * Get the adapter this SplitBar uses
34304      * @return The adapter object
34305      */
34306     getAdapter : function(){
34307         return this.adapter;
34308     },
34309     
34310     /**
34311      * Set the adapter this SplitBar uses
34312      * @param {Object} adapter A SplitBar adapter object
34313      */
34314     setAdapter : function(adapter){
34315         this.adapter = adapter;
34316         this.adapter.init(this);
34317     },
34318     
34319     /**
34320      * Gets the minimum size for the resizing element
34321      * @return {Number} The minimum size
34322      */
34323     getMinimumSize : function(){
34324         return this.minSize;
34325     },
34326     
34327     /**
34328      * Sets the minimum size for the resizing element
34329      * @param {Number} minSize The minimum size
34330      */
34331     setMinimumSize : function(minSize){
34332         this.minSize = minSize;
34333     },
34334     
34335     /**
34336      * Gets the maximum size for the resizing element
34337      * @return {Number} The maximum size
34338      */
34339     getMaximumSize : function(){
34340         return this.maxSize;
34341     },
34342     
34343     /**
34344      * Sets the maximum size for the resizing element
34345      * @param {Number} maxSize The maximum size
34346      */
34347     setMaximumSize : function(maxSize){
34348         this.maxSize = maxSize;
34349     },
34350     
34351     /**
34352      * Sets the initialize size for the resizing element
34353      * @param {Number} size The initial size
34354      */
34355     setCurrentSize : function(size){
34356         var oldAnimate = this.animate;
34357         this.animate = false;
34358         this.adapter.setElementSize(this, size);
34359         this.animate = oldAnimate;
34360     },
34361     
34362     /**
34363      * Destroy this splitbar. 
34364      * @param {Boolean} removeEl True to remove the element
34365      */
34366     destroy : function(removeEl){
34367         if(this.shim){
34368             this.shim.remove();
34369         }
34370         this.dd.unreg();
34371         this.proxy.parentNode.removeChild(this.proxy);
34372         if(removeEl){
34373             this.el.remove();
34374         }
34375     }
34376 });
34377
34378 /**
34379  * @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.
34380  */
34381 Roo.bootstrap.SplitBar.createProxy = function(dir){
34382     var proxy = new Roo.Element(document.createElement("div"));
34383     proxy.unselectable();
34384     var cls = 'roo-splitbar-proxy';
34385     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34386     document.body.appendChild(proxy.dom);
34387     return proxy.dom;
34388 };
34389
34390 /** 
34391  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34392  * Default Adapter. It assumes the splitter and resizing element are not positioned
34393  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34394  */
34395 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34396 };
34397
34398 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34399     // do nothing for now
34400     init : function(s){
34401     
34402     },
34403     /**
34404      * Called before drag operations to get the current size of the resizing element. 
34405      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34406      */
34407      getElementSize : function(s){
34408         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34409             return s.resizingEl.getWidth();
34410         }else{
34411             return s.resizingEl.getHeight();
34412         }
34413     },
34414     
34415     /**
34416      * Called after drag operations to set the size of the resizing element.
34417      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34418      * @param {Number} newSize The new size to set
34419      * @param {Function} onComplete A function to be invoked when resizing is complete
34420      */
34421     setElementSize : function(s, newSize, onComplete){
34422         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34423             if(!s.animate){
34424                 s.resizingEl.setWidth(newSize);
34425                 if(onComplete){
34426                     onComplete(s, newSize);
34427                 }
34428             }else{
34429                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34430             }
34431         }else{
34432             
34433             if(!s.animate){
34434                 s.resizingEl.setHeight(newSize);
34435                 if(onComplete){
34436                     onComplete(s, newSize);
34437                 }
34438             }else{
34439                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34440             }
34441         }
34442     }
34443 };
34444
34445 /** 
34446  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34447  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34448  * Adapter that  moves the splitter element to align with the resized sizing element. 
34449  * Used with an absolute positioned SplitBar.
34450  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34451  * document.body, make sure you assign an id to the body element.
34452  */
34453 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34454     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34455     this.container = Roo.get(container);
34456 };
34457
34458 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34459     init : function(s){
34460         this.basic.init(s);
34461     },
34462     
34463     getElementSize : function(s){
34464         return this.basic.getElementSize(s);
34465     },
34466     
34467     setElementSize : function(s, newSize, onComplete){
34468         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34469     },
34470     
34471     moveSplitter : function(s){
34472         var yes = Roo.bootstrap.SplitBar;
34473         switch(s.placement){
34474             case yes.LEFT:
34475                 s.el.setX(s.resizingEl.getRight());
34476                 break;
34477             case yes.RIGHT:
34478                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34479                 break;
34480             case yes.TOP:
34481                 s.el.setY(s.resizingEl.getBottom());
34482                 break;
34483             case yes.BOTTOM:
34484                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34485                 break;
34486         }
34487     }
34488 };
34489
34490 /**
34491  * Orientation constant - Create a vertical SplitBar
34492  * @static
34493  * @type Number
34494  */
34495 Roo.bootstrap.SplitBar.VERTICAL = 1;
34496
34497 /**
34498  * Orientation constant - Create a horizontal SplitBar
34499  * @static
34500  * @type Number
34501  */
34502 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34503
34504 /**
34505  * Placement constant - The resizing element is to the left of the splitter element
34506  * @static
34507  * @type Number
34508  */
34509 Roo.bootstrap.SplitBar.LEFT = 1;
34510
34511 /**
34512  * Placement constant - The resizing element is to the right of the splitter element
34513  * @static
34514  * @type Number
34515  */
34516 Roo.bootstrap.SplitBar.RIGHT = 2;
34517
34518 /**
34519  * Placement constant - The resizing element is positioned above the splitter element
34520  * @static
34521  * @type Number
34522  */
34523 Roo.bootstrap.SplitBar.TOP = 3;
34524
34525 /**
34526  * Placement constant - The resizing element is positioned under splitter element
34527  * @static
34528  * @type Number
34529  */
34530 Roo.bootstrap.SplitBar.BOTTOM = 4;
34531 Roo.namespace("Roo.bootstrap.layout");/*
34532  * Based on:
34533  * Ext JS Library 1.1.1
34534  * Copyright(c) 2006-2007, Ext JS, LLC.
34535  *
34536  * Originally Released Under LGPL - original licence link has changed is not relivant.
34537  *
34538  * Fork - LGPL
34539  * <script type="text/javascript">
34540  */
34541
34542 /**
34543  * @class Roo.bootstrap.layout.Manager
34544  * @extends Roo.bootstrap.Component
34545  * Base class for layout managers.
34546  */
34547 Roo.bootstrap.layout.Manager = function(config)
34548 {
34549     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34550
34551
34552
34553
34554
34555     /** false to disable window resize monitoring @type Boolean */
34556     this.monitorWindowResize = true;
34557     this.regions = {};
34558     this.addEvents({
34559         /**
34560          * @event layout
34561          * Fires when a layout is performed.
34562          * @param {Roo.LayoutManager} this
34563          */
34564         "layout" : true,
34565         /**
34566          * @event regionresized
34567          * Fires when the user resizes a region.
34568          * @param {Roo.LayoutRegion} region The resized region
34569          * @param {Number} newSize The new size (width for east/west, height for north/south)
34570          */
34571         "regionresized" : true,
34572         /**
34573          * @event regioncollapsed
34574          * Fires when a region is collapsed.
34575          * @param {Roo.LayoutRegion} region The collapsed region
34576          */
34577         "regioncollapsed" : true,
34578         /**
34579          * @event regionexpanded
34580          * Fires when a region is expanded.
34581          * @param {Roo.LayoutRegion} region The expanded region
34582          */
34583         "regionexpanded" : true
34584     });
34585     this.updating = false;
34586
34587     if (config.el) {
34588         this.el = Roo.get(config.el);
34589         this.initEvents();
34590     }
34591
34592 };
34593
34594 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34595
34596
34597     regions : null,
34598
34599     monitorWindowResize : true,
34600
34601
34602     updating : false,
34603
34604
34605     onRender : function(ct, position)
34606     {
34607         if(!this.el){
34608             this.el = Roo.get(ct);
34609             this.initEvents();
34610         }
34611         //this.fireEvent('render',this);
34612     },
34613
34614
34615     initEvents: function()
34616     {
34617
34618
34619         // ie scrollbar fix
34620         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34621             document.body.scroll = "no";
34622         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34623             this.el.position('relative');
34624         }
34625         this.id = this.el.id;
34626         this.el.addClass("roo-layout-container");
34627         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34628         if(this.el.dom != document.body ) {
34629             this.el.on('resize', this.layout,this);
34630             this.el.on('show', this.layout,this);
34631         }
34632
34633     },
34634
34635     /**
34636      * Returns true if this layout is currently being updated
34637      * @return {Boolean}
34638      */
34639     isUpdating : function(){
34640         return this.updating;
34641     },
34642
34643     /**
34644      * Suspend the LayoutManager from doing auto-layouts while
34645      * making multiple add or remove calls
34646      */
34647     beginUpdate : function(){
34648         this.updating = true;
34649     },
34650
34651     /**
34652      * Restore auto-layouts and optionally disable the manager from performing a layout
34653      * @param {Boolean} noLayout true to disable a layout update
34654      */
34655     endUpdate : function(noLayout){
34656         this.updating = false;
34657         if(!noLayout){
34658             this.layout();
34659         }
34660     },
34661
34662     layout: function(){
34663         // abstract...
34664     },
34665
34666     onRegionResized : function(region, newSize){
34667         this.fireEvent("regionresized", region, newSize);
34668         this.layout();
34669     },
34670
34671     onRegionCollapsed : function(region){
34672         this.fireEvent("regioncollapsed", region);
34673     },
34674
34675     onRegionExpanded : function(region){
34676         this.fireEvent("regionexpanded", region);
34677     },
34678
34679     /**
34680      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34681      * performs box-model adjustments.
34682      * @return {Object} The size as an object {width: (the width), height: (the height)}
34683      */
34684     getViewSize : function()
34685     {
34686         var size;
34687         if(this.el.dom != document.body){
34688             size = this.el.getSize();
34689         }else{
34690             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34691         }
34692         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34693         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34694         return size;
34695     },
34696
34697     /**
34698      * Returns the Element this layout is bound to.
34699      * @return {Roo.Element}
34700      */
34701     getEl : function(){
34702         return this.el;
34703     },
34704
34705     /**
34706      * Returns the specified region.
34707      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34708      * @return {Roo.LayoutRegion}
34709      */
34710     getRegion : function(target){
34711         return this.regions[target.toLowerCase()];
34712     },
34713
34714     onWindowResize : function(){
34715         if(this.monitorWindowResize){
34716             this.layout();
34717         }
34718     }
34719 });
34720 /*
34721  * Based on:
34722  * Ext JS Library 1.1.1
34723  * Copyright(c) 2006-2007, Ext JS, LLC.
34724  *
34725  * Originally Released Under LGPL - original licence link has changed is not relivant.
34726  *
34727  * Fork - LGPL
34728  * <script type="text/javascript">
34729  */
34730 /**
34731  * @class Roo.bootstrap.layout.Border
34732  * @extends Roo.bootstrap.layout.Manager
34733  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34734  * please see: examples/bootstrap/nested.html<br><br>
34735  
34736 <b>The container the layout is rendered into can be either the body element or any other element.
34737 If it is not the body element, the container needs to either be an absolute positioned element,
34738 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34739 the container size if it is not the body element.</b>
34740
34741 * @constructor
34742 * Create a new Border
34743 * @param {Object} config Configuration options
34744  */
34745 Roo.bootstrap.layout.Border = function(config){
34746     config = config || {};
34747     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34748     
34749     
34750     
34751     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34752         if(config[region]){
34753             config[region].region = region;
34754             this.addRegion(config[region]);
34755         }
34756     },this);
34757     
34758 };
34759
34760 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34761
34762 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34763     /**
34764      * Creates and adds a new region if it doesn't already exist.
34765      * @param {String} target The target region key (north, south, east, west or center).
34766      * @param {Object} config The regions config object
34767      * @return {BorderLayoutRegion} The new region
34768      */
34769     addRegion : function(config)
34770     {
34771         if(!this.regions[config.region]){
34772             var r = this.factory(config);
34773             this.bindRegion(r);
34774         }
34775         return this.regions[config.region];
34776     },
34777
34778     // private (kinda)
34779     bindRegion : function(r){
34780         this.regions[r.config.region] = r;
34781         
34782         r.on("visibilitychange",    this.layout, this);
34783         r.on("paneladded",          this.layout, this);
34784         r.on("panelremoved",        this.layout, this);
34785         r.on("invalidated",         this.layout, this);
34786         r.on("resized",             this.onRegionResized, this);
34787         r.on("collapsed",           this.onRegionCollapsed, this);
34788         r.on("expanded",            this.onRegionExpanded, this);
34789     },
34790
34791     /**
34792      * Performs a layout update.
34793      */
34794     layout : function()
34795     {
34796         if(this.updating) {
34797             return;
34798         }
34799         
34800         // render all the rebions if they have not been done alreayd?
34801         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34802             if(this.regions[region] && !this.regions[region].bodyEl){
34803                 this.regions[region].onRender(this.el)
34804             }
34805         },this);
34806         
34807         var size = this.getViewSize();
34808         var w = size.width;
34809         var h = size.height;
34810         var centerW = w;
34811         var centerH = h;
34812         var centerY = 0;
34813         var centerX = 0;
34814         //var x = 0, y = 0;
34815
34816         var rs = this.regions;
34817         var north = rs["north"];
34818         var south = rs["south"]; 
34819         var west = rs["west"];
34820         var east = rs["east"];
34821         var center = rs["center"];
34822         //if(this.hideOnLayout){ // not supported anymore
34823             //c.el.setStyle("display", "none");
34824         //}
34825         if(north && north.isVisible()){
34826             var b = north.getBox();
34827             var m = north.getMargins();
34828             b.width = w - (m.left+m.right);
34829             b.x = m.left;
34830             b.y = m.top;
34831             centerY = b.height + b.y + m.bottom;
34832             centerH -= centerY;
34833             north.updateBox(this.safeBox(b));
34834         }
34835         if(south && south.isVisible()){
34836             var b = south.getBox();
34837             var m = south.getMargins();
34838             b.width = w - (m.left+m.right);
34839             b.x = m.left;
34840             var totalHeight = (b.height + m.top + m.bottom);
34841             b.y = h - totalHeight + m.top;
34842             centerH -= totalHeight;
34843             south.updateBox(this.safeBox(b));
34844         }
34845         if(west && west.isVisible()){
34846             var b = west.getBox();
34847             var m = west.getMargins();
34848             b.height = centerH - (m.top+m.bottom);
34849             b.x = m.left;
34850             b.y = centerY + m.top;
34851             var totalWidth = (b.width + m.left + m.right);
34852             centerX += totalWidth;
34853             centerW -= totalWidth;
34854             west.updateBox(this.safeBox(b));
34855         }
34856         if(east && east.isVisible()){
34857             var b = east.getBox();
34858             var m = east.getMargins();
34859             b.height = centerH - (m.top+m.bottom);
34860             var totalWidth = (b.width + m.left + m.right);
34861             b.x = w - totalWidth + m.left;
34862             b.y = centerY + m.top;
34863             centerW -= totalWidth;
34864             east.updateBox(this.safeBox(b));
34865         }
34866         if(center){
34867             var m = center.getMargins();
34868             var centerBox = {
34869                 x: centerX + m.left,
34870                 y: centerY + m.top,
34871                 width: centerW - (m.left+m.right),
34872                 height: centerH - (m.top+m.bottom)
34873             };
34874             //if(this.hideOnLayout){
34875                 //center.el.setStyle("display", "block");
34876             //}
34877             center.updateBox(this.safeBox(centerBox));
34878         }
34879         this.el.repaint();
34880         this.fireEvent("layout", this);
34881     },
34882
34883     // private
34884     safeBox : function(box){
34885         box.width = Math.max(0, box.width);
34886         box.height = Math.max(0, box.height);
34887         return box;
34888     },
34889
34890     /**
34891      * Adds a ContentPanel (or subclass) to this layout.
34892      * @param {String} target The target region key (north, south, east, west or center).
34893      * @param {Roo.ContentPanel} panel The panel to add
34894      * @return {Roo.ContentPanel} The added panel
34895      */
34896     add : function(target, panel){
34897          
34898         target = target.toLowerCase();
34899         return this.regions[target].add(panel);
34900     },
34901
34902     /**
34903      * Remove a ContentPanel (or subclass) to this layout.
34904      * @param {String} target The target region key (north, south, east, west or center).
34905      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34906      * @return {Roo.ContentPanel} The removed panel
34907      */
34908     remove : function(target, panel){
34909         target = target.toLowerCase();
34910         return this.regions[target].remove(panel);
34911     },
34912
34913     /**
34914      * Searches all regions for a panel with the specified id
34915      * @param {String} panelId
34916      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34917      */
34918     findPanel : function(panelId){
34919         var rs = this.regions;
34920         for(var target in rs){
34921             if(typeof rs[target] != "function"){
34922                 var p = rs[target].getPanel(panelId);
34923                 if(p){
34924                     return p;
34925                 }
34926             }
34927         }
34928         return null;
34929     },
34930
34931     /**
34932      * Searches all regions for a panel with the specified id and activates (shows) it.
34933      * @param {String/ContentPanel} panelId The panels id or the panel itself
34934      * @return {Roo.ContentPanel} The shown panel or null
34935      */
34936     showPanel : function(panelId) {
34937       var rs = this.regions;
34938       for(var target in rs){
34939          var r = rs[target];
34940          if(typeof r != "function"){
34941             if(r.hasPanel(panelId)){
34942                return r.showPanel(panelId);
34943             }
34944          }
34945       }
34946       return null;
34947    },
34948
34949    /**
34950      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34951      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34952      */
34953    /*
34954     restoreState : function(provider){
34955         if(!provider){
34956             provider = Roo.state.Manager;
34957         }
34958         var sm = new Roo.LayoutStateManager();
34959         sm.init(this, provider);
34960     },
34961 */
34962  
34963  
34964     /**
34965      * Adds a xtype elements to the layout.
34966      * <pre><code>
34967
34968 layout.addxtype({
34969        xtype : 'ContentPanel',
34970        region: 'west',
34971        items: [ .... ]
34972    }
34973 );
34974
34975 layout.addxtype({
34976         xtype : 'NestedLayoutPanel',
34977         region: 'west',
34978         layout: {
34979            center: { },
34980            west: { }   
34981         },
34982         items : [ ... list of content panels or nested layout panels.. ]
34983    }
34984 );
34985 </code></pre>
34986      * @param {Object} cfg Xtype definition of item to add.
34987      */
34988     addxtype : function(cfg)
34989     {
34990         // basically accepts a pannel...
34991         // can accept a layout region..!?!?
34992         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34993         
34994         
34995         // theory?  children can only be panels??
34996         
34997         //if (!cfg.xtype.match(/Panel$/)) {
34998         //    return false;
34999         //}
35000         var ret = false;
35001         
35002         if (typeof(cfg.region) == 'undefined') {
35003             Roo.log("Failed to add Panel, region was not set");
35004             Roo.log(cfg);
35005             return false;
35006         }
35007         var region = cfg.region;
35008         delete cfg.region;
35009         
35010           
35011         var xitems = [];
35012         if (cfg.items) {
35013             xitems = cfg.items;
35014             delete cfg.items;
35015         }
35016         var nb = false;
35017         
35018         switch(cfg.xtype) 
35019         {
35020             case 'Content':  // ContentPanel (el, cfg)
35021             case 'Scroll':  // ContentPanel (el, cfg)
35022             case 'View': 
35023                 cfg.autoCreate = true;
35024                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35025                 //} else {
35026                 //    var el = this.el.createChild();
35027                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35028                 //}
35029                 
35030                 this.add(region, ret);
35031                 break;
35032             
35033             /*
35034             case 'TreePanel': // our new panel!
35035                 cfg.el = this.el.createChild();
35036                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35037                 this.add(region, ret);
35038                 break;
35039             */
35040             
35041             case 'Nest': 
35042                 // create a new Layout (which is  a Border Layout...
35043                 
35044                 var clayout = cfg.layout;
35045                 clayout.el  = this.el.createChild();
35046                 clayout.items   = clayout.items  || [];
35047                 
35048                 delete cfg.layout;
35049                 
35050                 // replace this exitems with the clayout ones..
35051                 xitems = clayout.items;
35052                  
35053                 // force background off if it's in center...
35054                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35055                     cfg.background = false;
35056                 }
35057                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35058                 
35059                 
35060                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35061                 //console.log('adding nested layout panel '  + cfg.toSource());
35062                 this.add(region, ret);
35063                 nb = {}; /// find first...
35064                 break;
35065             
35066             case 'Grid':
35067                 
35068                 // needs grid and region
35069                 
35070                 //var el = this.getRegion(region).el.createChild();
35071                 /*
35072                  *var el = this.el.createChild();
35073                 // create the grid first...
35074                 cfg.grid.container = el;
35075                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35076                 */
35077                 
35078                 if (region == 'center' && this.active ) {
35079                     cfg.background = false;
35080                 }
35081                 
35082                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35083                 
35084                 this.add(region, ret);
35085                 /*
35086                 if (cfg.background) {
35087                     // render grid on panel activation (if panel background)
35088                     ret.on('activate', function(gp) {
35089                         if (!gp.grid.rendered) {
35090                     //        gp.grid.render(el);
35091                         }
35092                     });
35093                 } else {
35094                   //  cfg.grid.render(el);
35095                 }
35096                 */
35097                 break;
35098            
35099            
35100             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35101                 // it was the old xcomponent building that caused this before.
35102                 // espeically if border is the top element in the tree.
35103                 ret = this;
35104                 break; 
35105                 
35106                     
35107                 
35108                 
35109                 
35110             default:
35111                 /*
35112                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35113                     
35114                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35115                     this.add(region, ret);
35116                 } else {
35117                 */
35118                     Roo.log(cfg);
35119                     throw "Can not add '" + cfg.xtype + "' to Border";
35120                     return null;
35121              
35122                                 
35123              
35124         }
35125         this.beginUpdate();
35126         // add children..
35127         var region = '';
35128         var abn = {};
35129         Roo.each(xitems, function(i)  {
35130             region = nb && i.region ? i.region : false;
35131             
35132             var add = ret.addxtype(i);
35133            
35134             if (region) {
35135                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35136                 if (!i.background) {
35137                     abn[region] = nb[region] ;
35138                 }
35139             }
35140             
35141         });
35142         this.endUpdate();
35143
35144         // make the last non-background panel active..
35145         //if (nb) { Roo.log(abn); }
35146         if (nb) {
35147             
35148             for(var r in abn) {
35149                 region = this.getRegion(r);
35150                 if (region) {
35151                     // tried using nb[r], but it does not work..
35152                      
35153                     region.showPanel(abn[r]);
35154                    
35155                 }
35156             }
35157         }
35158         return ret;
35159         
35160     },
35161     
35162     
35163 // private
35164     factory : function(cfg)
35165     {
35166         
35167         var validRegions = Roo.bootstrap.layout.Border.regions;
35168
35169         var target = cfg.region;
35170         cfg.mgr = this;
35171         
35172         var r = Roo.bootstrap.layout;
35173         Roo.log(target);
35174         switch(target){
35175             case "north":
35176                 return new r.North(cfg);
35177             case "south":
35178                 return new r.South(cfg);
35179             case "east":
35180                 return new r.East(cfg);
35181             case "west":
35182                 return new r.West(cfg);
35183             case "center":
35184                 return new r.Center(cfg);
35185         }
35186         throw 'Layout region "'+target+'" not supported.';
35187     }
35188     
35189     
35190 });
35191  /*
35192  * Based on:
35193  * Ext JS Library 1.1.1
35194  * Copyright(c) 2006-2007, Ext JS, LLC.
35195  *
35196  * Originally Released Under LGPL - original licence link has changed is not relivant.
35197  *
35198  * Fork - LGPL
35199  * <script type="text/javascript">
35200  */
35201  
35202 /**
35203  * @class Roo.bootstrap.layout.Basic
35204  * @extends Roo.util.Observable
35205  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35206  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35207  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35208  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35209  * @cfg {string}   region  the region that it inhabits..
35210  * @cfg {bool}   skipConfig skip config?
35211  * 
35212
35213  */
35214 Roo.bootstrap.layout.Basic = function(config){
35215     
35216     this.mgr = config.mgr;
35217     
35218     this.position = config.region;
35219     
35220     var skipConfig = config.skipConfig;
35221     
35222     this.events = {
35223         /**
35224          * @scope Roo.BasicLayoutRegion
35225          */
35226         
35227         /**
35228          * @event beforeremove
35229          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35230          * @param {Roo.LayoutRegion} this
35231          * @param {Roo.ContentPanel} panel The panel
35232          * @param {Object} e The cancel event object
35233          */
35234         "beforeremove" : true,
35235         /**
35236          * @event invalidated
35237          * Fires when the layout for this region is changed.
35238          * @param {Roo.LayoutRegion} this
35239          */
35240         "invalidated" : true,
35241         /**
35242          * @event visibilitychange
35243          * Fires when this region is shown or hidden 
35244          * @param {Roo.LayoutRegion} this
35245          * @param {Boolean} visibility true or false
35246          */
35247         "visibilitychange" : true,
35248         /**
35249          * @event paneladded
35250          * Fires when a panel is added. 
35251          * @param {Roo.LayoutRegion} this
35252          * @param {Roo.ContentPanel} panel The panel
35253          */
35254         "paneladded" : true,
35255         /**
35256          * @event panelremoved
35257          * Fires when a panel is removed. 
35258          * @param {Roo.LayoutRegion} this
35259          * @param {Roo.ContentPanel} panel The panel
35260          */
35261         "panelremoved" : true,
35262         /**
35263          * @event beforecollapse
35264          * Fires when this region before collapse.
35265          * @param {Roo.LayoutRegion} this
35266          */
35267         "beforecollapse" : true,
35268         /**
35269          * @event collapsed
35270          * Fires when this region is collapsed.
35271          * @param {Roo.LayoutRegion} this
35272          */
35273         "collapsed" : true,
35274         /**
35275          * @event expanded
35276          * Fires when this region is expanded.
35277          * @param {Roo.LayoutRegion} this
35278          */
35279         "expanded" : true,
35280         /**
35281          * @event slideshow
35282          * Fires when this region is slid into view.
35283          * @param {Roo.LayoutRegion} this
35284          */
35285         "slideshow" : true,
35286         /**
35287          * @event slidehide
35288          * Fires when this region slides out of view. 
35289          * @param {Roo.LayoutRegion} this
35290          */
35291         "slidehide" : true,
35292         /**
35293          * @event panelactivated
35294          * Fires when a panel is activated. 
35295          * @param {Roo.LayoutRegion} this
35296          * @param {Roo.ContentPanel} panel The activated panel
35297          */
35298         "panelactivated" : true,
35299         /**
35300          * @event resized
35301          * Fires when the user resizes this region. 
35302          * @param {Roo.LayoutRegion} this
35303          * @param {Number} newSize The new size (width for east/west, height for north/south)
35304          */
35305         "resized" : true
35306     };
35307     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35308     this.panels = new Roo.util.MixedCollection();
35309     this.panels.getKey = this.getPanelId.createDelegate(this);
35310     this.box = null;
35311     this.activePanel = null;
35312     // ensure listeners are added...
35313     
35314     if (config.listeners || config.events) {
35315         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35316             listeners : config.listeners || {},
35317             events : config.events || {}
35318         });
35319     }
35320     
35321     if(skipConfig !== true){
35322         this.applyConfig(config);
35323     }
35324 };
35325
35326 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35327 {
35328     getPanelId : function(p){
35329         return p.getId();
35330     },
35331     
35332     applyConfig : function(config){
35333         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35334         this.config = config;
35335         
35336     },
35337     
35338     /**
35339      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35340      * the width, for horizontal (north, south) the height.
35341      * @param {Number} newSize The new width or height
35342      */
35343     resizeTo : function(newSize){
35344         var el = this.el ? this.el :
35345                  (this.activePanel ? this.activePanel.getEl() : null);
35346         if(el){
35347             switch(this.position){
35348                 case "east":
35349                 case "west":
35350                     el.setWidth(newSize);
35351                     this.fireEvent("resized", this, newSize);
35352                 break;
35353                 case "north":
35354                 case "south":
35355                     el.setHeight(newSize);
35356                     this.fireEvent("resized", this, newSize);
35357                 break;                
35358             }
35359         }
35360     },
35361     
35362     getBox : function(){
35363         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35364     },
35365     
35366     getMargins : function(){
35367         return this.margins;
35368     },
35369     
35370     updateBox : function(box){
35371         this.box = box;
35372         var el = this.activePanel.getEl();
35373         el.dom.style.left = box.x + "px";
35374         el.dom.style.top = box.y + "px";
35375         this.activePanel.setSize(box.width, box.height);
35376     },
35377     
35378     /**
35379      * Returns the container element for this region.
35380      * @return {Roo.Element}
35381      */
35382     getEl : function(){
35383         return this.activePanel;
35384     },
35385     
35386     /**
35387      * Returns true if this region is currently visible.
35388      * @return {Boolean}
35389      */
35390     isVisible : function(){
35391         return this.activePanel ? true : false;
35392     },
35393     
35394     setActivePanel : function(panel){
35395         panel = this.getPanel(panel);
35396         if(this.activePanel && this.activePanel != panel){
35397             this.activePanel.setActiveState(false);
35398             this.activePanel.getEl().setLeftTop(-10000,-10000);
35399         }
35400         this.activePanel = panel;
35401         panel.setActiveState(true);
35402         if(this.box){
35403             panel.setSize(this.box.width, this.box.height);
35404         }
35405         this.fireEvent("panelactivated", this, panel);
35406         this.fireEvent("invalidated");
35407     },
35408     
35409     /**
35410      * Show the specified panel.
35411      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35412      * @return {Roo.ContentPanel} The shown panel or null
35413      */
35414     showPanel : function(panel){
35415         panel = this.getPanel(panel);
35416         if(panel){
35417             this.setActivePanel(panel);
35418         }
35419         return panel;
35420     },
35421     
35422     /**
35423      * Get the active panel for this region.
35424      * @return {Roo.ContentPanel} The active panel or null
35425      */
35426     getActivePanel : function(){
35427         return this.activePanel;
35428     },
35429     
35430     /**
35431      * Add the passed ContentPanel(s)
35432      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35433      * @return {Roo.ContentPanel} The panel added (if only one was added)
35434      */
35435     add : function(panel){
35436         if(arguments.length > 1){
35437             for(var i = 0, len = arguments.length; i < len; i++) {
35438                 this.add(arguments[i]);
35439             }
35440             return null;
35441         }
35442         if(this.hasPanel(panel)){
35443             this.showPanel(panel);
35444             return panel;
35445         }
35446         var el = panel.getEl();
35447         if(el.dom.parentNode != this.mgr.el.dom){
35448             this.mgr.el.dom.appendChild(el.dom);
35449         }
35450         if(panel.setRegion){
35451             panel.setRegion(this);
35452         }
35453         this.panels.add(panel);
35454         el.setStyle("position", "absolute");
35455         if(!panel.background){
35456             this.setActivePanel(panel);
35457             if(this.config.initialSize && this.panels.getCount()==1){
35458                 this.resizeTo(this.config.initialSize);
35459             }
35460         }
35461         this.fireEvent("paneladded", this, panel);
35462         return panel;
35463     },
35464     
35465     /**
35466      * Returns true if the panel is in this region.
35467      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35468      * @return {Boolean}
35469      */
35470     hasPanel : function(panel){
35471         if(typeof panel == "object"){ // must be panel obj
35472             panel = panel.getId();
35473         }
35474         return this.getPanel(panel) ? true : false;
35475     },
35476     
35477     /**
35478      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35479      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35480      * @param {Boolean} preservePanel Overrides the config preservePanel option
35481      * @return {Roo.ContentPanel} The panel that was removed
35482      */
35483     remove : function(panel, preservePanel){
35484         panel = this.getPanel(panel);
35485         if(!panel){
35486             return null;
35487         }
35488         var e = {};
35489         this.fireEvent("beforeremove", this, panel, e);
35490         if(e.cancel === true){
35491             return null;
35492         }
35493         var panelId = panel.getId();
35494         this.panels.removeKey(panelId);
35495         return panel;
35496     },
35497     
35498     /**
35499      * Returns the panel specified or null if it's not in this region.
35500      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35501      * @return {Roo.ContentPanel}
35502      */
35503     getPanel : function(id){
35504         if(typeof id == "object"){ // must be panel obj
35505             return id;
35506         }
35507         return this.panels.get(id);
35508     },
35509     
35510     /**
35511      * Returns this regions position (north/south/east/west/center).
35512      * @return {String} 
35513      */
35514     getPosition: function(){
35515         return this.position;    
35516     }
35517 });/*
35518  * Based on:
35519  * Ext JS Library 1.1.1
35520  * Copyright(c) 2006-2007, Ext JS, LLC.
35521  *
35522  * Originally Released Under LGPL - original licence link has changed is not relivant.
35523  *
35524  * Fork - LGPL
35525  * <script type="text/javascript">
35526  */
35527  
35528 /**
35529  * @class Roo.bootstrap.layout.Region
35530  * @extends Roo.bootstrap.layout.Basic
35531  * This class represents a region in a layout manager.
35532  
35533  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35534  * @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})
35535  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35536  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35537  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35538  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35539  * @cfg {String}    title           The title for the region (overrides panel titles)
35540  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35541  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35542  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35543  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35544  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35545  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35546  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35547  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35548  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35549  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35550
35551  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35552  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35553  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35554  * @cfg {Number}    width           For East/West panels
35555  * @cfg {Number}    height          For North/South panels
35556  * @cfg {Boolean}   split           To show the splitter
35557  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35558  * 
35559  * @cfg {string}   cls             Extra CSS classes to add to region
35560  * 
35561  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35562  * @cfg {string}   region  the region that it inhabits..
35563  *
35564
35565  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35566  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35567
35568  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35569  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35570  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35571  */
35572 Roo.bootstrap.layout.Region = function(config)
35573 {
35574     this.applyConfig(config);
35575
35576     var mgr = config.mgr;
35577     var pos = config.region;
35578     config.skipConfig = true;
35579     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35580     
35581     if (mgr.el) {
35582         this.onRender(mgr.el);   
35583     }
35584      
35585     this.visible = true;
35586     this.collapsed = false;
35587     this.unrendered_panels = [];
35588 };
35589
35590 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35591
35592     position: '', // set by wrapper (eg. north/south etc..)
35593     unrendered_panels : null,  // unrendered panels.
35594     createBody : function(){
35595         /** This region's body element 
35596         * @type Roo.Element */
35597         this.bodyEl = this.el.createChild({
35598                 tag: "div",
35599                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35600         });
35601     },
35602
35603     onRender: function(ctr, pos)
35604     {
35605         var dh = Roo.DomHelper;
35606         /** This region's container element 
35607         * @type Roo.Element */
35608         this.el = dh.append(ctr.dom, {
35609                 tag: "div",
35610                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35611             }, true);
35612         /** This region's title element 
35613         * @type Roo.Element */
35614     
35615         this.titleEl = dh.append(this.el.dom,
35616             {
35617                     tag: "div",
35618                     unselectable: "on",
35619                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35620                     children:[
35621                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35622                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35623                     ]}, true);
35624         
35625         this.titleEl.enableDisplayMode();
35626         /** This region's title text element 
35627         * @type HTMLElement */
35628         this.titleTextEl = this.titleEl.dom.firstChild;
35629         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35630         /*
35631         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35632         this.closeBtn.enableDisplayMode();
35633         this.closeBtn.on("click", this.closeClicked, this);
35634         this.closeBtn.hide();
35635     */
35636         this.createBody(this.config);
35637         if(this.config.hideWhenEmpty){
35638             this.hide();
35639             this.on("paneladded", this.validateVisibility, this);
35640             this.on("panelremoved", this.validateVisibility, this);
35641         }
35642         if(this.autoScroll){
35643             this.bodyEl.setStyle("overflow", "auto");
35644         }else{
35645             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35646         }
35647         //if(c.titlebar !== false){
35648             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35649                 this.titleEl.hide();
35650             }else{
35651                 this.titleEl.show();
35652                 if(this.config.title){
35653                     this.titleTextEl.innerHTML = this.config.title;
35654                 }
35655             }
35656         //}
35657         if(this.config.collapsed){
35658             this.collapse(true);
35659         }
35660         if(this.config.hidden){
35661             this.hide();
35662         }
35663         
35664         if (this.unrendered_panels && this.unrendered_panels.length) {
35665             for (var i =0;i< this.unrendered_panels.length; i++) {
35666                 this.add(this.unrendered_panels[i]);
35667             }
35668             this.unrendered_panels = null;
35669             
35670         }
35671         
35672     },
35673     
35674     applyConfig : function(c)
35675     {
35676         /*
35677          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35678             var dh = Roo.DomHelper;
35679             if(c.titlebar !== false){
35680                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35681                 this.collapseBtn.on("click", this.collapse, this);
35682                 this.collapseBtn.enableDisplayMode();
35683                 /*
35684                 if(c.showPin === true || this.showPin){
35685                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35686                     this.stickBtn.enableDisplayMode();
35687                     this.stickBtn.on("click", this.expand, this);
35688                     this.stickBtn.hide();
35689                 }
35690                 
35691             }
35692             */
35693             /** This region's collapsed element
35694             * @type Roo.Element */
35695             /*
35696              *
35697             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35698                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35699             ]}, true);
35700             
35701             if(c.floatable !== false){
35702                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35703                this.collapsedEl.on("click", this.collapseClick, this);
35704             }
35705
35706             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35707                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35708                    id: "message", unselectable: "on", style:{"float":"left"}});
35709                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35710              }
35711             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35712             this.expandBtn.on("click", this.expand, this);
35713             
35714         }
35715         
35716         if(this.collapseBtn){
35717             this.collapseBtn.setVisible(c.collapsible == true);
35718         }
35719         
35720         this.cmargins = c.cmargins || this.cmargins ||
35721                          (this.position == "west" || this.position == "east" ?
35722                              {top: 0, left: 2, right:2, bottom: 0} :
35723                              {top: 2, left: 0, right:0, bottom: 2});
35724         */
35725         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35726         
35727         
35728         this.bottomTabs = c.tabPosition != "top";
35729         
35730         this.autoScroll = c.autoScroll || false;
35731         
35732         
35733        
35734         
35735         this.duration = c.duration || .30;
35736         this.slideDuration = c.slideDuration || .45;
35737         this.config = c;
35738        
35739     },
35740     /**
35741      * Returns true if this region is currently visible.
35742      * @return {Boolean}
35743      */
35744     isVisible : function(){
35745         return this.visible;
35746     },
35747
35748     /**
35749      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35750      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35751      */
35752     //setCollapsedTitle : function(title){
35753     //    title = title || "&#160;";
35754      //   if(this.collapsedTitleTextEl){
35755       //      this.collapsedTitleTextEl.innerHTML = title;
35756        // }
35757     //},
35758
35759     getBox : function(){
35760         var b;
35761       //  if(!this.collapsed){
35762             b = this.el.getBox(false, true);
35763        // }else{
35764           //  b = this.collapsedEl.getBox(false, true);
35765         //}
35766         return b;
35767     },
35768
35769     getMargins : function(){
35770         return this.margins;
35771         //return this.collapsed ? this.cmargins : this.margins;
35772     },
35773 /*
35774     highlight : function(){
35775         this.el.addClass("x-layout-panel-dragover");
35776     },
35777
35778     unhighlight : function(){
35779         this.el.removeClass("x-layout-panel-dragover");
35780     },
35781 */
35782     updateBox : function(box)
35783     {
35784         if (!this.bodyEl) {
35785             return; // not rendered yet..
35786         }
35787         
35788         this.box = box;
35789         if(!this.collapsed){
35790             this.el.dom.style.left = box.x + "px";
35791             this.el.dom.style.top = box.y + "px";
35792             this.updateBody(box.width, box.height);
35793         }else{
35794             this.collapsedEl.dom.style.left = box.x + "px";
35795             this.collapsedEl.dom.style.top = box.y + "px";
35796             this.collapsedEl.setSize(box.width, box.height);
35797         }
35798         if(this.tabs){
35799             this.tabs.autoSizeTabs();
35800         }
35801     },
35802
35803     updateBody : function(w, h)
35804     {
35805         if(w !== null){
35806             this.el.setWidth(w);
35807             w -= this.el.getBorderWidth("rl");
35808             if(this.config.adjustments){
35809                 w += this.config.adjustments[0];
35810             }
35811         }
35812         if(h !== null && h > 0){
35813             this.el.setHeight(h);
35814             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35815             h -= this.el.getBorderWidth("tb");
35816             if(this.config.adjustments){
35817                 h += this.config.adjustments[1];
35818             }
35819             this.bodyEl.setHeight(h);
35820             if(this.tabs){
35821                 h = this.tabs.syncHeight(h);
35822             }
35823         }
35824         if(this.panelSize){
35825             w = w !== null ? w : this.panelSize.width;
35826             h = h !== null ? h : this.panelSize.height;
35827         }
35828         if(this.activePanel){
35829             var el = this.activePanel.getEl();
35830             w = w !== null ? w : el.getWidth();
35831             h = h !== null ? h : el.getHeight();
35832             this.panelSize = {width: w, height: h};
35833             this.activePanel.setSize(w, h);
35834         }
35835         if(Roo.isIE && this.tabs){
35836             this.tabs.el.repaint();
35837         }
35838     },
35839
35840     /**
35841      * Returns the container element for this region.
35842      * @return {Roo.Element}
35843      */
35844     getEl : function(){
35845         return this.el;
35846     },
35847
35848     /**
35849      * Hides this region.
35850      */
35851     hide : function(){
35852         //if(!this.collapsed){
35853             this.el.dom.style.left = "-2000px";
35854             this.el.hide();
35855         //}else{
35856          //   this.collapsedEl.dom.style.left = "-2000px";
35857          //   this.collapsedEl.hide();
35858        // }
35859         this.visible = false;
35860         this.fireEvent("visibilitychange", this, false);
35861     },
35862
35863     /**
35864      * Shows this region if it was previously hidden.
35865      */
35866     show : function(){
35867         //if(!this.collapsed){
35868             this.el.show();
35869         //}else{
35870         //    this.collapsedEl.show();
35871        // }
35872         this.visible = true;
35873         this.fireEvent("visibilitychange", this, true);
35874     },
35875 /*
35876     closeClicked : function(){
35877         if(this.activePanel){
35878             this.remove(this.activePanel);
35879         }
35880     },
35881
35882     collapseClick : function(e){
35883         if(this.isSlid){
35884            e.stopPropagation();
35885            this.slideIn();
35886         }else{
35887            e.stopPropagation();
35888            this.slideOut();
35889         }
35890     },
35891 */
35892     /**
35893      * Collapses this region.
35894      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35895      */
35896     /*
35897     collapse : function(skipAnim, skipCheck = false){
35898         if(this.collapsed) {
35899             return;
35900         }
35901         
35902         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35903             
35904             this.collapsed = true;
35905             if(this.split){
35906                 this.split.el.hide();
35907             }
35908             if(this.config.animate && skipAnim !== true){
35909                 this.fireEvent("invalidated", this);
35910                 this.animateCollapse();
35911             }else{
35912                 this.el.setLocation(-20000,-20000);
35913                 this.el.hide();
35914                 this.collapsedEl.show();
35915                 this.fireEvent("collapsed", this);
35916                 this.fireEvent("invalidated", this);
35917             }
35918         }
35919         
35920     },
35921 */
35922     animateCollapse : function(){
35923         // overridden
35924     },
35925
35926     /**
35927      * Expands this region if it was previously collapsed.
35928      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35929      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35930      */
35931     /*
35932     expand : function(e, skipAnim){
35933         if(e) {
35934             e.stopPropagation();
35935         }
35936         if(!this.collapsed || this.el.hasActiveFx()) {
35937             return;
35938         }
35939         if(this.isSlid){
35940             this.afterSlideIn();
35941             skipAnim = true;
35942         }
35943         this.collapsed = false;
35944         if(this.config.animate && skipAnim !== true){
35945             this.animateExpand();
35946         }else{
35947             this.el.show();
35948             if(this.split){
35949                 this.split.el.show();
35950             }
35951             this.collapsedEl.setLocation(-2000,-2000);
35952             this.collapsedEl.hide();
35953             this.fireEvent("invalidated", this);
35954             this.fireEvent("expanded", this);
35955         }
35956     },
35957 */
35958     animateExpand : function(){
35959         // overridden
35960     },
35961
35962     initTabs : function()
35963     {
35964         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35965         
35966         var ts = new Roo.bootstrap.panel.Tabs({
35967                 el: this.bodyEl.dom,
35968                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35969                 disableTooltips: this.config.disableTabTips,
35970                 toolbar : this.config.toolbar
35971             });
35972         
35973         if(this.config.hideTabs){
35974             ts.stripWrap.setDisplayed(false);
35975         }
35976         this.tabs = ts;
35977         ts.resizeTabs = this.config.resizeTabs === true;
35978         ts.minTabWidth = this.config.minTabWidth || 40;
35979         ts.maxTabWidth = this.config.maxTabWidth || 250;
35980         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35981         ts.monitorResize = false;
35982         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35983         ts.bodyEl.addClass('roo-layout-tabs-body');
35984         this.panels.each(this.initPanelAsTab, this);
35985     },
35986
35987     initPanelAsTab : function(panel){
35988         var ti = this.tabs.addTab(
35989             panel.getEl().id,
35990             panel.getTitle(),
35991             null,
35992             this.config.closeOnTab && panel.isClosable(),
35993             panel.tpl
35994         );
35995         if(panel.tabTip !== undefined){
35996             ti.setTooltip(panel.tabTip);
35997         }
35998         ti.on("activate", function(){
35999               this.setActivePanel(panel);
36000         }, this);
36001         
36002         if(this.config.closeOnTab){
36003             ti.on("beforeclose", function(t, e){
36004                 e.cancel = true;
36005                 this.remove(panel);
36006             }, this);
36007         }
36008         
36009         panel.tabItem = ti;
36010         
36011         return ti;
36012     },
36013
36014     updatePanelTitle : function(panel, title)
36015     {
36016         if(this.activePanel == panel){
36017             this.updateTitle(title);
36018         }
36019         if(this.tabs){
36020             var ti = this.tabs.getTab(panel.getEl().id);
36021             ti.setText(title);
36022             if(panel.tabTip !== undefined){
36023                 ti.setTooltip(panel.tabTip);
36024             }
36025         }
36026     },
36027
36028     updateTitle : function(title){
36029         if(this.titleTextEl && !this.config.title){
36030             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36031         }
36032     },
36033
36034     setActivePanel : function(panel)
36035     {
36036         panel = this.getPanel(panel);
36037         if(this.activePanel && this.activePanel != panel){
36038             if(this.activePanel.setActiveState(false) === false){
36039                 return;
36040             }
36041         }
36042         this.activePanel = panel;
36043         panel.setActiveState(true);
36044         if(this.panelSize){
36045             panel.setSize(this.panelSize.width, this.panelSize.height);
36046         }
36047         if(this.closeBtn){
36048             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36049         }
36050         this.updateTitle(panel.getTitle());
36051         if(this.tabs){
36052             this.fireEvent("invalidated", this);
36053         }
36054         this.fireEvent("panelactivated", this, panel);
36055     },
36056
36057     /**
36058      * Shows the specified panel.
36059      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36060      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36061      */
36062     showPanel : function(panel)
36063     {
36064         panel = this.getPanel(panel);
36065         if(panel){
36066             if(this.tabs){
36067                 var tab = this.tabs.getTab(panel.getEl().id);
36068                 if(tab.isHidden()){
36069                     this.tabs.unhideTab(tab.id);
36070                 }
36071                 tab.activate();
36072             }else{
36073                 this.setActivePanel(panel);
36074             }
36075         }
36076         return panel;
36077     },
36078
36079     /**
36080      * Get the active panel for this region.
36081      * @return {Roo.ContentPanel} The active panel or null
36082      */
36083     getActivePanel : function(){
36084         return this.activePanel;
36085     },
36086
36087     validateVisibility : function(){
36088         if(this.panels.getCount() < 1){
36089             this.updateTitle("&#160;");
36090             this.closeBtn.hide();
36091             this.hide();
36092         }else{
36093             if(!this.isVisible()){
36094                 this.show();
36095             }
36096         }
36097     },
36098
36099     /**
36100      * Adds the passed ContentPanel(s) to this region.
36101      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36102      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36103      */
36104     add : function(panel)
36105     {
36106         if(arguments.length > 1){
36107             for(var i = 0, len = arguments.length; i < len; i++) {
36108                 this.add(arguments[i]);
36109             }
36110             return null;
36111         }
36112         
36113         // if we have not been rendered yet, then we can not really do much of this..
36114         if (!this.bodyEl) {
36115             this.unrendered_panels.push(panel);
36116             return panel;
36117         }
36118         
36119         
36120         
36121         
36122         if(this.hasPanel(panel)){
36123             this.showPanel(panel);
36124             return panel;
36125         }
36126         panel.setRegion(this);
36127         this.panels.add(panel);
36128        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36129             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36130             // and hide them... ???
36131             this.bodyEl.dom.appendChild(panel.getEl().dom);
36132             if(panel.background !== true){
36133                 this.setActivePanel(panel);
36134             }
36135             this.fireEvent("paneladded", this, panel);
36136             return panel;
36137         }
36138         */
36139         if(!this.tabs){
36140             this.initTabs();
36141         }else{
36142             this.initPanelAsTab(panel);
36143         }
36144         
36145         
36146         if(panel.background !== true){
36147             this.tabs.activate(panel.getEl().id);
36148         }
36149         this.fireEvent("paneladded", this, panel);
36150         return panel;
36151     },
36152
36153     /**
36154      * Hides the tab for the specified panel.
36155      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36156      */
36157     hidePanel : function(panel){
36158         if(this.tabs && (panel = this.getPanel(panel))){
36159             this.tabs.hideTab(panel.getEl().id);
36160         }
36161     },
36162
36163     /**
36164      * Unhides the tab for a previously hidden panel.
36165      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36166      */
36167     unhidePanel : function(panel){
36168         if(this.tabs && (panel = this.getPanel(panel))){
36169             this.tabs.unhideTab(panel.getEl().id);
36170         }
36171     },
36172
36173     clearPanels : function(){
36174         while(this.panels.getCount() > 0){
36175              this.remove(this.panels.first());
36176         }
36177     },
36178
36179     /**
36180      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36181      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36182      * @param {Boolean} preservePanel Overrides the config preservePanel option
36183      * @return {Roo.ContentPanel} The panel that was removed
36184      */
36185     remove : function(panel, preservePanel)
36186     {
36187         panel = this.getPanel(panel);
36188         if(!panel){
36189             return null;
36190         }
36191         var e = {};
36192         this.fireEvent("beforeremove", this, panel, e);
36193         if(e.cancel === true){
36194             return null;
36195         }
36196         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36197         var panelId = panel.getId();
36198         this.panels.removeKey(panelId);
36199         if(preservePanel){
36200             document.body.appendChild(panel.getEl().dom);
36201         }
36202         if(this.tabs){
36203             this.tabs.removeTab(panel.getEl().id);
36204         }else if (!preservePanel){
36205             this.bodyEl.dom.removeChild(panel.getEl().dom);
36206         }
36207         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36208             var p = this.panels.first();
36209             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36210             tempEl.appendChild(p.getEl().dom);
36211             this.bodyEl.update("");
36212             this.bodyEl.dom.appendChild(p.getEl().dom);
36213             tempEl = null;
36214             this.updateTitle(p.getTitle());
36215             this.tabs = null;
36216             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36217             this.setActivePanel(p);
36218         }
36219         panel.setRegion(null);
36220         if(this.activePanel == panel){
36221             this.activePanel = null;
36222         }
36223         if(this.config.autoDestroy !== false && preservePanel !== true){
36224             try{panel.destroy();}catch(e){}
36225         }
36226         this.fireEvent("panelremoved", this, panel);
36227         return panel;
36228     },
36229
36230     /**
36231      * Returns the TabPanel component used by this region
36232      * @return {Roo.TabPanel}
36233      */
36234     getTabs : function(){
36235         return this.tabs;
36236     },
36237
36238     createTool : function(parentEl, className){
36239         var btn = Roo.DomHelper.append(parentEl, {
36240             tag: "div",
36241             cls: "x-layout-tools-button",
36242             children: [ {
36243                 tag: "div",
36244                 cls: "roo-layout-tools-button-inner " + className,
36245                 html: "&#160;"
36246             }]
36247         }, true);
36248         btn.addClassOnOver("roo-layout-tools-button-over");
36249         return btn;
36250     }
36251 });/*
36252  * Based on:
36253  * Ext JS Library 1.1.1
36254  * Copyright(c) 2006-2007, Ext JS, LLC.
36255  *
36256  * Originally Released Under LGPL - original licence link has changed is not relivant.
36257  *
36258  * Fork - LGPL
36259  * <script type="text/javascript">
36260  */
36261  
36262
36263
36264 /**
36265  * @class Roo.SplitLayoutRegion
36266  * @extends Roo.LayoutRegion
36267  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36268  */
36269 Roo.bootstrap.layout.Split = function(config){
36270     this.cursor = config.cursor;
36271     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36272 };
36273
36274 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36275 {
36276     splitTip : "Drag to resize.",
36277     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36278     useSplitTips : false,
36279
36280     applyConfig : function(config){
36281         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36282     },
36283     
36284     onRender : function(ctr,pos) {
36285         
36286         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36287         if(!this.config.split){
36288             return;
36289         }
36290         if(!this.split){
36291             
36292             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36293                             tag: "div",
36294                             id: this.el.id + "-split",
36295                             cls: "roo-layout-split roo-layout-split-"+this.position,
36296                             html: "&#160;"
36297             });
36298             /** The SplitBar for this region 
36299             * @type Roo.SplitBar */
36300             // does not exist yet...
36301             Roo.log([this.position, this.orientation]);
36302             
36303             this.split = new Roo.bootstrap.SplitBar({
36304                 dragElement : splitEl,
36305                 resizingElement: this.el,
36306                 orientation : this.orientation
36307             });
36308             
36309             this.split.on("moved", this.onSplitMove, this);
36310             this.split.useShim = this.config.useShim === true;
36311             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36312             if(this.useSplitTips){
36313                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36314             }
36315             //if(config.collapsible){
36316             //    this.split.el.on("dblclick", this.collapse,  this);
36317             //}
36318         }
36319         if(typeof this.config.minSize != "undefined"){
36320             this.split.minSize = this.config.minSize;
36321         }
36322         if(typeof this.config.maxSize != "undefined"){
36323             this.split.maxSize = this.config.maxSize;
36324         }
36325         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36326             this.hideSplitter();
36327         }
36328         
36329     },
36330
36331     getHMaxSize : function(){
36332          var cmax = this.config.maxSize || 10000;
36333          var center = this.mgr.getRegion("center");
36334          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36335     },
36336
36337     getVMaxSize : function(){
36338          var cmax = this.config.maxSize || 10000;
36339          var center = this.mgr.getRegion("center");
36340          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36341     },
36342
36343     onSplitMove : function(split, newSize){
36344         this.fireEvent("resized", this, newSize);
36345     },
36346     
36347     /** 
36348      * Returns the {@link Roo.SplitBar} for this region.
36349      * @return {Roo.SplitBar}
36350      */
36351     getSplitBar : function(){
36352         return this.split;
36353     },
36354     
36355     hide : function(){
36356         this.hideSplitter();
36357         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36358     },
36359
36360     hideSplitter : function(){
36361         if(this.split){
36362             this.split.el.setLocation(-2000,-2000);
36363             this.split.el.hide();
36364         }
36365     },
36366
36367     show : function(){
36368         if(this.split){
36369             this.split.el.show();
36370         }
36371         Roo.bootstrap.layout.Split.superclass.show.call(this);
36372     },
36373     
36374     beforeSlide: function(){
36375         if(Roo.isGecko){// firefox overflow auto bug workaround
36376             this.bodyEl.clip();
36377             if(this.tabs) {
36378                 this.tabs.bodyEl.clip();
36379             }
36380             if(this.activePanel){
36381                 this.activePanel.getEl().clip();
36382                 
36383                 if(this.activePanel.beforeSlide){
36384                     this.activePanel.beforeSlide();
36385                 }
36386             }
36387         }
36388     },
36389     
36390     afterSlide : function(){
36391         if(Roo.isGecko){// firefox overflow auto bug workaround
36392             this.bodyEl.unclip();
36393             if(this.tabs) {
36394                 this.tabs.bodyEl.unclip();
36395             }
36396             if(this.activePanel){
36397                 this.activePanel.getEl().unclip();
36398                 if(this.activePanel.afterSlide){
36399                     this.activePanel.afterSlide();
36400                 }
36401             }
36402         }
36403     },
36404
36405     initAutoHide : function(){
36406         if(this.autoHide !== false){
36407             if(!this.autoHideHd){
36408                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36409                 this.autoHideHd = {
36410                     "mouseout": function(e){
36411                         if(!e.within(this.el, true)){
36412                             st.delay(500);
36413                         }
36414                     },
36415                     "mouseover" : function(e){
36416                         st.cancel();
36417                     },
36418                     scope : this
36419                 };
36420             }
36421             this.el.on(this.autoHideHd);
36422         }
36423     },
36424
36425     clearAutoHide : function(){
36426         if(this.autoHide !== false){
36427             this.el.un("mouseout", this.autoHideHd.mouseout);
36428             this.el.un("mouseover", this.autoHideHd.mouseover);
36429         }
36430     },
36431
36432     clearMonitor : function(){
36433         Roo.get(document).un("click", this.slideInIf, this);
36434     },
36435
36436     // these names are backwards but not changed for compat
36437     slideOut : function(){
36438         if(this.isSlid || this.el.hasActiveFx()){
36439             return;
36440         }
36441         this.isSlid = true;
36442         if(this.collapseBtn){
36443             this.collapseBtn.hide();
36444         }
36445         this.closeBtnState = this.closeBtn.getStyle('display');
36446         this.closeBtn.hide();
36447         if(this.stickBtn){
36448             this.stickBtn.show();
36449         }
36450         this.el.show();
36451         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36452         this.beforeSlide();
36453         this.el.setStyle("z-index", 10001);
36454         this.el.slideIn(this.getSlideAnchor(), {
36455             callback: function(){
36456                 this.afterSlide();
36457                 this.initAutoHide();
36458                 Roo.get(document).on("click", this.slideInIf, this);
36459                 this.fireEvent("slideshow", this);
36460             },
36461             scope: this,
36462             block: true
36463         });
36464     },
36465
36466     afterSlideIn : function(){
36467         this.clearAutoHide();
36468         this.isSlid = false;
36469         this.clearMonitor();
36470         this.el.setStyle("z-index", "");
36471         if(this.collapseBtn){
36472             this.collapseBtn.show();
36473         }
36474         this.closeBtn.setStyle('display', this.closeBtnState);
36475         if(this.stickBtn){
36476             this.stickBtn.hide();
36477         }
36478         this.fireEvent("slidehide", this);
36479     },
36480
36481     slideIn : function(cb){
36482         if(!this.isSlid || this.el.hasActiveFx()){
36483             Roo.callback(cb);
36484             return;
36485         }
36486         this.isSlid = false;
36487         this.beforeSlide();
36488         this.el.slideOut(this.getSlideAnchor(), {
36489             callback: function(){
36490                 this.el.setLeftTop(-10000, -10000);
36491                 this.afterSlide();
36492                 this.afterSlideIn();
36493                 Roo.callback(cb);
36494             },
36495             scope: this,
36496             block: true
36497         });
36498     },
36499     
36500     slideInIf : function(e){
36501         if(!e.within(this.el)){
36502             this.slideIn();
36503         }
36504     },
36505
36506     animateCollapse : function(){
36507         this.beforeSlide();
36508         this.el.setStyle("z-index", 20000);
36509         var anchor = this.getSlideAnchor();
36510         this.el.slideOut(anchor, {
36511             callback : function(){
36512                 this.el.setStyle("z-index", "");
36513                 this.collapsedEl.slideIn(anchor, {duration:.3});
36514                 this.afterSlide();
36515                 this.el.setLocation(-10000,-10000);
36516                 this.el.hide();
36517                 this.fireEvent("collapsed", this);
36518             },
36519             scope: this,
36520             block: true
36521         });
36522     },
36523
36524     animateExpand : function(){
36525         this.beforeSlide();
36526         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36527         this.el.setStyle("z-index", 20000);
36528         this.collapsedEl.hide({
36529             duration:.1
36530         });
36531         this.el.slideIn(this.getSlideAnchor(), {
36532             callback : function(){
36533                 this.el.setStyle("z-index", "");
36534                 this.afterSlide();
36535                 if(this.split){
36536                     this.split.el.show();
36537                 }
36538                 this.fireEvent("invalidated", this);
36539                 this.fireEvent("expanded", this);
36540             },
36541             scope: this,
36542             block: true
36543         });
36544     },
36545
36546     anchors : {
36547         "west" : "left",
36548         "east" : "right",
36549         "north" : "top",
36550         "south" : "bottom"
36551     },
36552
36553     sanchors : {
36554         "west" : "l",
36555         "east" : "r",
36556         "north" : "t",
36557         "south" : "b"
36558     },
36559
36560     canchors : {
36561         "west" : "tl-tr",
36562         "east" : "tr-tl",
36563         "north" : "tl-bl",
36564         "south" : "bl-tl"
36565     },
36566
36567     getAnchor : function(){
36568         return this.anchors[this.position];
36569     },
36570
36571     getCollapseAnchor : function(){
36572         return this.canchors[this.position];
36573     },
36574
36575     getSlideAnchor : function(){
36576         return this.sanchors[this.position];
36577     },
36578
36579     getAlignAdj : function(){
36580         var cm = this.cmargins;
36581         switch(this.position){
36582             case "west":
36583                 return [0, 0];
36584             break;
36585             case "east":
36586                 return [0, 0];
36587             break;
36588             case "north":
36589                 return [0, 0];
36590             break;
36591             case "south":
36592                 return [0, 0];
36593             break;
36594         }
36595     },
36596
36597     getExpandAdj : function(){
36598         var c = this.collapsedEl, cm = this.cmargins;
36599         switch(this.position){
36600             case "west":
36601                 return [-(cm.right+c.getWidth()+cm.left), 0];
36602             break;
36603             case "east":
36604                 return [cm.right+c.getWidth()+cm.left, 0];
36605             break;
36606             case "north":
36607                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36608             break;
36609             case "south":
36610                 return [0, cm.top+cm.bottom+c.getHeight()];
36611             break;
36612         }
36613     }
36614 });/*
36615  * Based on:
36616  * Ext JS Library 1.1.1
36617  * Copyright(c) 2006-2007, Ext JS, LLC.
36618  *
36619  * Originally Released Under LGPL - original licence link has changed is not relivant.
36620  *
36621  * Fork - LGPL
36622  * <script type="text/javascript">
36623  */
36624 /*
36625  * These classes are private internal classes
36626  */
36627 Roo.bootstrap.layout.Center = function(config){
36628     config.region = "center";
36629     Roo.bootstrap.layout.Region.call(this, config);
36630     this.visible = true;
36631     this.minWidth = config.minWidth || 20;
36632     this.minHeight = config.minHeight || 20;
36633 };
36634
36635 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36636     hide : function(){
36637         // center panel can't be hidden
36638     },
36639     
36640     show : function(){
36641         // center panel can't be hidden
36642     },
36643     
36644     getMinWidth: function(){
36645         return this.minWidth;
36646     },
36647     
36648     getMinHeight: function(){
36649         return this.minHeight;
36650     }
36651 });
36652
36653
36654
36655
36656  
36657
36658
36659
36660
36661
36662 Roo.bootstrap.layout.North = function(config)
36663 {
36664     config.region = 'north';
36665     config.cursor = 'n-resize';
36666     
36667     Roo.bootstrap.layout.Split.call(this, config);
36668     
36669     
36670     if(this.split){
36671         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36672         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36673         this.split.el.addClass("roo-layout-split-v");
36674     }
36675     var size = config.initialSize || config.height;
36676     if(typeof size != "undefined"){
36677         this.el.setHeight(size);
36678     }
36679 };
36680 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36681 {
36682     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36683     
36684     
36685     
36686     getBox : function(){
36687         if(this.collapsed){
36688             return this.collapsedEl.getBox();
36689         }
36690         var box = this.el.getBox();
36691         if(this.split){
36692             box.height += this.split.el.getHeight();
36693         }
36694         return box;
36695     },
36696     
36697     updateBox : function(box){
36698         if(this.split && !this.collapsed){
36699             box.height -= this.split.el.getHeight();
36700             this.split.el.setLeft(box.x);
36701             this.split.el.setTop(box.y+box.height);
36702             this.split.el.setWidth(box.width);
36703         }
36704         if(this.collapsed){
36705             this.updateBody(box.width, null);
36706         }
36707         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36708     }
36709 });
36710
36711
36712
36713
36714
36715 Roo.bootstrap.layout.South = function(config){
36716     config.region = 'south';
36717     config.cursor = 's-resize';
36718     Roo.bootstrap.layout.Split.call(this, config);
36719     if(this.split){
36720         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36721         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36722         this.split.el.addClass("roo-layout-split-v");
36723     }
36724     var size = config.initialSize || config.height;
36725     if(typeof size != "undefined"){
36726         this.el.setHeight(size);
36727     }
36728 };
36729
36730 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36731     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36732     getBox : function(){
36733         if(this.collapsed){
36734             return this.collapsedEl.getBox();
36735         }
36736         var box = this.el.getBox();
36737         if(this.split){
36738             var sh = this.split.el.getHeight();
36739             box.height += sh;
36740             box.y -= sh;
36741         }
36742         return box;
36743     },
36744     
36745     updateBox : function(box){
36746         if(this.split && !this.collapsed){
36747             var sh = this.split.el.getHeight();
36748             box.height -= sh;
36749             box.y += sh;
36750             this.split.el.setLeft(box.x);
36751             this.split.el.setTop(box.y-sh);
36752             this.split.el.setWidth(box.width);
36753         }
36754         if(this.collapsed){
36755             this.updateBody(box.width, null);
36756         }
36757         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36758     }
36759 });
36760
36761 Roo.bootstrap.layout.East = function(config){
36762     config.region = "east";
36763     config.cursor = "e-resize";
36764     Roo.bootstrap.layout.Split.call(this, config);
36765     if(this.split){
36766         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36767         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36768         this.split.el.addClass("roo-layout-split-h");
36769     }
36770     var size = config.initialSize || config.width;
36771     if(typeof size != "undefined"){
36772         this.el.setWidth(size);
36773     }
36774 };
36775 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36776     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36777     getBox : function(){
36778         if(this.collapsed){
36779             return this.collapsedEl.getBox();
36780         }
36781         var box = this.el.getBox();
36782         if(this.split){
36783             var sw = this.split.el.getWidth();
36784             box.width += sw;
36785             box.x -= sw;
36786         }
36787         return box;
36788     },
36789
36790     updateBox : function(box){
36791         if(this.split && !this.collapsed){
36792             var sw = this.split.el.getWidth();
36793             box.width -= sw;
36794             this.split.el.setLeft(box.x);
36795             this.split.el.setTop(box.y);
36796             this.split.el.setHeight(box.height);
36797             box.x += sw;
36798         }
36799         if(this.collapsed){
36800             this.updateBody(null, box.height);
36801         }
36802         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36803     }
36804 });
36805
36806 Roo.bootstrap.layout.West = function(config){
36807     config.region = "west";
36808     config.cursor = "w-resize";
36809     
36810     Roo.bootstrap.layout.Split.call(this, config);
36811     if(this.split){
36812         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36813         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36814         this.split.el.addClass("roo-layout-split-h");
36815     }
36816     
36817 };
36818 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36819     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36820     
36821     onRender: function(ctr, pos)
36822     {
36823         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36824         var size = this.config.initialSize || this.config.width;
36825         if(typeof size != "undefined"){
36826             this.el.setWidth(size);
36827         }
36828     },
36829     
36830     getBox : function(){
36831         if(this.collapsed){
36832             return this.collapsedEl.getBox();
36833         }
36834         var box = this.el.getBox();
36835         if(this.split){
36836             box.width += this.split.el.getWidth();
36837         }
36838         return box;
36839     },
36840     
36841     updateBox : function(box){
36842         if(this.split && !this.collapsed){
36843             var sw = this.split.el.getWidth();
36844             box.width -= sw;
36845             this.split.el.setLeft(box.x+box.width);
36846             this.split.el.setTop(box.y);
36847             this.split.el.setHeight(box.height);
36848         }
36849         if(this.collapsed){
36850             this.updateBody(null, box.height);
36851         }
36852         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36853     }
36854 });
36855 Roo.namespace("Roo.bootstrap.panel");/*
36856  * Based on:
36857  * Ext JS Library 1.1.1
36858  * Copyright(c) 2006-2007, Ext JS, LLC.
36859  *
36860  * Originally Released Under LGPL - original licence link has changed is not relivant.
36861  *
36862  * Fork - LGPL
36863  * <script type="text/javascript">
36864  */
36865 /**
36866  * @class Roo.ContentPanel
36867  * @extends Roo.util.Observable
36868  * A basic ContentPanel element.
36869  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36870  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36871  * @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
36872  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36873  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36874  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36875  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36876  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36877  * @cfg {String} title          The title for this panel
36878  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36879  * @cfg {String} url            Calls {@link #setUrl} with this value
36880  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36881  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36882  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36883  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36884  * @cfg {Boolean} badges render the badges
36885
36886  * @constructor
36887  * Create a new ContentPanel.
36888  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36889  * @param {String/Object} config A string to set only the title or a config object
36890  * @param {String} content (optional) Set the HTML content for this panel
36891  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36892  */
36893 Roo.bootstrap.panel.Content = function( config){
36894     
36895     this.tpl = config.tpl || false;
36896     
36897     var el = config.el;
36898     var content = config.content;
36899
36900     if(config.autoCreate){ // xtype is available if this is called from factory
36901         el = Roo.id();
36902     }
36903     this.el = Roo.get(el);
36904     if(!this.el && config && config.autoCreate){
36905         if(typeof config.autoCreate == "object"){
36906             if(!config.autoCreate.id){
36907                 config.autoCreate.id = config.id||el;
36908             }
36909             this.el = Roo.DomHelper.append(document.body,
36910                         config.autoCreate, true);
36911         }else{
36912             var elcfg =  {   tag: "div",
36913                             cls: "roo-layout-inactive-content",
36914                             id: config.id||el
36915                             };
36916             if (config.html) {
36917                 elcfg.html = config.html;
36918                 
36919             }
36920                         
36921             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36922         }
36923     } 
36924     this.closable = false;
36925     this.loaded = false;
36926     this.active = false;
36927    
36928       
36929     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36930         
36931         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36932         
36933         this.wrapEl = this.el; //this.el.wrap();
36934         var ti = [];
36935         if (config.toolbar.items) {
36936             ti = config.toolbar.items ;
36937             delete config.toolbar.items ;
36938         }
36939         
36940         var nitems = [];
36941         this.toolbar.render(this.wrapEl, 'before');
36942         for(var i =0;i < ti.length;i++) {
36943           //  Roo.log(['add child', items[i]]);
36944             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36945         }
36946         this.toolbar.items = nitems;
36947         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36948         delete config.toolbar;
36949         
36950     }
36951     /*
36952     // xtype created footer. - not sure if will work as we normally have to render first..
36953     if (this.footer && !this.footer.el && this.footer.xtype) {
36954         if (!this.wrapEl) {
36955             this.wrapEl = this.el.wrap();
36956         }
36957     
36958         this.footer.container = this.wrapEl.createChild();
36959          
36960         this.footer = Roo.factory(this.footer, Roo);
36961         
36962     }
36963     */
36964     
36965      if(typeof config == "string"){
36966         this.title = config;
36967     }else{
36968         Roo.apply(this, config);
36969     }
36970     
36971     if(this.resizeEl){
36972         this.resizeEl = Roo.get(this.resizeEl, true);
36973     }else{
36974         this.resizeEl = this.el;
36975     }
36976     // handle view.xtype
36977     
36978  
36979     
36980     
36981     this.addEvents({
36982         /**
36983          * @event activate
36984          * Fires when this panel is activated. 
36985          * @param {Roo.ContentPanel} this
36986          */
36987         "activate" : true,
36988         /**
36989          * @event deactivate
36990          * Fires when this panel is activated. 
36991          * @param {Roo.ContentPanel} this
36992          */
36993         "deactivate" : true,
36994
36995         /**
36996          * @event resize
36997          * Fires when this panel is resized if fitToFrame is true.
36998          * @param {Roo.ContentPanel} this
36999          * @param {Number} width The width after any component adjustments
37000          * @param {Number} height The height after any component adjustments
37001          */
37002         "resize" : true,
37003         
37004          /**
37005          * @event render
37006          * Fires when this tab is created
37007          * @param {Roo.ContentPanel} this
37008          */
37009         "render" : true
37010         
37011         
37012         
37013     });
37014     
37015
37016     
37017     
37018     if(this.autoScroll){
37019         this.resizeEl.setStyle("overflow", "auto");
37020     } else {
37021         // fix randome scrolling
37022         //this.el.on('scroll', function() {
37023         //    Roo.log('fix random scolling');
37024         //    this.scrollTo('top',0); 
37025         //});
37026     }
37027     content = content || this.content;
37028     if(content){
37029         this.setContent(content);
37030     }
37031     if(config && config.url){
37032         this.setUrl(this.url, this.params, this.loadOnce);
37033     }
37034     
37035     
37036     
37037     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37038     
37039     if (this.view && typeof(this.view.xtype) != 'undefined') {
37040         this.view.el = this.el.appendChild(document.createElement("div"));
37041         this.view = Roo.factory(this.view); 
37042         this.view.render  &&  this.view.render(false, '');  
37043     }
37044     
37045     
37046     this.fireEvent('render', this);
37047 };
37048
37049 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37050     
37051     tabTip : '',
37052     
37053     setRegion : function(region){
37054         this.region = region;
37055         this.setActiveClass(region && !this.background);
37056     },
37057     
37058     
37059     setActiveClass: function(state)
37060     {
37061         if(state){
37062            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37063            this.el.setStyle('position','relative');
37064         }else{
37065            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37066            this.el.setStyle('position', 'absolute');
37067         } 
37068     },
37069     
37070     /**
37071      * Returns the toolbar for this Panel if one was configured. 
37072      * @return {Roo.Toolbar} 
37073      */
37074     getToolbar : function(){
37075         return this.toolbar;
37076     },
37077     
37078     setActiveState : function(active)
37079     {
37080         this.active = active;
37081         this.setActiveClass(active);
37082         if(!active){
37083             if(this.fireEvent("deactivate", this) === false){
37084                 return false;
37085             }
37086             return true;
37087         }
37088         this.fireEvent("activate", this);
37089         return true;
37090     },
37091     /**
37092      * Updates this panel's element
37093      * @param {String} content The new content
37094      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37095     */
37096     setContent : function(content, loadScripts){
37097         this.el.update(content, loadScripts);
37098     },
37099
37100     ignoreResize : function(w, h){
37101         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37102             return true;
37103         }else{
37104             this.lastSize = {width: w, height: h};
37105             return false;
37106         }
37107     },
37108     /**
37109      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37110      * @return {Roo.UpdateManager} The UpdateManager
37111      */
37112     getUpdateManager : function(){
37113         return this.el.getUpdateManager();
37114     },
37115      /**
37116      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37117      * @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:
37118 <pre><code>
37119 panel.load({
37120     url: "your-url.php",
37121     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37122     callback: yourFunction,
37123     scope: yourObject, //(optional scope)
37124     discardUrl: false,
37125     nocache: false,
37126     text: "Loading...",
37127     timeout: 30,
37128     scripts: false
37129 });
37130 </code></pre>
37131      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37132      * 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.
37133      * @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}
37134      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37135      * @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.
37136      * @return {Roo.ContentPanel} this
37137      */
37138     load : function(){
37139         var um = this.el.getUpdateManager();
37140         um.update.apply(um, arguments);
37141         return this;
37142     },
37143
37144
37145     /**
37146      * 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.
37147      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37148      * @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)
37149      * @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)
37150      * @return {Roo.UpdateManager} The UpdateManager
37151      */
37152     setUrl : function(url, params, loadOnce){
37153         if(this.refreshDelegate){
37154             this.removeListener("activate", this.refreshDelegate);
37155         }
37156         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37157         this.on("activate", this.refreshDelegate);
37158         return this.el.getUpdateManager();
37159     },
37160     
37161     _handleRefresh : function(url, params, loadOnce){
37162         if(!loadOnce || !this.loaded){
37163             var updater = this.el.getUpdateManager();
37164             updater.update(url, params, this._setLoaded.createDelegate(this));
37165         }
37166     },
37167     
37168     _setLoaded : function(){
37169         this.loaded = true;
37170     }, 
37171     
37172     /**
37173      * Returns this panel's id
37174      * @return {String} 
37175      */
37176     getId : function(){
37177         return this.el.id;
37178     },
37179     
37180     /** 
37181      * Returns this panel's element - used by regiosn to add.
37182      * @return {Roo.Element} 
37183      */
37184     getEl : function(){
37185         return this.wrapEl || this.el;
37186     },
37187     
37188    
37189     
37190     adjustForComponents : function(width, height)
37191     {
37192         //Roo.log('adjustForComponents ');
37193         if(this.resizeEl != this.el){
37194             width -= this.el.getFrameWidth('lr');
37195             height -= this.el.getFrameWidth('tb');
37196         }
37197         if(this.toolbar){
37198             var te = this.toolbar.getEl();
37199             te.setWidth(width);
37200             height -= te.getHeight();
37201         }
37202         if(this.footer){
37203             var te = this.footer.getEl();
37204             te.setWidth(width);
37205             height -= te.getHeight();
37206         }
37207         
37208         
37209         if(this.adjustments){
37210             width += this.adjustments[0];
37211             height += this.adjustments[1];
37212         }
37213         return {"width": width, "height": height};
37214     },
37215     
37216     setSize : function(width, height){
37217         if(this.fitToFrame && !this.ignoreResize(width, height)){
37218             if(this.fitContainer && this.resizeEl != this.el){
37219                 this.el.setSize(width, height);
37220             }
37221             var size = this.adjustForComponents(width, height);
37222             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37223             this.fireEvent('resize', this, size.width, size.height);
37224         }
37225     },
37226     
37227     /**
37228      * Returns this panel's title
37229      * @return {String} 
37230      */
37231     getTitle : function(){
37232         
37233         if (typeof(this.title) != 'object') {
37234             return this.title;
37235         }
37236         
37237         var t = '';
37238         for (var k in this.title) {
37239             if (!this.title.hasOwnProperty(k)) {
37240                 continue;
37241             }
37242             
37243             if (k.indexOf('-') >= 0) {
37244                 var s = k.split('-');
37245                 for (var i = 0; i<s.length; i++) {
37246                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37247                 }
37248             } else {
37249                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37250             }
37251         }
37252         return t;
37253     },
37254     
37255     /**
37256      * Set this panel's title
37257      * @param {String} title
37258      */
37259     setTitle : function(title){
37260         this.title = title;
37261         if(this.region){
37262             this.region.updatePanelTitle(this, title);
37263         }
37264     },
37265     
37266     /**
37267      * Returns true is this panel was configured to be closable
37268      * @return {Boolean} 
37269      */
37270     isClosable : function(){
37271         return this.closable;
37272     },
37273     
37274     beforeSlide : function(){
37275         this.el.clip();
37276         this.resizeEl.clip();
37277     },
37278     
37279     afterSlide : function(){
37280         this.el.unclip();
37281         this.resizeEl.unclip();
37282     },
37283     
37284     /**
37285      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37286      *   Will fail silently if the {@link #setUrl} method has not been called.
37287      *   This does not activate the panel, just updates its content.
37288      */
37289     refresh : function(){
37290         if(this.refreshDelegate){
37291            this.loaded = false;
37292            this.refreshDelegate();
37293         }
37294     },
37295     
37296     /**
37297      * Destroys this panel
37298      */
37299     destroy : function(){
37300         this.el.removeAllListeners();
37301         var tempEl = document.createElement("span");
37302         tempEl.appendChild(this.el.dom);
37303         tempEl.innerHTML = "";
37304         this.el.remove();
37305         this.el = null;
37306     },
37307     
37308     /**
37309      * form - if the content panel contains a form - this is a reference to it.
37310      * @type {Roo.form.Form}
37311      */
37312     form : false,
37313     /**
37314      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37315      *    This contains a reference to it.
37316      * @type {Roo.View}
37317      */
37318     view : false,
37319     
37320       /**
37321      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37322      * <pre><code>
37323
37324 layout.addxtype({
37325        xtype : 'Form',
37326        items: [ .... ]
37327    }
37328 );
37329
37330 </code></pre>
37331      * @param {Object} cfg Xtype definition of item to add.
37332      */
37333     
37334     
37335     getChildContainer: function () {
37336         return this.getEl();
37337     }
37338     
37339     
37340     /*
37341         var  ret = new Roo.factory(cfg);
37342         return ret;
37343         
37344         
37345         // add form..
37346         if (cfg.xtype.match(/^Form$/)) {
37347             
37348             var el;
37349             //if (this.footer) {
37350             //    el = this.footer.container.insertSibling(false, 'before');
37351             //} else {
37352                 el = this.el.createChild();
37353             //}
37354
37355             this.form = new  Roo.form.Form(cfg);
37356             
37357             
37358             if ( this.form.allItems.length) {
37359                 this.form.render(el.dom);
37360             }
37361             return this.form;
37362         }
37363         // should only have one of theses..
37364         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37365             // views.. should not be just added - used named prop 'view''
37366             
37367             cfg.el = this.el.appendChild(document.createElement("div"));
37368             // factory?
37369             
37370             var ret = new Roo.factory(cfg);
37371              
37372              ret.render && ret.render(false, ''); // render blank..
37373             this.view = ret;
37374             return ret;
37375         }
37376         return false;
37377     }
37378     \*/
37379 });
37380  
37381 /**
37382  * @class Roo.bootstrap.panel.Grid
37383  * @extends Roo.bootstrap.panel.Content
37384  * @constructor
37385  * Create a new GridPanel.
37386  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37387  * @param {Object} config A the config object
37388   
37389  */
37390
37391
37392
37393 Roo.bootstrap.panel.Grid = function(config)
37394 {
37395     
37396       
37397     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37398         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37399
37400     config.el = this.wrapper;
37401     //this.el = this.wrapper;
37402     
37403       if (config.container) {
37404         // ctor'ed from a Border/panel.grid
37405         
37406         
37407         this.wrapper.setStyle("overflow", "hidden");
37408         this.wrapper.addClass('roo-grid-container');
37409
37410     }
37411     
37412     
37413     if(config.toolbar){
37414         var tool_el = this.wrapper.createChild();    
37415         this.toolbar = Roo.factory(config.toolbar);
37416         var ti = [];
37417         if (config.toolbar.items) {
37418             ti = config.toolbar.items ;
37419             delete config.toolbar.items ;
37420         }
37421         
37422         var nitems = [];
37423         this.toolbar.render(tool_el);
37424         for(var i =0;i < ti.length;i++) {
37425           //  Roo.log(['add child', items[i]]);
37426             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37427         }
37428         this.toolbar.items = nitems;
37429         
37430         delete config.toolbar;
37431     }
37432     
37433     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37434     config.grid.scrollBody = true;;
37435     config.grid.monitorWindowResize = false; // turn off autosizing
37436     config.grid.autoHeight = false;
37437     config.grid.autoWidth = false;
37438     
37439     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37440     
37441     if (config.background) {
37442         // render grid on panel activation (if panel background)
37443         this.on('activate', function(gp) {
37444             if (!gp.grid.rendered) {
37445                 gp.grid.render(this.wrapper);
37446                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37447             }
37448         });
37449             
37450     } else {
37451         this.grid.render(this.wrapper);
37452         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37453
37454     }
37455     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37456     // ??? needed ??? config.el = this.wrapper;
37457     
37458     
37459     
37460   
37461     // xtype created footer. - not sure if will work as we normally have to render first..
37462     if (this.footer && !this.footer.el && this.footer.xtype) {
37463         
37464         var ctr = this.grid.getView().getFooterPanel(true);
37465         this.footer.dataSource = this.grid.dataSource;
37466         this.footer = Roo.factory(this.footer, Roo);
37467         this.footer.render(ctr);
37468         
37469     }
37470     
37471     
37472     
37473     
37474      
37475 };
37476
37477 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37478     getId : function(){
37479         return this.grid.id;
37480     },
37481     
37482     /**
37483      * Returns the grid for this panel
37484      * @return {Roo.bootstrap.Table} 
37485      */
37486     getGrid : function(){
37487         return this.grid;    
37488     },
37489     
37490     setSize : function(width, height){
37491         if(!this.ignoreResize(width, height)){
37492             var grid = this.grid;
37493             var size = this.adjustForComponents(width, height);
37494             var gridel = grid.getGridEl();
37495             gridel.setSize(size.width, size.height);
37496             /*
37497             var thd = grid.getGridEl().select('thead',true).first();
37498             var tbd = grid.getGridEl().select('tbody', true).first();
37499             if (tbd) {
37500                 tbd.setSize(width, height - thd.getHeight());
37501             }
37502             */
37503             grid.autoSize();
37504         }
37505     },
37506      
37507     
37508     
37509     beforeSlide : function(){
37510         this.grid.getView().scroller.clip();
37511     },
37512     
37513     afterSlide : function(){
37514         this.grid.getView().scroller.unclip();
37515     },
37516     
37517     destroy : function(){
37518         this.grid.destroy();
37519         delete this.grid;
37520         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37521     }
37522 });
37523
37524 /**
37525  * @class Roo.bootstrap.panel.Nest
37526  * @extends Roo.bootstrap.panel.Content
37527  * @constructor
37528  * Create a new Panel, that can contain a layout.Border.
37529  * 
37530  * 
37531  * @param {Roo.BorderLayout} layout The layout for this panel
37532  * @param {String/Object} config A string to set only the title or a config object
37533  */
37534 Roo.bootstrap.panel.Nest = function(config)
37535 {
37536     // construct with only one argument..
37537     /* FIXME - implement nicer consturctors
37538     if (layout.layout) {
37539         config = layout;
37540         layout = config.layout;
37541         delete config.layout;
37542     }
37543     if (layout.xtype && !layout.getEl) {
37544         // then layout needs constructing..
37545         layout = Roo.factory(layout, Roo);
37546     }
37547     */
37548     
37549     config.el =  config.layout.getEl();
37550     
37551     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37552     
37553     config.layout.monitorWindowResize = false; // turn off autosizing
37554     this.layout = config.layout;
37555     this.layout.getEl().addClass("roo-layout-nested-layout");
37556     
37557     
37558     
37559     
37560 };
37561
37562 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37563
37564     setSize : function(width, height){
37565         if(!this.ignoreResize(width, height)){
37566             var size = this.adjustForComponents(width, height);
37567             var el = this.layout.getEl();
37568             if (size.height < 1) {
37569                 el.setWidth(size.width);   
37570             } else {
37571                 el.setSize(size.width, size.height);
37572             }
37573             var touch = el.dom.offsetWidth;
37574             this.layout.layout();
37575             // ie requires a double layout on the first pass
37576             if(Roo.isIE && !this.initialized){
37577                 this.initialized = true;
37578                 this.layout.layout();
37579             }
37580         }
37581     },
37582     
37583     // activate all subpanels if not currently active..
37584     
37585     setActiveState : function(active){
37586         this.active = active;
37587         this.setActiveClass(active);
37588         
37589         if(!active){
37590             this.fireEvent("deactivate", this);
37591             return;
37592         }
37593         
37594         this.fireEvent("activate", this);
37595         // not sure if this should happen before or after..
37596         if (!this.layout) {
37597             return; // should not happen..
37598         }
37599         var reg = false;
37600         for (var r in this.layout.regions) {
37601             reg = this.layout.getRegion(r);
37602             if (reg.getActivePanel()) {
37603                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37604                 reg.setActivePanel(reg.getActivePanel());
37605                 continue;
37606             }
37607             if (!reg.panels.length) {
37608                 continue;
37609             }
37610             reg.showPanel(reg.getPanel(0));
37611         }
37612         
37613         
37614         
37615         
37616     },
37617     
37618     /**
37619      * Returns the nested BorderLayout for this panel
37620      * @return {Roo.BorderLayout} 
37621      */
37622     getLayout : function(){
37623         return this.layout;
37624     },
37625     
37626      /**
37627      * Adds a xtype elements to the layout of the nested panel
37628      * <pre><code>
37629
37630 panel.addxtype({
37631        xtype : 'ContentPanel',
37632        region: 'west',
37633        items: [ .... ]
37634    }
37635 );
37636
37637 panel.addxtype({
37638         xtype : 'NestedLayoutPanel',
37639         region: 'west',
37640         layout: {
37641            center: { },
37642            west: { }   
37643         },
37644         items : [ ... list of content panels or nested layout panels.. ]
37645    }
37646 );
37647 </code></pre>
37648      * @param {Object} cfg Xtype definition of item to add.
37649      */
37650     addxtype : function(cfg) {
37651         return this.layout.addxtype(cfg);
37652     
37653     }
37654 });        /*
37655  * Based on:
37656  * Ext JS Library 1.1.1
37657  * Copyright(c) 2006-2007, Ext JS, LLC.
37658  *
37659  * Originally Released Under LGPL - original licence link has changed is not relivant.
37660  *
37661  * Fork - LGPL
37662  * <script type="text/javascript">
37663  */
37664 /**
37665  * @class Roo.TabPanel
37666  * @extends Roo.util.Observable
37667  * A lightweight tab container.
37668  * <br><br>
37669  * Usage:
37670  * <pre><code>
37671 // basic tabs 1, built from existing content
37672 var tabs = new Roo.TabPanel("tabs1");
37673 tabs.addTab("script", "View Script");
37674 tabs.addTab("markup", "View Markup");
37675 tabs.activate("script");
37676
37677 // more advanced tabs, built from javascript
37678 var jtabs = new Roo.TabPanel("jtabs");
37679 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37680
37681 // set up the UpdateManager
37682 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37683 var updater = tab2.getUpdateManager();
37684 updater.setDefaultUrl("ajax1.htm");
37685 tab2.on('activate', updater.refresh, updater, true);
37686
37687 // Use setUrl for Ajax loading
37688 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37689 tab3.setUrl("ajax2.htm", null, true);
37690
37691 // Disabled tab
37692 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37693 tab4.disable();
37694
37695 jtabs.activate("jtabs-1");
37696  * </code></pre>
37697  * @constructor
37698  * Create a new TabPanel.
37699  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37700  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37701  */
37702 Roo.bootstrap.panel.Tabs = function(config){
37703     /**
37704     * The container element for this TabPanel.
37705     * @type Roo.Element
37706     */
37707     this.el = Roo.get(config.el);
37708     delete config.el;
37709     if(config){
37710         if(typeof config == "boolean"){
37711             this.tabPosition = config ? "bottom" : "top";
37712         }else{
37713             Roo.apply(this, config);
37714         }
37715     }
37716     
37717     if(this.tabPosition == "bottom"){
37718         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37719         this.el.addClass("roo-tabs-bottom");
37720     }
37721     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37722     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37723     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37724     if(Roo.isIE){
37725         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37726     }
37727     if(this.tabPosition != "bottom"){
37728         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37729          * @type Roo.Element
37730          */
37731         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37732         this.el.addClass("roo-tabs-top");
37733     }
37734     this.items = [];
37735
37736     this.bodyEl.setStyle("position", "relative");
37737
37738     this.active = null;
37739     this.activateDelegate = this.activate.createDelegate(this);
37740
37741     this.addEvents({
37742         /**
37743          * @event tabchange
37744          * Fires when the active tab changes
37745          * @param {Roo.TabPanel} this
37746          * @param {Roo.TabPanelItem} activePanel The new active tab
37747          */
37748         "tabchange": true,
37749         /**
37750          * @event beforetabchange
37751          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37752          * @param {Roo.TabPanel} this
37753          * @param {Object} e Set cancel to true on this object to cancel the tab change
37754          * @param {Roo.TabPanelItem} tab The tab being changed to
37755          */
37756         "beforetabchange" : true
37757     });
37758
37759     Roo.EventManager.onWindowResize(this.onResize, this);
37760     this.cpad = this.el.getPadding("lr");
37761     this.hiddenCount = 0;
37762
37763
37764     // toolbar on the tabbar support...
37765     if (this.toolbar) {
37766         alert("no toolbar support yet");
37767         this.toolbar  = false;
37768         /*
37769         var tcfg = this.toolbar;
37770         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37771         this.toolbar = new Roo.Toolbar(tcfg);
37772         if (Roo.isSafari) {
37773             var tbl = tcfg.container.child('table', true);
37774             tbl.setAttribute('width', '100%');
37775         }
37776         */
37777         
37778     }
37779    
37780
37781
37782     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37783 };
37784
37785 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37786     /*
37787      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37788      */
37789     tabPosition : "top",
37790     /*
37791      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37792      */
37793     currentTabWidth : 0,
37794     /*
37795      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37796      */
37797     minTabWidth : 40,
37798     /*
37799      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37800      */
37801     maxTabWidth : 250,
37802     /*
37803      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37804      */
37805     preferredTabWidth : 175,
37806     /*
37807      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37808      */
37809     resizeTabs : false,
37810     /*
37811      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37812      */
37813     monitorResize : true,
37814     /*
37815      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37816      */
37817     toolbar : false,
37818
37819     /**
37820      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37821      * @param {String} id The id of the div to use <b>or create</b>
37822      * @param {String} text The text for the tab
37823      * @param {String} content (optional) Content to put in the TabPanelItem body
37824      * @param {Boolean} closable (optional) True to create a close icon on the tab
37825      * @return {Roo.TabPanelItem} The created TabPanelItem
37826      */
37827     addTab : function(id, text, content, closable, tpl)
37828     {
37829         var item = new Roo.bootstrap.panel.TabItem({
37830             panel: this,
37831             id : id,
37832             text : text,
37833             closable : closable,
37834             tpl : tpl
37835         });
37836         this.addTabItem(item);
37837         if(content){
37838             item.setContent(content);
37839         }
37840         return item;
37841     },
37842
37843     /**
37844      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37845      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37846      * @return {Roo.TabPanelItem}
37847      */
37848     getTab : function(id){
37849         return this.items[id];
37850     },
37851
37852     /**
37853      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37854      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37855      */
37856     hideTab : function(id){
37857         var t = this.items[id];
37858         if(!t.isHidden()){
37859            t.setHidden(true);
37860            this.hiddenCount++;
37861            this.autoSizeTabs();
37862         }
37863     },
37864
37865     /**
37866      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37867      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37868      */
37869     unhideTab : function(id){
37870         var t = this.items[id];
37871         if(t.isHidden()){
37872            t.setHidden(false);
37873            this.hiddenCount--;
37874            this.autoSizeTabs();
37875         }
37876     },
37877
37878     /**
37879      * Adds an existing {@link Roo.TabPanelItem}.
37880      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37881      */
37882     addTabItem : function(item){
37883         this.items[item.id] = item;
37884         this.items.push(item);
37885       //  if(this.resizeTabs){
37886     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37887   //         this.autoSizeTabs();
37888 //        }else{
37889 //            item.autoSize();
37890        // }
37891     },
37892
37893     /**
37894      * Removes a {@link Roo.TabPanelItem}.
37895      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37896      */
37897     removeTab : function(id){
37898         var items = this.items;
37899         var tab = items[id];
37900         if(!tab) { return; }
37901         var index = items.indexOf(tab);
37902         if(this.active == tab && items.length > 1){
37903             var newTab = this.getNextAvailable(index);
37904             if(newTab) {
37905                 newTab.activate();
37906             }
37907         }
37908         this.stripEl.dom.removeChild(tab.pnode.dom);
37909         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37910             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37911         }
37912         items.splice(index, 1);
37913         delete this.items[tab.id];
37914         tab.fireEvent("close", tab);
37915         tab.purgeListeners();
37916         this.autoSizeTabs();
37917     },
37918
37919     getNextAvailable : function(start){
37920         var items = this.items;
37921         var index = start;
37922         // look for a next tab that will slide over to
37923         // replace the one being removed
37924         while(index < items.length){
37925             var item = items[++index];
37926             if(item && !item.isHidden()){
37927                 return item;
37928             }
37929         }
37930         // if one isn't found select the previous tab (on the left)
37931         index = start;
37932         while(index >= 0){
37933             var item = items[--index];
37934             if(item && !item.isHidden()){
37935                 return item;
37936             }
37937         }
37938         return null;
37939     },
37940
37941     /**
37942      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37943      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37944      */
37945     disableTab : function(id){
37946         var tab = this.items[id];
37947         if(tab && this.active != tab){
37948             tab.disable();
37949         }
37950     },
37951
37952     /**
37953      * Enables a {@link Roo.TabPanelItem} that is disabled.
37954      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37955      */
37956     enableTab : function(id){
37957         var tab = this.items[id];
37958         tab.enable();
37959     },
37960
37961     /**
37962      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37963      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37964      * @return {Roo.TabPanelItem} The TabPanelItem.
37965      */
37966     activate : function(id){
37967         var tab = this.items[id];
37968         if(!tab){
37969             return null;
37970         }
37971         if(tab == this.active || tab.disabled){
37972             return tab;
37973         }
37974         var e = {};
37975         this.fireEvent("beforetabchange", this, e, tab);
37976         if(e.cancel !== true && !tab.disabled){
37977             if(this.active){
37978                 this.active.hide();
37979             }
37980             this.active = this.items[id];
37981             this.active.show();
37982             this.fireEvent("tabchange", this, this.active);
37983         }
37984         return tab;
37985     },
37986
37987     /**
37988      * Gets the active {@link Roo.TabPanelItem}.
37989      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37990      */
37991     getActiveTab : function(){
37992         return this.active;
37993     },
37994
37995     /**
37996      * Updates the tab body element to fit the height of the container element
37997      * for overflow scrolling
37998      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37999      */
38000     syncHeight : function(targetHeight){
38001         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38002         var bm = this.bodyEl.getMargins();
38003         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38004         this.bodyEl.setHeight(newHeight);
38005         return newHeight;
38006     },
38007
38008     onResize : function(){
38009         if(this.monitorResize){
38010             this.autoSizeTabs();
38011         }
38012     },
38013
38014     /**
38015      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38016      */
38017     beginUpdate : function(){
38018         this.updating = true;
38019     },
38020
38021     /**
38022      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38023      */
38024     endUpdate : function(){
38025         this.updating = false;
38026         this.autoSizeTabs();
38027     },
38028
38029     /**
38030      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38031      */
38032     autoSizeTabs : function(){
38033         var count = this.items.length;
38034         var vcount = count - this.hiddenCount;
38035         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38036             return;
38037         }
38038         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38039         var availWidth = Math.floor(w / vcount);
38040         var b = this.stripBody;
38041         if(b.getWidth() > w){
38042             var tabs = this.items;
38043             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38044             if(availWidth < this.minTabWidth){
38045                 /*if(!this.sleft){    // incomplete scrolling code
38046                     this.createScrollButtons();
38047                 }
38048                 this.showScroll();
38049                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38050             }
38051         }else{
38052             if(this.currentTabWidth < this.preferredTabWidth){
38053                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38054             }
38055         }
38056     },
38057
38058     /**
38059      * Returns the number of tabs in this TabPanel.
38060      * @return {Number}
38061      */
38062      getCount : function(){
38063          return this.items.length;
38064      },
38065
38066     /**
38067      * Resizes all the tabs to the passed width
38068      * @param {Number} The new width
38069      */
38070     setTabWidth : function(width){
38071         this.currentTabWidth = width;
38072         for(var i = 0, len = this.items.length; i < len; i++) {
38073                 if(!this.items[i].isHidden()) {
38074                 this.items[i].setWidth(width);
38075             }
38076         }
38077     },
38078
38079     /**
38080      * Destroys this TabPanel
38081      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38082      */
38083     destroy : function(removeEl){
38084         Roo.EventManager.removeResizeListener(this.onResize, this);
38085         for(var i = 0, len = this.items.length; i < len; i++){
38086             this.items[i].purgeListeners();
38087         }
38088         if(removeEl === true){
38089             this.el.update("");
38090             this.el.remove();
38091         }
38092     },
38093     
38094     createStrip : function(container)
38095     {
38096         var strip = document.createElement("nav");
38097         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38098         container.appendChild(strip);
38099         return strip;
38100     },
38101     
38102     createStripList : function(strip)
38103     {
38104         // div wrapper for retard IE
38105         // returns the "tr" element.
38106         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38107         //'<div class="x-tabs-strip-wrap">'+
38108           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38109           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38110         return strip.firstChild; //.firstChild.firstChild.firstChild;
38111     },
38112     createBody : function(container)
38113     {
38114         var body = document.createElement("div");
38115         Roo.id(body, "tab-body");
38116         //Roo.fly(body).addClass("x-tabs-body");
38117         Roo.fly(body).addClass("tab-content");
38118         container.appendChild(body);
38119         return body;
38120     },
38121     createItemBody :function(bodyEl, id){
38122         var body = Roo.getDom(id);
38123         if(!body){
38124             body = document.createElement("div");
38125             body.id = id;
38126         }
38127         //Roo.fly(body).addClass("x-tabs-item-body");
38128         Roo.fly(body).addClass("tab-pane");
38129          bodyEl.insertBefore(body, bodyEl.firstChild);
38130         return body;
38131     },
38132     /** @private */
38133     createStripElements :  function(stripEl, text, closable, tpl)
38134     {
38135         var td = document.createElement("li"); // was td..
38136         
38137         
38138         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38139         
38140         
38141         stripEl.appendChild(td);
38142         /*if(closable){
38143             td.className = "x-tabs-closable";
38144             if(!this.closeTpl){
38145                 this.closeTpl = new Roo.Template(
38146                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38147                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38148                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38149                 );
38150             }
38151             var el = this.closeTpl.overwrite(td, {"text": text});
38152             var close = el.getElementsByTagName("div")[0];
38153             var inner = el.getElementsByTagName("em")[0];
38154             return {"el": el, "close": close, "inner": inner};
38155         } else {
38156         */
38157         // not sure what this is..
38158 //            if(!this.tabTpl){
38159                 //this.tabTpl = new Roo.Template(
38160                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38161                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38162                 //);
38163 //                this.tabTpl = new Roo.Template(
38164 //                   '<a href="#">' +
38165 //                   '<span unselectable="on"' +
38166 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38167 //                            ' >{text}</span></a>'
38168 //                );
38169 //                
38170 //            }
38171
38172
38173             var template = tpl || this.tabTpl || false;
38174             
38175             if(!template){
38176                 
38177                 template = new Roo.Template(
38178                    '<a href="#">' +
38179                    '<span unselectable="on"' +
38180                             (this.disableTooltips ? '' : ' title="{text}"') +
38181                             ' >{text}</span></a>'
38182                 );
38183             }
38184             
38185             switch (typeof(template)) {
38186                 case 'object' :
38187                     break;
38188                 case 'string' :
38189                     template = new Roo.Template(template);
38190                     break;
38191                 default :
38192                     break;
38193             }
38194             
38195             var el = template.overwrite(td, {"text": text});
38196             
38197             var inner = el.getElementsByTagName("span")[0];
38198             
38199             return {"el": el, "inner": inner};
38200             
38201     }
38202         
38203     
38204 });
38205
38206 /**
38207  * @class Roo.TabPanelItem
38208  * @extends Roo.util.Observable
38209  * Represents an individual item (tab plus body) in a TabPanel.
38210  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38211  * @param {String} id The id of this TabPanelItem
38212  * @param {String} text The text for the tab of this TabPanelItem
38213  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38214  */
38215 Roo.bootstrap.panel.TabItem = function(config){
38216     /**
38217      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38218      * @type Roo.TabPanel
38219      */
38220     this.tabPanel = config.panel;
38221     /**
38222      * The id for this TabPanelItem
38223      * @type String
38224      */
38225     this.id = config.id;
38226     /** @private */
38227     this.disabled = false;
38228     /** @private */
38229     this.text = config.text;
38230     /** @private */
38231     this.loaded = false;
38232     this.closable = config.closable;
38233
38234     /**
38235      * The body element for this TabPanelItem.
38236      * @type Roo.Element
38237      */
38238     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38239     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38240     this.bodyEl.setStyle("display", "block");
38241     this.bodyEl.setStyle("zoom", "1");
38242     //this.hideAction();
38243
38244     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38245     /** @private */
38246     this.el = Roo.get(els.el);
38247     this.inner = Roo.get(els.inner, true);
38248     this.textEl = Roo.get(this.el.dom.firstChild, true);
38249     this.pnode = Roo.get(els.el.parentNode, true);
38250 //    this.el.on("mousedown", this.onTabMouseDown, this);
38251     this.el.on("click", this.onTabClick, this);
38252     /** @private */
38253     if(config.closable){
38254         var c = Roo.get(els.close, true);
38255         c.dom.title = this.closeText;
38256         c.addClassOnOver("close-over");
38257         c.on("click", this.closeClick, this);
38258      }
38259
38260     this.addEvents({
38261          /**
38262          * @event activate
38263          * Fires when this tab becomes the active tab.
38264          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38265          * @param {Roo.TabPanelItem} this
38266          */
38267         "activate": true,
38268         /**
38269          * @event beforeclose
38270          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38271          * @param {Roo.TabPanelItem} this
38272          * @param {Object} e Set cancel to true on this object to cancel the close.
38273          */
38274         "beforeclose": true,
38275         /**
38276          * @event close
38277          * Fires when this tab is closed.
38278          * @param {Roo.TabPanelItem} this
38279          */
38280          "close": true,
38281         /**
38282          * @event deactivate
38283          * Fires when this tab is no longer the active tab.
38284          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38285          * @param {Roo.TabPanelItem} this
38286          */
38287          "deactivate" : true
38288     });
38289     this.hidden = false;
38290
38291     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38292 };
38293
38294 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38295            {
38296     purgeListeners : function(){
38297        Roo.util.Observable.prototype.purgeListeners.call(this);
38298        this.el.removeAllListeners();
38299     },
38300     /**
38301      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38302      */
38303     show : function(){
38304         this.pnode.addClass("active");
38305         this.showAction();
38306         if(Roo.isOpera){
38307             this.tabPanel.stripWrap.repaint();
38308         }
38309         this.fireEvent("activate", this.tabPanel, this);
38310     },
38311
38312     /**
38313      * Returns true if this tab is the active tab.
38314      * @return {Boolean}
38315      */
38316     isActive : function(){
38317         return this.tabPanel.getActiveTab() == this;
38318     },
38319
38320     /**
38321      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38322      */
38323     hide : function(){
38324         this.pnode.removeClass("active");
38325         this.hideAction();
38326         this.fireEvent("deactivate", this.tabPanel, this);
38327     },
38328
38329     hideAction : function(){
38330         this.bodyEl.hide();
38331         this.bodyEl.setStyle("position", "absolute");
38332         this.bodyEl.setLeft("-20000px");
38333         this.bodyEl.setTop("-20000px");
38334     },
38335
38336     showAction : function(){
38337         this.bodyEl.setStyle("position", "relative");
38338         this.bodyEl.setTop("");
38339         this.bodyEl.setLeft("");
38340         this.bodyEl.show();
38341     },
38342
38343     /**
38344      * Set the tooltip for the tab.
38345      * @param {String} tooltip The tab's tooltip
38346      */
38347     setTooltip : function(text){
38348         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38349             this.textEl.dom.qtip = text;
38350             this.textEl.dom.removeAttribute('title');
38351         }else{
38352             this.textEl.dom.title = text;
38353         }
38354     },
38355
38356     onTabClick : function(e){
38357         e.preventDefault();
38358         this.tabPanel.activate(this.id);
38359     },
38360
38361     onTabMouseDown : function(e){
38362         e.preventDefault();
38363         this.tabPanel.activate(this.id);
38364     },
38365 /*
38366     getWidth : function(){
38367         return this.inner.getWidth();
38368     },
38369
38370     setWidth : function(width){
38371         var iwidth = width - this.pnode.getPadding("lr");
38372         this.inner.setWidth(iwidth);
38373         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38374         this.pnode.setWidth(width);
38375     },
38376 */
38377     /**
38378      * Show or hide the tab
38379      * @param {Boolean} hidden True to hide or false to show.
38380      */
38381     setHidden : function(hidden){
38382         this.hidden = hidden;
38383         this.pnode.setStyle("display", hidden ? "none" : "");
38384     },
38385
38386     /**
38387      * Returns true if this tab is "hidden"
38388      * @return {Boolean}
38389      */
38390     isHidden : function(){
38391         return this.hidden;
38392     },
38393
38394     /**
38395      * Returns the text for this tab
38396      * @return {String}
38397      */
38398     getText : function(){
38399         return this.text;
38400     },
38401     /*
38402     autoSize : function(){
38403         //this.el.beginMeasure();
38404         this.textEl.setWidth(1);
38405         /*
38406          *  #2804 [new] Tabs in Roojs
38407          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38408          */
38409         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38410         //this.el.endMeasure();
38411     //},
38412
38413     /**
38414      * Sets the text for the tab (Note: this also sets the tooltip text)
38415      * @param {String} text The tab's text and tooltip
38416      */
38417     setText : function(text){
38418         this.text = text;
38419         this.textEl.update(text);
38420         this.setTooltip(text);
38421         //if(!this.tabPanel.resizeTabs){
38422         //    this.autoSize();
38423         //}
38424     },
38425     /**
38426      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38427      */
38428     activate : function(){
38429         this.tabPanel.activate(this.id);
38430     },
38431
38432     /**
38433      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38434      */
38435     disable : function(){
38436         if(this.tabPanel.active != this){
38437             this.disabled = true;
38438             this.pnode.addClass("disabled");
38439         }
38440     },
38441
38442     /**
38443      * Enables this TabPanelItem if it was previously disabled.
38444      */
38445     enable : function(){
38446         this.disabled = false;
38447         this.pnode.removeClass("disabled");
38448     },
38449
38450     /**
38451      * Sets the content for this TabPanelItem.
38452      * @param {String} content The content
38453      * @param {Boolean} loadScripts true to look for and load scripts
38454      */
38455     setContent : function(content, loadScripts){
38456         this.bodyEl.update(content, loadScripts);
38457     },
38458
38459     /**
38460      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38461      * @return {Roo.UpdateManager} The UpdateManager
38462      */
38463     getUpdateManager : function(){
38464         return this.bodyEl.getUpdateManager();
38465     },
38466
38467     /**
38468      * Set a URL to be used to load the content for this TabPanelItem.
38469      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38470      * @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)
38471      * @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)
38472      * @return {Roo.UpdateManager} The UpdateManager
38473      */
38474     setUrl : function(url, params, loadOnce){
38475         if(this.refreshDelegate){
38476             this.un('activate', this.refreshDelegate);
38477         }
38478         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38479         this.on("activate", this.refreshDelegate);
38480         return this.bodyEl.getUpdateManager();
38481     },
38482
38483     /** @private */
38484     _handleRefresh : function(url, params, loadOnce){
38485         if(!loadOnce || !this.loaded){
38486             var updater = this.bodyEl.getUpdateManager();
38487             updater.update(url, params, this._setLoaded.createDelegate(this));
38488         }
38489     },
38490
38491     /**
38492      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38493      *   Will fail silently if the setUrl method has not been called.
38494      *   This does not activate the panel, just updates its content.
38495      */
38496     refresh : function(){
38497         if(this.refreshDelegate){
38498            this.loaded = false;
38499            this.refreshDelegate();
38500         }
38501     },
38502
38503     /** @private */
38504     _setLoaded : function(){
38505         this.loaded = true;
38506     },
38507
38508     /** @private */
38509     closeClick : function(e){
38510         var o = {};
38511         e.stopEvent();
38512         this.fireEvent("beforeclose", this, o);
38513         if(o.cancel !== true){
38514             this.tabPanel.removeTab(this.id);
38515         }
38516     },
38517     /**
38518      * The text displayed in the tooltip for the close icon.
38519      * @type String
38520      */
38521     closeText : "Close this tab"
38522 });
38523 /**
38524 *    This script refer to:
38525 *    Title: International Telephone Input
38526 *    Author: Jack O'Connor
38527 *    Code version:  v12.1.12
38528 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38529 **/
38530
38531 Roo.bootstrap.PhoneInputData = function() {
38532     var d = [
38533       [
38534         "Afghanistan (‫افغانستان‬‎)",
38535         "af",
38536         "93"
38537       ],
38538       [
38539         "Albania (Shqipëri)",
38540         "al",
38541         "355"
38542       ],
38543       [
38544         "Algeria (‫الجزائر‬‎)",
38545         "dz",
38546         "213"
38547       ],
38548       [
38549         "American Samoa",
38550         "as",
38551         "1684"
38552       ],
38553       [
38554         "Andorra",
38555         "ad",
38556         "376"
38557       ],
38558       [
38559         "Angola",
38560         "ao",
38561         "244"
38562       ],
38563       [
38564         "Anguilla",
38565         "ai",
38566         "1264"
38567       ],
38568       [
38569         "Antigua and Barbuda",
38570         "ag",
38571         "1268"
38572       ],
38573       [
38574         "Argentina",
38575         "ar",
38576         "54"
38577       ],
38578       [
38579         "Armenia (Հայաստան)",
38580         "am",
38581         "374"
38582       ],
38583       [
38584         "Aruba",
38585         "aw",
38586         "297"
38587       ],
38588       [
38589         "Australia",
38590         "au",
38591         "61",
38592         0
38593       ],
38594       [
38595         "Austria (Österreich)",
38596         "at",
38597         "43"
38598       ],
38599       [
38600         "Azerbaijan (Azərbaycan)",
38601         "az",
38602         "994"
38603       ],
38604       [
38605         "Bahamas",
38606         "bs",
38607         "1242"
38608       ],
38609       [
38610         "Bahrain (‫البحرين‬‎)",
38611         "bh",
38612         "973"
38613       ],
38614       [
38615         "Bangladesh (বাংলাদেশ)",
38616         "bd",
38617         "880"
38618       ],
38619       [
38620         "Barbados",
38621         "bb",
38622         "1246"
38623       ],
38624       [
38625         "Belarus (Беларусь)",
38626         "by",
38627         "375"
38628       ],
38629       [
38630         "Belgium (België)",
38631         "be",
38632         "32"
38633       ],
38634       [
38635         "Belize",
38636         "bz",
38637         "501"
38638       ],
38639       [
38640         "Benin (Bénin)",
38641         "bj",
38642         "229"
38643       ],
38644       [
38645         "Bermuda",
38646         "bm",
38647         "1441"
38648       ],
38649       [
38650         "Bhutan (འབྲུག)",
38651         "bt",
38652         "975"
38653       ],
38654       [
38655         "Bolivia",
38656         "bo",
38657         "591"
38658       ],
38659       [
38660         "Bosnia and Herzegovina (Босна и Херцеговина)",
38661         "ba",
38662         "387"
38663       ],
38664       [
38665         "Botswana",
38666         "bw",
38667         "267"
38668       ],
38669       [
38670         "Brazil (Brasil)",
38671         "br",
38672         "55"
38673       ],
38674       [
38675         "British Indian Ocean Territory",
38676         "io",
38677         "246"
38678       ],
38679       [
38680         "British Virgin Islands",
38681         "vg",
38682         "1284"
38683       ],
38684       [
38685         "Brunei",
38686         "bn",
38687         "673"
38688       ],
38689       [
38690         "Bulgaria (България)",
38691         "bg",
38692         "359"
38693       ],
38694       [
38695         "Burkina Faso",
38696         "bf",
38697         "226"
38698       ],
38699       [
38700         "Burundi (Uburundi)",
38701         "bi",
38702         "257"
38703       ],
38704       [
38705         "Cambodia (កម្ពុជា)",
38706         "kh",
38707         "855"
38708       ],
38709       [
38710         "Cameroon (Cameroun)",
38711         "cm",
38712         "237"
38713       ],
38714       [
38715         "Canada",
38716         "ca",
38717         "1",
38718         1,
38719         ["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"]
38720       ],
38721       [
38722         "Cape Verde (Kabu Verdi)",
38723         "cv",
38724         "238"
38725       ],
38726       [
38727         "Caribbean Netherlands",
38728         "bq",
38729         "599",
38730         1
38731       ],
38732       [
38733         "Cayman Islands",
38734         "ky",
38735         "1345"
38736       ],
38737       [
38738         "Central African Republic (République centrafricaine)",
38739         "cf",
38740         "236"
38741       ],
38742       [
38743         "Chad (Tchad)",
38744         "td",
38745         "235"
38746       ],
38747       [
38748         "Chile",
38749         "cl",
38750         "56"
38751       ],
38752       [
38753         "China (中国)",
38754         "cn",
38755         "86"
38756       ],
38757       [
38758         "Christmas Island",
38759         "cx",
38760         "61",
38761         2
38762       ],
38763       [
38764         "Cocos (Keeling) Islands",
38765         "cc",
38766         "61",
38767         1
38768       ],
38769       [
38770         "Colombia",
38771         "co",
38772         "57"
38773       ],
38774       [
38775         "Comoros (‫جزر القمر‬‎)",
38776         "km",
38777         "269"
38778       ],
38779       [
38780         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38781         "cd",
38782         "243"
38783       ],
38784       [
38785         "Congo (Republic) (Congo-Brazzaville)",
38786         "cg",
38787         "242"
38788       ],
38789       [
38790         "Cook Islands",
38791         "ck",
38792         "682"
38793       ],
38794       [
38795         "Costa Rica",
38796         "cr",
38797         "506"
38798       ],
38799       [
38800         "Côte d’Ivoire",
38801         "ci",
38802         "225"
38803       ],
38804       [
38805         "Croatia (Hrvatska)",
38806         "hr",
38807         "385"
38808       ],
38809       [
38810         "Cuba",
38811         "cu",
38812         "53"
38813       ],
38814       [
38815         "Curaçao",
38816         "cw",
38817         "599",
38818         0
38819       ],
38820       [
38821         "Cyprus (Κύπρος)",
38822         "cy",
38823         "357"
38824       ],
38825       [
38826         "Czech Republic (Česká republika)",
38827         "cz",
38828         "420"
38829       ],
38830       [
38831         "Denmark (Danmark)",
38832         "dk",
38833         "45"
38834       ],
38835       [
38836         "Djibouti",
38837         "dj",
38838         "253"
38839       ],
38840       [
38841         "Dominica",
38842         "dm",
38843         "1767"
38844       ],
38845       [
38846         "Dominican Republic (República Dominicana)",
38847         "do",
38848         "1",
38849         2,
38850         ["809", "829", "849"]
38851       ],
38852       [
38853         "Ecuador",
38854         "ec",
38855         "593"
38856       ],
38857       [
38858         "Egypt (‫مصر‬‎)",
38859         "eg",
38860         "20"
38861       ],
38862       [
38863         "El Salvador",
38864         "sv",
38865         "503"
38866       ],
38867       [
38868         "Equatorial Guinea (Guinea Ecuatorial)",
38869         "gq",
38870         "240"
38871       ],
38872       [
38873         "Eritrea",
38874         "er",
38875         "291"
38876       ],
38877       [
38878         "Estonia (Eesti)",
38879         "ee",
38880         "372"
38881       ],
38882       [
38883         "Ethiopia",
38884         "et",
38885         "251"
38886       ],
38887       [
38888         "Falkland Islands (Islas Malvinas)",
38889         "fk",
38890         "500"
38891       ],
38892       [
38893         "Faroe Islands (Føroyar)",
38894         "fo",
38895         "298"
38896       ],
38897       [
38898         "Fiji",
38899         "fj",
38900         "679"
38901       ],
38902       [
38903         "Finland (Suomi)",
38904         "fi",
38905         "358",
38906         0
38907       ],
38908       [
38909         "France",
38910         "fr",
38911         "33"
38912       ],
38913       [
38914         "French Guiana (Guyane française)",
38915         "gf",
38916         "594"
38917       ],
38918       [
38919         "French Polynesia (Polynésie française)",
38920         "pf",
38921         "689"
38922       ],
38923       [
38924         "Gabon",
38925         "ga",
38926         "241"
38927       ],
38928       [
38929         "Gambia",
38930         "gm",
38931         "220"
38932       ],
38933       [
38934         "Georgia (საქართველო)",
38935         "ge",
38936         "995"
38937       ],
38938       [
38939         "Germany (Deutschland)",
38940         "de",
38941         "49"
38942       ],
38943       [
38944         "Ghana (Gaana)",
38945         "gh",
38946         "233"
38947       ],
38948       [
38949         "Gibraltar",
38950         "gi",
38951         "350"
38952       ],
38953       [
38954         "Greece (Ελλάδα)",
38955         "gr",
38956         "30"
38957       ],
38958       [
38959         "Greenland (Kalaallit Nunaat)",
38960         "gl",
38961         "299"
38962       ],
38963       [
38964         "Grenada",
38965         "gd",
38966         "1473"
38967       ],
38968       [
38969         "Guadeloupe",
38970         "gp",
38971         "590",
38972         0
38973       ],
38974       [
38975         "Guam",
38976         "gu",
38977         "1671"
38978       ],
38979       [
38980         "Guatemala",
38981         "gt",
38982         "502"
38983       ],
38984       [
38985         "Guernsey",
38986         "gg",
38987         "44",
38988         1
38989       ],
38990       [
38991         "Guinea (Guinée)",
38992         "gn",
38993         "224"
38994       ],
38995       [
38996         "Guinea-Bissau (Guiné Bissau)",
38997         "gw",
38998         "245"
38999       ],
39000       [
39001         "Guyana",
39002         "gy",
39003         "592"
39004       ],
39005       [
39006         "Haiti",
39007         "ht",
39008         "509"
39009       ],
39010       [
39011         "Honduras",
39012         "hn",
39013         "504"
39014       ],
39015       [
39016         "Hong Kong (香港)",
39017         "hk",
39018         "852"
39019       ],
39020       [
39021         "Hungary (Magyarország)",
39022         "hu",
39023         "36"
39024       ],
39025       [
39026         "Iceland (Ísland)",
39027         "is",
39028         "354"
39029       ],
39030       [
39031         "India (भारत)",
39032         "in",
39033         "91"
39034       ],
39035       [
39036         "Indonesia",
39037         "id",
39038         "62"
39039       ],
39040       [
39041         "Iran (‫ایران‬‎)",
39042         "ir",
39043         "98"
39044       ],
39045       [
39046         "Iraq (‫العراق‬‎)",
39047         "iq",
39048         "964"
39049       ],
39050       [
39051         "Ireland",
39052         "ie",
39053         "353"
39054       ],
39055       [
39056         "Isle of Man",
39057         "im",
39058         "44",
39059         2
39060       ],
39061       [
39062         "Israel (‫ישראל‬‎)",
39063         "il",
39064         "972"
39065       ],
39066       [
39067         "Italy (Italia)",
39068         "it",
39069         "39",
39070         0
39071       ],
39072       [
39073         "Jamaica",
39074         "jm",
39075         "1876"
39076       ],
39077       [
39078         "Japan (日本)",
39079         "jp",
39080         "81"
39081       ],
39082       [
39083         "Jersey",
39084         "je",
39085         "44",
39086         3
39087       ],
39088       [
39089         "Jordan (‫الأردن‬‎)",
39090         "jo",
39091         "962"
39092       ],
39093       [
39094         "Kazakhstan (Казахстан)",
39095         "kz",
39096         "7",
39097         1
39098       ],
39099       [
39100         "Kenya",
39101         "ke",
39102         "254"
39103       ],
39104       [
39105         "Kiribati",
39106         "ki",
39107         "686"
39108       ],
39109       [
39110         "Kosovo",
39111         "xk",
39112         "383"
39113       ],
39114       [
39115         "Kuwait (‫الكويت‬‎)",
39116         "kw",
39117         "965"
39118       ],
39119       [
39120         "Kyrgyzstan (Кыргызстан)",
39121         "kg",
39122         "996"
39123       ],
39124       [
39125         "Laos (ລາວ)",
39126         "la",
39127         "856"
39128       ],
39129       [
39130         "Latvia (Latvija)",
39131         "lv",
39132         "371"
39133       ],
39134       [
39135         "Lebanon (‫لبنان‬‎)",
39136         "lb",
39137         "961"
39138       ],
39139       [
39140         "Lesotho",
39141         "ls",
39142         "266"
39143       ],
39144       [
39145         "Liberia",
39146         "lr",
39147         "231"
39148       ],
39149       [
39150         "Libya (‫ليبيا‬‎)",
39151         "ly",
39152         "218"
39153       ],
39154       [
39155         "Liechtenstein",
39156         "li",
39157         "423"
39158       ],
39159       [
39160         "Lithuania (Lietuva)",
39161         "lt",
39162         "370"
39163       ],
39164       [
39165         "Luxembourg",
39166         "lu",
39167         "352"
39168       ],
39169       [
39170         "Macau (澳門)",
39171         "mo",
39172         "853"
39173       ],
39174       [
39175         "Macedonia (FYROM) (Македонија)",
39176         "mk",
39177         "389"
39178       ],
39179       [
39180         "Madagascar (Madagasikara)",
39181         "mg",
39182         "261"
39183       ],
39184       [
39185         "Malawi",
39186         "mw",
39187         "265"
39188       ],
39189       [
39190         "Malaysia",
39191         "my",
39192         "60"
39193       ],
39194       [
39195         "Maldives",
39196         "mv",
39197         "960"
39198       ],
39199       [
39200         "Mali",
39201         "ml",
39202         "223"
39203       ],
39204       [
39205         "Malta",
39206         "mt",
39207         "356"
39208       ],
39209       [
39210         "Marshall Islands",
39211         "mh",
39212         "692"
39213       ],
39214       [
39215         "Martinique",
39216         "mq",
39217         "596"
39218       ],
39219       [
39220         "Mauritania (‫موريتانيا‬‎)",
39221         "mr",
39222         "222"
39223       ],
39224       [
39225         "Mauritius (Moris)",
39226         "mu",
39227         "230"
39228       ],
39229       [
39230         "Mayotte",
39231         "yt",
39232         "262",
39233         1
39234       ],
39235       [
39236         "Mexico (México)",
39237         "mx",
39238         "52"
39239       ],
39240       [
39241         "Micronesia",
39242         "fm",
39243         "691"
39244       ],
39245       [
39246         "Moldova (Republica Moldova)",
39247         "md",
39248         "373"
39249       ],
39250       [
39251         "Monaco",
39252         "mc",
39253         "377"
39254       ],
39255       [
39256         "Mongolia (Монгол)",
39257         "mn",
39258         "976"
39259       ],
39260       [
39261         "Montenegro (Crna Gora)",
39262         "me",
39263         "382"
39264       ],
39265       [
39266         "Montserrat",
39267         "ms",
39268         "1664"
39269       ],
39270       [
39271         "Morocco (‫المغرب‬‎)",
39272         "ma",
39273         "212",
39274         0
39275       ],
39276       [
39277         "Mozambique (Moçambique)",
39278         "mz",
39279         "258"
39280       ],
39281       [
39282         "Myanmar (Burma) (မြန်မာ)",
39283         "mm",
39284         "95"
39285       ],
39286       [
39287         "Namibia (Namibië)",
39288         "na",
39289         "264"
39290       ],
39291       [
39292         "Nauru",
39293         "nr",
39294         "674"
39295       ],
39296       [
39297         "Nepal (नेपाल)",
39298         "np",
39299         "977"
39300       ],
39301       [
39302         "Netherlands (Nederland)",
39303         "nl",
39304         "31"
39305       ],
39306       [
39307         "New Caledonia (Nouvelle-Calédonie)",
39308         "nc",
39309         "687"
39310       ],
39311       [
39312         "New Zealand",
39313         "nz",
39314         "64"
39315       ],
39316       [
39317         "Nicaragua",
39318         "ni",
39319         "505"
39320       ],
39321       [
39322         "Niger (Nijar)",
39323         "ne",
39324         "227"
39325       ],
39326       [
39327         "Nigeria",
39328         "ng",
39329         "234"
39330       ],
39331       [
39332         "Niue",
39333         "nu",
39334         "683"
39335       ],
39336       [
39337         "Norfolk Island",
39338         "nf",
39339         "672"
39340       ],
39341       [
39342         "North Korea (조선 민주주의 인민 공화국)",
39343         "kp",
39344         "850"
39345       ],
39346       [
39347         "Northern Mariana Islands",
39348         "mp",
39349         "1670"
39350       ],
39351       [
39352         "Norway (Norge)",
39353         "no",
39354         "47",
39355         0
39356       ],
39357       [
39358         "Oman (‫عُمان‬‎)",
39359         "om",
39360         "968"
39361       ],
39362       [
39363         "Pakistan (‫پاکستان‬‎)",
39364         "pk",
39365         "92"
39366       ],
39367       [
39368         "Palau",
39369         "pw",
39370         "680"
39371       ],
39372       [
39373         "Palestine (‫فلسطين‬‎)",
39374         "ps",
39375         "970"
39376       ],
39377       [
39378         "Panama (Panamá)",
39379         "pa",
39380         "507"
39381       ],
39382       [
39383         "Papua New Guinea",
39384         "pg",
39385         "675"
39386       ],
39387       [
39388         "Paraguay",
39389         "py",
39390         "595"
39391       ],
39392       [
39393         "Peru (Perú)",
39394         "pe",
39395         "51"
39396       ],
39397       [
39398         "Philippines",
39399         "ph",
39400         "63"
39401       ],
39402       [
39403         "Poland (Polska)",
39404         "pl",
39405         "48"
39406       ],
39407       [
39408         "Portugal",
39409         "pt",
39410         "351"
39411       ],
39412       [
39413         "Puerto Rico",
39414         "pr",
39415         "1",
39416         3,
39417         ["787", "939"]
39418       ],
39419       [
39420         "Qatar (‫قطر‬‎)",
39421         "qa",
39422         "974"
39423       ],
39424       [
39425         "Réunion (La Réunion)",
39426         "re",
39427         "262",
39428         0
39429       ],
39430       [
39431         "Romania (România)",
39432         "ro",
39433         "40"
39434       ],
39435       [
39436         "Russia (Россия)",
39437         "ru",
39438         "7",
39439         0
39440       ],
39441       [
39442         "Rwanda",
39443         "rw",
39444         "250"
39445       ],
39446       [
39447         "Saint Barthélemy",
39448         "bl",
39449         "590",
39450         1
39451       ],
39452       [
39453         "Saint Helena",
39454         "sh",
39455         "290"
39456       ],
39457       [
39458         "Saint Kitts and Nevis",
39459         "kn",
39460         "1869"
39461       ],
39462       [
39463         "Saint Lucia",
39464         "lc",
39465         "1758"
39466       ],
39467       [
39468         "Saint Martin (Saint-Martin (partie française))",
39469         "mf",
39470         "590",
39471         2
39472       ],
39473       [
39474         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39475         "pm",
39476         "508"
39477       ],
39478       [
39479         "Saint Vincent and the Grenadines",
39480         "vc",
39481         "1784"
39482       ],
39483       [
39484         "Samoa",
39485         "ws",
39486         "685"
39487       ],
39488       [
39489         "San Marino",
39490         "sm",
39491         "378"
39492       ],
39493       [
39494         "São Tomé and Príncipe (São Tomé e Príncipe)",
39495         "st",
39496         "239"
39497       ],
39498       [
39499         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39500         "sa",
39501         "966"
39502       ],
39503       [
39504         "Senegal (Sénégal)",
39505         "sn",
39506         "221"
39507       ],
39508       [
39509         "Serbia (Србија)",
39510         "rs",
39511         "381"
39512       ],
39513       [
39514         "Seychelles",
39515         "sc",
39516         "248"
39517       ],
39518       [
39519         "Sierra Leone",
39520         "sl",
39521         "232"
39522       ],
39523       [
39524         "Singapore",
39525         "sg",
39526         "65"
39527       ],
39528       [
39529         "Sint Maarten",
39530         "sx",
39531         "1721"
39532       ],
39533       [
39534         "Slovakia (Slovensko)",
39535         "sk",
39536         "421"
39537       ],
39538       [
39539         "Slovenia (Slovenija)",
39540         "si",
39541         "386"
39542       ],
39543       [
39544         "Solomon Islands",
39545         "sb",
39546         "677"
39547       ],
39548       [
39549         "Somalia (Soomaaliya)",
39550         "so",
39551         "252"
39552       ],
39553       [
39554         "South Africa",
39555         "za",
39556         "27"
39557       ],
39558       [
39559         "South Korea (대한민국)",
39560         "kr",
39561         "82"
39562       ],
39563       [
39564         "South Sudan (‫جنوب السودان‬‎)",
39565         "ss",
39566         "211"
39567       ],
39568       [
39569         "Spain (España)",
39570         "es",
39571         "34"
39572       ],
39573       [
39574         "Sri Lanka (ශ්‍රී ලංකාව)",
39575         "lk",
39576         "94"
39577       ],
39578       [
39579         "Sudan (‫السودان‬‎)",
39580         "sd",
39581         "249"
39582       ],
39583       [
39584         "Suriname",
39585         "sr",
39586         "597"
39587       ],
39588       [
39589         "Svalbard and Jan Mayen",
39590         "sj",
39591         "47",
39592         1
39593       ],
39594       [
39595         "Swaziland",
39596         "sz",
39597         "268"
39598       ],
39599       [
39600         "Sweden (Sverige)",
39601         "se",
39602         "46"
39603       ],
39604       [
39605         "Switzerland (Schweiz)",
39606         "ch",
39607         "41"
39608       ],
39609       [
39610         "Syria (‫سوريا‬‎)",
39611         "sy",
39612         "963"
39613       ],
39614       [
39615         "Taiwan (台灣)",
39616         "tw",
39617         "886"
39618       ],
39619       [
39620         "Tajikistan",
39621         "tj",
39622         "992"
39623       ],
39624       [
39625         "Tanzania",
39626         "tz",
39627         "255"
39628       ],
39629       [
39630         "Thailand (ไทย)",
39631         "th",
39632         "66"
39633       ],
39634       [
39635         "Timor-Leste",
39636         "tl",
39637         "670"
39638       ],
39639       [
39640         "Togo",
39641         "tg",
39642         "228"
39643       ],
39644       [
39645         "Tokelau",
39646         "tk",
39647         "690"
39648       ],
39649       [
39650         "Tonga",
39651         "to",
39652         "676"
39653       ],
39654       [
39655         "Trinidad and Tobago",
39656         "tt",
39657         "1868"
39658       ],
39659       [
39660         "Tunisia (‫تونس‬‎)",
39661         "tn",
39662         "216"
39663       ],
39664       [
39665         "Turkey (Türkiye)",
39666         "tr",
39667         "90"
39668       ],
39669       [
39670         "Turkmenistan",
39671         "tm",
39672         "993"
39673       ],
39674       [
39675         "Turks and Caicos Islands",
39676         "tc",
39677         "1649"
39678       ],
39679       [
39680         "Tuvalu",
39681         "tv",
39682         "688"
39683       ],
39684       [
39685         "U.S. Virgin Islands",
39686         "vi",
39687         "1340"
39688       ],
39689       [
39690         "Uganda",
39691         "ug",
39692         "256"
39693       ],
39694       [
39695         "Ukraine (Україна)",
39696         "ua",
39697         "380"
39698       ],
39699       [
39700         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39701         "ae",
39702         "971"
39703       ],
39704       [
39705         "United Kingdom",
39706         "gb",
39707         "44",
39708         0
39709       ],
39710       [
39711         "United States",
39712         "us",
39713         "1",
39714         0
39715       ],
39716       [
39717         "Uruguay",
39718         "uy",
39719         "598"
39720       ],
39721       [
39722         "Uzbekistan (Oʻzbekiston)",
39723         "uz",
39724         "998"
39725       ],
39726       [
39727         "Vanuatu",
39728         "vu",
39729         "678"
39730       ],
39731       [
39732         "Vatican City (Città del Vaticano)",
39733         "va",
39734         "39",
39735         1
39736       ],
39737       [
39738         "Venezuela",
39739         "ve",
39740         "58"
39741       ],
39742       [
39743         "Vietnam (Việt Nam)",
39744         "vn",
39745         "84"
39746       ],
39747       [
39748         "Wallis and Futuna (Wallis-et-Futuna)",
39749         "wf",
39750         "681"
39751       ],
39752       [
39753         "Western Sahara (‫الصحراء الغربية‬‎)",
39754         "eh",
39755         "212",
39756         1
39757       ],
39758       [
39759         "Yemen (‫اليمن‬‎)",
39760         "ye",
39761         "967"
39762       ],
39763       [
39764         "Zambia",
39765         "zm",
39766         "260"
39767       ],
39768       [
39769         "Zimbabwe",
39770         "zw",
39771         "263"
39772       ],
39773       [
39774         "Åland Islands",
39775         "ax",
39776         "358",
39777         1
39778       ]
39779   ];
39780   
39781   return d;
39782 }/**
39783 *    This script refer to:
39784 *    Title: International Telephone Input
39785 *    Author: Jack O'Connor
39786 *    Code version:  v12.1.12
39787 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39788 **/
39789
39790 /**
39791  * @class Roo.bootstrap.PhoneInput
39792  * @extends Roo.bootstrap.TriggerField
39793  * An input with International dial-code selection
39794  
39795  * @cfg {String} defaultDialCode default '+852'
39796  * @cfg {Array} preferedCountries default []
39797   
39798  * @constructor
39799  * Create a new PhoneInput.
39800  * @param {Object} config Configuration options
39801  */
39802
39803 Roo.bootstrap.PhoneInput = function(config) {
39804     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39805 };
39806
39807 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39808         
39809         listWidth: undefined,
39810         
39811         selectedClass: 'active',
39812         
39813         invalidClass : "has-warning",
39814         
39815         validClass: 'has-success',
39816         
39817         allowed: '0123456789',
39818         
39819         /**
39820          * @cfg {String} defaultDialCode The default dial code when initializing the input
39821          */
39822         defaultDialCode: '+852',
39823         
39824         /**
39825          * @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
39826          */
39827         preferedCountries: false,
39828         
39829         getAutoCreate : function()
39830         {
39831             var data = Roo.bootstrap.PhoneInputData();
39832             var align = this.labelAlign || this.parentLabelAlign();
39833             var id = Roo.id();
39834             
39835             this.allCountries = [];
39836             this.dialCodeMapping = [];
39837             
39838             for (var i = 0; i < data.length; i++) {
39839               var c = data[i];
39840               this.allCountries[i] = {
39841                 name: c[0],
39842                 iso2: c[1],
39843                 dialCode: c[2],
39844                 priority: c[3] || 0,
39845                 areaCodes: c[4] || null
39846               };
39847               this.dialCodeMapping[c[2]] = {
39848                   name: c[0],
39849                   iso2: c[1],
39850                   priority: c[3] || 0,
39851                   areaCodes: c[4] || null
39852               };
39853             }
39854             
39855             var cfg = {
39856                 cls: 'form-group',
39857                 cn: []
39858             };
39859             
39860             var input =  {
39861                 tag: 'input',
39862                 id : id,
39863                 cls : 'form-control tel-input',
39864                 autocomplete: 'new-password'
39865             };
39866             
39867             var hiddenInput = {
39868                 tag: 'input',
39869                 type: 'hidden',
39870                 cls: 'hidden-tel-input'
39871             };
39872             
39873             if (this.name) {
39874                 hiddenInput.name = this.name;
39875             }
39876             
39877             if (this.disabled) {
39878                 input.disabled = true;
39879             }
39880             
39881             var flag_container = {
39882                 tag: 'div',
39883                 cls: 'flag-box',
39884                 cn: [
39885                     {
39886                         tag: 'div',
39887                         cls: 'flag'
39888                     },
39889                     {
39890                         tag: 'div',
39891                         cls: 'caret'
39892                     }
39893                 ]
39894             };
39895             
39896             var box = {
39897                 tag: 'div',
39898                 cls: this.hasFeedback ? 'has-feedback' : '',
39899                 cn: [
39900                     hiddenInput,
39901                     input,
39902                     {
39903                         tag: 'input',
39904                         cls: 'dial-code-holder',
39905                         disabled: true
39906                     }
39907                 ]
39908             };
39909             
39910             var container = {
39911                 cls: 'roo-select2-container input-group',
39912                 cn: [
39913                     flag_container,
39914                     box
39915                 ]
39916             };
39917             
39918             if (this.fieldLabel.length) {
39919                 var indicator = {
39920                     tag: 'i',
39921                     tooltip: 'This field is required'
39922                 };
39923                 
39924                 var label = {
39925                     tag: 'label',
39926                     'for':  id,
39927                     cls: 'control-label',
39928                     cn: []
39929                 };
39930                 
39931                 var label_text = {
39932                     tag: 'span',
39933                     html: this.fieldLabel
39934                 };
39935                 
39936                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39937                 label.cn = [
39938                     indicator,
39939                     label_text
39940                 ];
39941                 
39942                 if(this.indicatorpos == 'right') {
39943                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39944                     label.cn = [
39945                         label_text,
39946                         indicator
39947                     ];
39948                 }
39949                 
39950                 if(align == 'left') {
39951                     container = {
39952                         tag: 'div',
39953                         cn: [
39954                             container
39955                         ]
39956                     };
39957                     
39958                     if(this.labelWidth > 12){
39959                         label.style = "width: " + this.labelWidth + 'px';
39960                     }
39961                     if(this.labelWidth < 13 && this.labelmd == 0){
39962                         this.labelmd = this.labelWidth;
39963                     }
39964                     if(this.labellg > 0){
39965                         label.cls += ' col-lg-' + this.labellg;
39966                         input.cls += ' col-lg-' + (12 - this.labellg);
39967                     }
39968                     if(this.labelmd > 0){
39969                         label.cls += ' col-md-' + this.labelmd;
39970                         container.cls += ' col-md-' + (12 - this.labelmd);
39971                     }
39972                     if(this.labelsm > 0){
39973                         label.cls += ' col-sm-' + this.labelsm;
39974                         container.cls += ' col-sm-' + (12 - this.labelsm);
39975                     }
39976                     if(this.labelxs > 0){
39977                         label.cls += ' col-xs-' + this.labelxs;
39978                         container.cls += ' col-xs-' + (12 - this.labelxs);
39979                     }
39980                 }
39981             }
39982             
39983             cfg.cn = [
39984                 label,
39985                 container
39986             ];
39987             
39988             var settings = this;
39989             
39990             ['xs','sm','md','lg'].map(function(size){
39991                 if (settings[size]) {
39992                     cfg.cls += ' col-' + size + '-' + settings[size];
39993                 }
39994             });
39995             
39996             this.store = new Roo.data.Store({
39997                 proxy : new Roo.data.MemoryProxy({}),
39998                 reader : new Roo.data.JsonReader({
39999                     fields : [
40000                         {
40001                             'name' : 'name',
40002                             'type' : 'string'
40003                         },
40004                         {
40005                             'name' : 'iso2',
40006                             'type' : 'string'
40007                         },
40008                         {
40009                             'name' : 'dialCode',
40010                             'type' : 'string'
40011                         },
40012                         {
40013                             'name' : 'priority',
40014                             'type' : 'string'
40015                         },
40016                         {
40017                             'name' : 'areaCodes',
40018                             'type' : 'string'
40019                         }
40020                     ]
40021                 })
40022             });
40023             
40024             if(!this.preferedCountries) {
40025                 this.preferedCountries = [
40026                     'hk',
40027                     'gb',
40028                     'us'
40029                 ];
40030             }
40031             
40032             var p = this.preferedCountries.reverse();
40033             
40034             if(p) {
40035                 for (var i = 0; i < p.length; i++) {
40036                     for (var j = 0; j < this.allCountries.length; j++) {
40037                         if(this.allCountries[j].iso2 == p[i]) {
40038                             var t = this.allCountries[j];
40039                             this.allCountries.splice(j,1);
40040                             this.allCountries.unshift(t);
40041                         }
40042                     } 
40043                 }
40044             }
40045             
40046             this.store.proxy.data = {
40047                 success: true,
40048                 data: this.allCountries
40049             };
40050             
40051             return cfg;
40052         },
40053         
40054         initEvents : function()
40055         {
40056             this.createList();
40057             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40058             
40059             this.indicator = this.indicatorEl();
40060             this.flag = this.flagEl();
40061             this.dialCodeHolder = this.dialCodeHolderEl();
40062             
40063             this.trigger = this.el.select('div.flag-box',true).first();
40064             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40065             
40066             var _this = this;
40067             
40068             (function(){
40069                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40070                 _this.list.setWidth(lw);
40071             }).defer(100);
40072             
40073             this.list.on('mouseover', this.onViewOver, this);
40074             this.list.on('mousemove', this.onViewMove, this);
40075             this.inputEl().on("keyup", this.onKeyUp, this);
40076             
40077             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40078
40079             this.view = new Roo.View(this.list, this.tpl, {
40080                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40081             });
40082             
40083             this.view.on('click', this.onViewClick, this);
40084             this.setValue(this.defaultDialCode);
40085         },
40086         
40087         onTriggerClick : function(e)
40088         {
40089             Roo.log('trigger click');
40090             if(this.disabled){
40091                 return;
40092             }
40093             
40094             if(this.isExpanded()){
40095                 this.collapse();
40096                 this.hasFocus = false;
40097             }else {
40098                 this.store.load({});
40099                 this.hasFocus = true;
40100                 this.expand();
40101             }
40102         },
40103         
40104         isExpanded : function()
40105         {
40106             return this.list.isVisible();
40107         },
40108         
40109         collapse : function()
40110         {
40111             if(!this.isExpanded()){
40112                 return;
40113             }
40114             this.list.hide();
40115             Roo.get(document).un('mousedown', this.collapseIf, this);
40116             Roo.get(document).un('mousewheel', this.collapseIf, this);
40117             this.fireEvent('collapse', this);
40118             this.validate();
40119         },
40120         
40121         expand : function()
40122         {
40123             Roo.log('expand');
40124
40125             if(this.isExpanded() || !this.hasFocus){
40126                 return;
40127             }
40128             
40129             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40130             this.list.setWidth(lw);
40131             
40132             this.list.show();
40133             this.restrictHeight();
40134             
40135             Roo.get(document).on('mousedown', this.collapseIf, this);
40136             Roo.get(document).on('mousewheel', this.collapseIf, this);
40137             
40138             this.fireEvent('expand', this);
40139         },
40140         
40141         restrictHeight : function()
40142         {
40143             this.list.alignTo(this.inputEl(), this.listAlign);
40144             this.list.alignTo(this.inputEl(), this.listAlign);
40145         },
40146         
40147         onViewOver : function(e, t)
40148         {
40149             if(this.inKeyMode){
40150                 return;
40151             }
40152             var item = this.view.findItemFromChild(t);
40153             
40154             if(item){
40155                 var index = this.view.indexOf(item);
40156                 this.select(index, false);
40157             }
40158         },
40159
40160         // private
40161         onViewClick : function(view, doFocus, el, e)
40162         {
40163             var index = this.view.getSelectedIndexes()[0];
40164             
40165             var r = this.store.getAt(index);
40166             
40167             if(r){
40168                 this.onSelect(r, index);
40169             }
40170             if(doFocus !== false && !this.blockFocus){
40171                 this.inputEl().focus();
40172             }
40173         },
40174         
40175         onViewMove : function(e, t)
40176         {
40177             this.inKeyMode = false;
40178         },
40179         
40180         select : function(index, scrollIntoView)
40181         {
40182             this.selectedIndex = index;
40183             this.view.select(index);
40184             if(scrollIntoView !== false){
40185                 var el = this.view.getNode(index);
40186                 if(el){
40187                     this.list.scrollChildIntoView(el, false);
40188                 }
40189             }
40190         },
40191         
40192         createList : function()
40193         {
40194             this.list = Roo.get(document.body).createChild({
40195                 tag: 'ul',
40196                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40197                 style: 'display:none'
40198             });
40199             
40200             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40201         },
40202         
40203         collapseIf : function(e)
40204         {
40205             var in_combo  = e.within(this.el);
40206             var in_list =  e.within(this.list);
40207             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40208             
40209             if (in_combo || in_list || is_list) {
40210                 return;
40211             }
40212             this.collapse();
40213         },
40214         
40215         onSelect : function(record, index)
40216         {
40217             if(this.fireEvent('beforeselect', this, record, index) !== false){
40218                 
40219                 this.setFlagClass(record.data.iso2);
40220                 this.setDialCode(record.data.dialCode);
40221                 this.hasFocus = false;
40222                 this.collapse();
40223                 this.fireEvent('select', this, record, index);
40224             }
40225         },
40226         
40227         flagEl : function()
40228         {
40229             var flag = this.el.select('div.flag',true).first();
40230             if(!flag){
40231                 return false;
40232             }
40233             return flag;
40234         },
40235         
40236         dialCodeHolderEl : function()
40237         {
40238             var d = this.el.select('input.dial-code-holder',true).first();
40239             if(!d){
40240                 return false;
40241             }
40242             return d;
40243         },
40244         
40245         setDialCode : function(v)
40246         {
40247             this.dialCodeHolder.dom.value = '+'+v;
40248         },
40249         
40250         setFlagClass : function(n)
40251         {
40252             this.flag.dom.className = 'flag '+n;
40253         },
40254         
40255         getValue : function()
40256         {
40257             var v = this.inputEl().getValue();
40258             if(this.dialCodeHolder) {
40259                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40260             }
40261             return v;
40262         },
40263         
40264         setValue : function(v)
40265         {
40266             var d = this.getDialCode(v);
40267             
40268             //invalid dial code
40269             if(v.length == 0 || !d || d.length == 0) {
40270                 if(this.rendered){
40271                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40272                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40273                 }
40274                 return;
40275             }
40276             
40277             //valid dial code
40278             this.setFlagClass(this.dialCodeMapping[d].iso2);
40279             this.setDialCode(d);
40280             this.inputEl().dom.value = v.replace('+'+d,'');
40281             this.hiddenEl().dom.value = this.getValue();
40282             
40283             this.validate();
40284         },
40285         
40286         getDialCode : function(v)
40287         {
40288             v = v ||  '';
40289             
40290             if (v.length == 0) {
40291                 return this.dialCodeHolder.dom.value;
40292             }
40293             
40294             var dialCode = "";
40295             if (v.charAt(0) != "+") {
40296                 return false;
40297             }
40298             var numericChars = "";
40299             for (var i = 1; i < v.length; i++) {
40300               var c = v.charAt(i);
40301               if (!isNaN(c)) {
40302                 numericChars += c;
40303                 if (this.dialCodeMapping[numericChars]) {
40304                   dialCode = v.substr(1, i);
40305                 }
40306                 if (numericChars.length == 4) {
40307                   break;
40308                 }
40309               }
40310             }
40311             return dialCode;
40312         },
40313         
40314         reset : function()
40315         {
40316             this.setValue(this.defaultDialCode);
40317             this.validate();
40318         },
40319         
40320         hiddenEl : function()
40321         {
40322             return this.el.select('input.hidden-tel-input',true).first();
40323         },
40324         
40325         onKeyUp : function(e){
40326             
40327             var k = e.getKey();
40328             var c = e.getCharCode();
40329             
40330             if(
40331                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40332                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40333             ){
40334                 e.stopEvent();
40335             }
40336             
40337             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40338             //     return;
40339             // }
40340             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40341                 e.stopEvent();
40342             }
40343             
40344             this.setValue(this.getValue());
40345         }
40346         
40347 });
40348 /**
40349  * @class Roo.bootstrap.MoneyField
40350  * @extends Roo.bootstrap.ComboBox
40351  * Bootstrap MoneyField class
40352  * 
40353  * @constructor
40354  * Create a new MoneyField.
40355  * @param {Object} config Configuration options
40356  */
40357
40358 Roo.bootstrap.MoneyField = function(config) {
40359     
40360     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40361     
40362 };
40363
40364 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40365     
40366     /**
40367      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40368      */
40369     allowDecimals : true,
40370     /**
40371      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40372      */
40373     decimalSeparator : ".",
40374     /**
40375      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40376      */
40377     decimalPrecision : 0,
40378     /**
40379      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40380      */
40381     allowNegative : true,
40382     /**
40383      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40384      */
40385     allowZero: true,
40386     /**
40387      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40388      */
40389     minValue : Number.NEGATIVE_INFINITY,
40390     /**
40391      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40392      */
40393     maxValue : Number.MAX_VALUE,
40394     /**
40395      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40396      */
40397     minText : "The minimum value for this field is {0}",
40398     /**
40399      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40400      */
40401     maxText : "The maximum value for this field is {0}",
40402     /**
40403      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40404      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40405      */
40406     nanText : "{0} is not a valid number",
40407     /**
40408      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40409      */
40410     castInt : true,
40411     /**
40412      * @cfg {String} defaults currency of the MoneyField
40413      * value should be in lkey
40414      */
40415     defaultCurrency : false,
40416     /**
40417      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40418      */
40419     thousandsDelimiter : false,
40420     
40421     
40422     inputlg : 9,
40423     inputmd : 9,
40424     inputsm : 9,
40425     inputxs : 6,
40426     
40427     store : false,
40428     
40429     getAutoCreate : function()
40430     {
40431         var align = this.labelAlign || this.parentLabelAlign();
40432         
40433         var id = Roo.id();
40434
40435         var cfg = {
40436             cls: 'form-group',
40437             cn: []
40438         };
40439
40440         var input =  {
40441             tag: 'input',
40442             id : id,
40443             cls : 'form-control roo-money-amount-input',
40444             autocomplete: 'new-password'
40445         };
40446         
40447         var hiddenInput = {
40448             tag: 'input',
40449             type: 'hidden',
40450             id: Roo.id(),
40451             cls: 'hidden-number-input'
40452         };
40453         
40454         if (this.name) {
40455             hiddenInput.name = this.name;
40456         }
40457
40458         if (this.disabled) {
40459             input.disabled = true;
40460         }
40461
40462         var clg = 12 - this.inputlg;
40463         var cmd = 12 - this.inputmd;
40464         var csm = 12 - this.inputsm;
40465         var cxs = 12 - this.inputxs;
40466         
40467         var container = {
40468             tag : 'div',
40469             cls : 'row roo-money-field',
40470             cn : [
40471                 {
40472                     tag : 'div',
40473                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40474                     cn : [
40475                         {
40476                             tag : 'div',
40477                             cls: 'roo-select2-container input-group',
40478                             cn: [
40479                                 {
40480                                     tag : 'input',
40481                                     cls : 'form-control roo-money-currency-input',
40482                                     autocomplete: 'new-password',
40483                                     readOnly : 1,
40484                                     name : this.currencyName
40485                                 },
40486                                 {
40487                                     tag :'span',
40488                                     cls : 'input-group-addon',
40489                                     cn : [
40490                                         {
40491                                             tag: 'span',
40492                                             cls: 'caret'
40493                                         }
40494                                     ]
40495                                 }
40496                             ]
40497                         }
40498                     ]
40499                 },
40500                 {
40501                     tag : 'div',
40502                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40503                     cn : [
40504                         {
40505                             tag: 'div',
40506                             cls: this.hasFeedback ? 'has-feedback' : '',
40507                             cn: [
40508                                 input
40509                             ]
40510                         }
40511                     ]
40512                 }
40513             ]
40514             
40515         };
40516         
40517         if (this.fieldLabel.length) {
40518             var indicator = {
40519                 tag: 'i',
40520                 tooltip: 'This field is required'
40521             };
40522
40523             var label = {
40524                 tag: 'label',
40525                 'for':  id,
40526                 cls: 'control-label',
40527                 cn: []
40528             };
40529
40530             var label_text = {
40531                 tag: 'span',
40532                 html: this.fieldLabel
40533             };
40534
40535             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40536             label.cn = [
40537                 indicator,
40538                 label_text
40539             ];
40540
40541             if(this.indicatorpos == 'right') {
40542                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40543                 label.cn = [
40544                     label_text,
40545                     indicator
40546                 ];
40547             }
40548
40549             if(align == 'left') {
40550                 container = {
40551                     tag: 'div',
40552                     cn: [
40553                         container
40554                     ]
40555                 };
40556
40557                 if(this.labelWidth > 12){
40558                     label.style = "width: " + this.labelWidth + 'px';
40559                 }
40560                 if(this.labelWidth < 13 && this.labelmd == 0){
40561                     this.labelmd = this.labelWidth;
40562                 }
40563                 if(this.labellg > 0){
40564                     label.cls += ' col-lg-' + this.labellg;
40565                     input.cls += ' col-lg-' + (12 - this.labellg);
40566                 }
40567                 if(this.labelmd > 0){
40568                     label.cls += ' col-md-' + this.labelmd;
40569                     container.cls += ' col-md-' + (12 - this.labelmd);
40570                 }
40571                 if(this.labelsm > 0){
40572                     label.cls += ' col-sm-' + this.labelsm;
40573                     container.cls += ' col-sm-' + (12 - this.labelsm);
40574                 }
40575                 if(this.labelxs > 0){
40576                     label.cls += ' col-xs-' + this.labelxs;
40577                     container.cls += ' col-xs-' + (12 - this.labelxs);
40578                 }
40579             }
40580         }
40581
40582         cfg.cn = [
40583             label,
40584             container,
40585             hiddenInput
40586         ];
40587         
40588         var settings = this;
40589
40590         ['xs','sm','md','lg'].map(function(size){
40591             if (settings[size]) {
40592                 cfg.cls += ' col-' + size + '-' + settings[size];
40593             }
40594         });
40595         
40596         return cfg;
40597     },
40598     
40599     initEvents : function()
40600     {
40601         this.indicator = this.indicatorEl();
40602         
40603         this.initCurrencyEvent();
40604         
40605         this.initNumberEvent();
40606     },
40607     
40608     initCurrencyEvent : function()
40609     {
40610         if (!this.store) {
40611             throw "can not find store for combo";
40612         }
40613         
40614         this.store = Roo.factory(this.store, Roo.data);
40615         this.store.parent = this;
40616         
40617         this.createList();
40618         
40619         this.triggerEl = this.el.select('.input-group-addon', true).first();
40620         
40621         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40622         
40623         var _this = this;
40624         
40625         (function(){
40626             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40627             _this.list.setWidth(lw);
40628         }).defer(100);
40629         
40630         this.list.on('mouseover', this.onViewOver, this);
40631         this.list.on('mousemove', this.onViewMove, this);
40632         this.list.on('scroll', this.onViewScroll, this);
40633         
40634         if(!this.tpl){
40635             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40636         }
40637         
40638         this.view = new Roo.View(this.list, this.tpl, {
40639             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40640         });
40641         
40642         this.view.on('click', this.onViewClick, this);
40643         
40644         this.store.on('beforeload', this.onBeforeLoad, this);
40645         this.store.on('load', this.onLoad, this);
40646         this.store.on('loadexception', this.onLoadException, this);
40647         
40648         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40649             "up" : function(e){
40650                 this.inKeyMode = true;
40651                 this.selectPrev();
40652             },
40653
40654             "down" : function(e){
40655                 if(!this.isExpanded()){
40656                     this.onTriggerClick();
40657                 }else{
40658                     this.inKeyMode = true;
40659                     this.selectNext();
40660                 }
40661             },
40662
40663             "enter" : function(e){
40664                 this.collapse();
40665                 
40666                 if(this.fireEvent("specialkey", this, e)){
40667                     this.onViewClick(false);
40668                 }
40669                 
40670                 return true;
40671             },
40672
40673             "esc" : function(e){
40674                 this.collapse();
40675             },
40676
40677             "tab" : function(e){
40678                 this.collapse();
40679                 
40680                 if(this.fireEvent("specialkey", this, e)){
40681                     this.onViewClick(false);
40682                 }
40683                 
40684                 return true;
40685             },
40686
40687             scope : this,
40688
40689             doRelay : function(foo, bar, hname){
40690                 if(hname == 'down' || this.scope.isExpanded()){
40691                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40692                 }
40693                 return true;
40694             },
40695
40696             forceKeyDown: true
40697         });
40698         
40699         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40700         
40701     },
40702     
40703     initNumberEvent : function(e)
40704     {
40705         this.inputEl().on("keydown" , this.fireKey,  this);
40706         this.inputEl().on("focus", this.onFocus,  this);
40707         this.inputEl().on("blur", this.onBlur,  this);
40708         
40709         this.inputEl().relayEvent('keyup', this);
40710         
40711         if(this.indicator){
40712             this.indicator.addClass('invisible');
40713         }
40714  
40715         this.originalValue = this.getValue();
40716         
40717         if(this.validationEvent == 'keyup'){
40718             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40719             this.inputEl().on('keyup', this.filterValidation, this);
40720         }
40721         else if(this.validationEvent !== false){
40722             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40723         }
40724         
40725         if(this.selectOnFocus){
40726             this.on("focus", this.preFocus, this);
40727             
40728         }
40729         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40730             this.inputEl().on("keypress", this.filterKeys, this);
40731         } else {
40732             this.inputEl().relayEvent('keypress', this);
40733         }
40734         
40735         var allowed = "0123456789";
40736         
40737         if(this.allowDecimals){
40738             allowed += this.decimalSeparator;
40739         }
40740         
40741         if(this.allowNegative){
40742             allowed += "-";
40743         }
40744         
40745         if(this.thousandsDelimiter) {
40746             allowed += ",";
40747         }
40748         
40749         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40750         
40751         var keyPress = function(e){
40752             
40753             var k = e.getKey();
40754             
40755             var c = e.getCharCode();
40756             
40757             if(
40758                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40759                     allowed.indexOf(String.fromCharCode(c)) === -1
40760             ){
40761                 e.stopEvent();
40762                 return;
40763             }
40764             
40765             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40766                 return;
40767             }
40768             
40769             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40770                 e.stopEvent();
40771             }
40772         };
40773         
40774         this.inputEl().on("keypress", keyPress, this);
40775         
40776     },
40777     
40778     onTriggerClick : function(e)
40779     {   
40780         if(this.disabled){
40781             return;
40782         }
40783         
40784         this.page = 0;
40785         this.loadNext = false;
40786         
40787         if(this.isExpanded()){
40788             this.collapse();
40789             return;
40790         }
40791         
40792         this.hasFocus = true;
40793         
40794         if(this.triggerAction == 'all') {
40795             this.doQuery(this.allQuery, true);
40796             return;
40797         }
40798         
40799         this.doQuery(this.getRawValue());
40800     },
40801     
40802     getCurrency : function()
40803     {   
40804         var v = this.currencyEl().getValue();
40805         
40806         return v;
40807     },
40808     
40809     restrictHeight : function()
40810     {
40811         this.list.alignTo(this.currencyEl(), this.listAlign);
40812         this.list.alignTo(this.currencyEl(), this.listAlign);
40813     },
40814     
40815     onViewClick : function(view, doFocus, el, e)
40816     {
40817         var index = this.view.getSelectedIndexes()[0];
40818         
40819         var r = this.store.getAt(index);
40820         
40821         if(r){
40822             this.onSelect(r, index);
40823         }
40824     },
40825     
40826     onSelect : function(record, index){
40827         
40828         if(this.fireEvent('beforeselect', this, record, index) !== false){
40829         
40830             this.setFromCurrencyData(index > -1 ? record.data : false);
40831             
40832             this.collapse();
40833             
40834             this.fireEvent('select', this, record, index);
40835         }
40836     },
40837     
40838     setFromCurrencyData : function(o)
40839     {
40840         var currency = '';
40841         
40842         this.lastCurrency = o;
40843         
40844         if (this.currencyField) {
40845             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40846         } else {
40847             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40848         }
40849         
40850         this.lastSelectionText = currency;
40851         
40852         //setting default currency
40853         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40854             this.setCurrency(this.defaultCurrency);
40855             return;
40856         }
40857         
40858         this.setCurrency(currency);
40859     },
40860     
40861     setFromData : function(o)
40862     {
40863         var c = {};
40864         
40865         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40866         
40867         this.setFromCurrencyData(c);
40868         
40869         var value = '';
40870         
40871         if (this.name) {
40872             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40873         } else {
40874             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40875         }
40876         
40877         this.setValue(value);
40878         
40879     },
40880     
40881     setCurrency : function(v)
40882     {   
40883         this.currencyValue = v;
40884         
40885         if(this.rendered){
40886             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40887             this.validate();
40888         }
40889     },
40890     
40891     setValue : function(v)
40892     {
40893         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40894         
40895         this.value = v;
40896         
40897         if(this.rendered){
40898             
40899             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40900             
40901             this.inputEl().dom.value = (v == '') ? '' :
40902                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40903             
40904             if(!this.allowZero && v === '0') {
40905                 this.hiddenEl().dom.value = '';
40906                 this.inputEl().dom.value = '';
40907             }
40908             
40909             this.validate();
40910         }
40911     },
40912     
40913     getRawValue : function()
40914     {
40915         var v = this.inputEl().getValue();
40916         
40917         return v;
40918     },
40919     
40920     getValue : function()
40921     {
40922         return this.fixPrecision(this.parseValue(this.getRawValue()));
40923     },
40924     
40925     parseValue : function(value)
40926     {
40927         if(this.thousandsDelimiter) {
40928             value += "";
40929             r = new RegExp(",", "g");
40930             value = value.replace(r, "");
40931         }
40932         
40933         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40934         return isNaN(value) ? '' : value;
40935         
40936     },
40937     
40938     fixPrecision : function(value)
40939     {
40940         if(this.thousandsDelimiter) {
40941             value += "";
40942             r = new RegExp(",", "g");
40943             value = value.replace(r, "");
40944         }
40945         
40946         var nan = isNaN(value);
40947         
40948         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40949             return nan ? '' : value;
40950         }
40951         return parseFloat(value).toFixed(this.decimalPrecision);
40952     },
40953     
40954     decimalPrecisionFcn : function(v)
40955     {
40956         return Math.floor(v);
40957     },
40958     
40959     validateValue : function(value)
40960     {
40961         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40962             return false;
40963         }
40964         
40965         var num = this.parseValue(value);
40966         
40967         if(isNaN(num)){
40968             this.markInvalid(String.format(this.nanText, value));
40969             return false;
40970         }
40971         
40972         if(num < this.minValue){
40973             this.markInvalid(String.format(this.minText, this.minValue));
40974             return false;
40975         }
40976         
40977         if(num > this.maxValue){
40978             this.markInvalid(String.format(this.maxText, this.maxValue));
40979             return false;
40980         }
40981         
40982         return true;
40983     },
40984     
40985     validate : function()
40986     {
40987         if(this.disabled || this.allowBlank){
40988             this.markValid();
40989             return true;
40990         }
40991         
40992         var currency = this.getCurrency();
40993         
40994         if(this.validateValue(this.getRawValue()) && currency.length){
40995             this.markValid();
40996             return true;
40997         }
40998         
40999         this.markInvalid();
41000         return false;
41001     },
41002     
41003     getName: function()
41004     {
41005         return this.name;
41006     },
41007     
41008     beforeBlur : function()
41009     {
41010         if(!this.castInt){
41011             return;
41012         }
41013         
41014         var v = this.parseValue(this.getRawValue());
41015         
41016         if(v || v == 0){
41017             this.setValue(v);
41018         }
41019     },
41020     
41021     onBlur : function()
41022     {
41023         this.beforeBlur();
41024         
41025         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41026             //this.el.removeClass(this.focusClass);
41027         }
41028         
41029         this.hasFocus = false;
41030         
41031         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41032             this.validate();
41033         }
41034         
41035         var v = this.getValue();
41036         
41037         if(String(v) !== String(this.startValue)){
41038             this.fireEvent('change', this, v, this.startValue);
41039         }
41040         
41041         this.fireEvent("blur", this);
41042     },
41043     
41044     inputEl : function()
41045     {
41046         return this.el.select('.roo-money-amount-input', true).first();
41047     },
41048     
41049     currencyEl : function()
41050     {
41051         return this.el.select('.roo-money-currency-input', true).first();
41052     },
41053     
41054     hiddenEl : function()
41055     {
41056         return this.el.select('input.hidden-number-input',true).first();
41057     }
41058     
41059 });